Django loaddata with Natural Keys not querying correct Foreign Key
Asked Answered
P

2

6

Here is my problem. I am trying to load some data that has a natural key relationship to another model.

I modified the parent model to generate natural keys.

I exported the the data from the old database using the command:

manage.py dumpdata resources.image -n --indent 4 > images.json

I then tried to import into the new database using the command:

manage.py loaddata images.json

At this point I get the error:

IntegrityError: Problem installing fixtures: The row in table 'resources_image' 
with primary key '340' has an invalid foreign key: resources_image.voyage_id contains 
a value '41890' that does not have a corresponding value in voyage_voyage.id.

It is trying to query on voyage_voyage.id instead of voyage_voyage.voyage_id as specified in the get_by_natural_key function. I double checked and the key in the fixture is in the voyage_id field. Below is my code and sample fixture.

Parent Model:

# for parsing natural key
class VoyageManager(models.Manager):
    def get_by_natural_key(self, voyage_id):
        return self.get(voyage_id=voyage_id)


class Voyage(models.Model):

    # for parsing natural key
    objects = VoyageManager()


    voyage_id = models.IntegerField("Voyage ID (can be empty)", null=True, blank=True)

    # A WHOLE BUNCH OF FIELDS

    # generate natural key
    def natural_key(self):
        return (self.voyage_id)


    class Meta:
        ordering = ['voyage_id',]
        verbose_name = 'Voyage'
        verbose_name_plural = "Voyages"

    def __unicode__(self):
        return "Voyage #%s" % str(self.voyage_id)

Child Model:

class Image(models.Model):
    voyage = models.ForeignKey(Voyage, null=True, blank=True)

    # MANY OTHER FIELDS

    class Meta:
        verbose_name = "Image"
        verbose_name_plural = "Images"

        ordering = ["date"]

The Fixture:

{
    "pk": 340, 
    "model": "resources.image", 
    "fields": {
        "category": 56, 
        "voyage": 41890, 
        "date": 1873, 
        "description": "blah blah blah", 
        "language": "  ", 
        "creator": null, 
        "title": "Catherine Zimmermann-Mulgrave, \nc.1873", 
        "source": "blah blah blah", 
        "ready_to_go": true, 
        "file": "images/5AF81DA065049ACE0EC8E236C445F5BC.JPG", 
        "order_num": 0
    }
}
Prosperus answered 16/1, 2014 at 1:4 Comment(0)
P
15

So here is what I finally figured out

This function expects the input to come from a tuple:

def get_by_natural_key(self, voyage_id):
        return self.get(voyage_id=voyage_id)

This function produces the tuple when --natural flag is specified

def natural_key(self):
        return (self.voyage_id)

The trick here is that when you have a tuple with only one element it reverts back to its original type (str, int etc.) to force a single element tuple you need a trailing "," so the fix is :

def natural_key(self):
        return (self.voyage_id,)
Prosperus answered 29/1, 2014 at 18:11 Comment(1)
I don't belive in god... but damn, god bless you @Alex!Maddalena
T
0

the question is wether

{
    "pk": 41890, 
    "model": "voyage.voyage", 
    "fields": {
        ...
    }
}

is in images.json file, if not I guess you'll have to include it in the dump somehow

Tedesco answered 16/1, 2014 at 1:36 Comment(1)
The voyage.voyage model is already loaded. For this particular object 41890 is in the voyage_id field not the voyag.id field. I thought the whole idea of natural keys was that, with existing data, the pk can be (and usually is not a static value) thus another field can be used to identify the record to associate the child record with the parent.Prosperus

© 2022 - 2024 — McMap. All rights reserved.