2

So I have a Film model that holds a list of Actors model in a many to many field:

class Person(models.Model):
    full = models.TextField()
    short = models.TextField()
    num = models.CharField(max_length=5)

class Film(models.Model):
    name = models.TextField()
    year = models.SmallIntegerField(blank=True)
    actors = models.ManyToManyField('Person')

I'm trying to load some initial data from json fixtures, however the problem I have is loading the many to many actors field. For example I get the error:

DeserializationError: [u"'Anna-Varney' value must be an integer."]

with these fixtures:

  {
    "pk": 1,
    "model": "data.Film",
    "fields": {
      "actors": [
        "Anna-Varney"
      ],
      "name": "Like a Corpse Standing in Desperation (2005) (V)",
      "year": "2005"
    }

while my actors fixture looks like this:

 {
    "pk": 1,
    "model": "data.Person",
    "fields": {
      "full": "Anna-Varney",
      "num": "I",
      "short": "Anna-Varney"
    }
  }

So the many to many fields must use the pk integer, but the problem is that the data isn't sorted and for a long list of actors I don't think its practical to manually look up the pk of each one. I've been looking for solutions and it seems I have to use natural keys, but I'm not exactly sure how to apply those for my models.

EDIT: I've changed my models to be:

class PersonManager(models.Manager):
    def get_by_natural_key(self, full):
        return self.get(full=full)

class Person(models.Model):
    objects = PersonManager()
    full = models.TextField()
    short = models.TextField()
    num = models.CharField(max_length=5)
    def natural_key(self):
        return self.full

But I'm still getting the same error

dl8
  • 1,270
  • 1
  • 14
  • 34

1 Answers1

3

There's a problem with both the input and the natural_key method.

Documentation: Serializing Django objects - natural keys states:

A natural key is a tuple of values that can be used to uniquely identify an object instance without using the primary key value.

The Person natural_key method should return a tuple

def natural_key(self):
    return (self.full,)

The serialised input should also contain tuples/lists for the natural keys.

{
    "pk": 1, 
    "model": "data.film", 
    "fields": {
        "actors": [
            [
                "Matt Damon"
            ], 
            [
                "Jodie Foster"
            ]
        ], 
        "name": "Elysium", 
        "year": 2013
    }
}
AndrewS
  • 1,666
  • 11
  • 15
  • Thank you, I think that solved it, but now I receive an error stating: DeserializationError: get() returned more than one Person -- it returned 2! Lookup parameters were {'full': 'FitzGerald, James (I)'}. I believe that's due to perhaps duplicate entries in my actor fixture that got loaded in, is there a way to ignore duplicate data in fixtures? I thought I already did it programatically with sets when creating the fixtures but I guess not. – dl8 Aug 11 '13 at 11:26
  • Actually looking through my person fixture, I don't see a duplicate of it so I have no clue. – dl8 Aug 11 '13 at 11:33
  • Whilst you don't need to enforce uniqueness of natural keys at the database level it's recommended. I would add a `unique=True` to the `full` field to prevent duplicates in future. Clear/drop the tables and try reloading the fixtures again. A previous partial load may have created the duplicates. – AndrewS Aug 11 '13 at 12:50
  • I tried adding the unique=True to my full field but I would get errors, I changed it to a CharField and that made it work. However, after resetting the app and reloading my actor fixture, I get IntegrityError: Could not load data.Person(pk=89388): (1062, "Duplicate entry 'de Santiago, Carlos' for key 'full'") even though I looked through the fixture and there are clearly no duplicates. Is there some command I can use to make it ignore duplicates? – dl8 Aug 11 '13 at 21:48
  • Actually, I located the duplicate...there's a person with the exact same name except with a capitalized D in "De Santiago" and I guess django treats those as case insensititve...hopefully fixing that will finally fix everything. – dl8 Aug 11 '13 at 22:47