Home > Software engineering >  Unable to load fixture for through model django
Unable to load fixture for through model django

Time:02-05

What I am trying to do?

I have created a fixture for a through model and now I want to load it in my database.

What is the problem?

While loading the fixture using Django loaddata command for through model I get this error:

django.core.serializers.base.DeserializationError: Problem installing fixture 
'm.json': ['“Dave Johnson” value must be an integer.']:(room.membership:pk=None) 
field_value was 'Dave Johnson'

models.py

class Person(models.Model):
    name = models.CharField(max_length=100, unique=True)

    def natural_key(self):
       return self.name

class Group(models.Model):
    name = models.CharField(max_length=100, unique=True)
    members = models.ManyToManyField(Person, through='Membership')

    def natural_key(self):
       return self.name

class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    joined_on = models.DateTimeField(auto_now_add=True)

    objects = MembershipManager()

    def natural_key(self):
       return self.person.name, self.group.name

I am creating fixture like this:

python manage.py dumpdata room.Membership 
--natural-foreign --natural-primary > m.json

which creates the following json:

[
{
    "model": "room.membership",
    "fields": {
        "person": "Dave Johnson",
        "group": "Django Learning",
        "joined_on": "2020-12-03T13:14:28.572Z"
    }
}
]

I have also added get_by_natural_key method in the manager for through model like this:

class MembershipManager(models.Manager):
   def get_by_natural_key(self, person_name, group_name):
      return self.get(person__name=person_name, group__name=group_name)

And loading the fixture

python manage.py loaddata m.json

Other models are working fine. I can load them without any issue it is only the through model which is not working.

CodePudding user response:

You are creating a model, not a manager. You should also ensure that the combination of fields is unique:

class MembershipManager(models.Manager):
    def get_by_natural_key(self, person_name, group_name):
        return self.get(person__name=person_name, group__name=group_name)


class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    joined_on = models.DateTimeField(auto_now_add=True)
    objects = MembershipManager()

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['person', 'contact'], name='unique_person_group'
            )
        ]

    def natural_key(self):
        return self.person.name, self.group.name

For the Person and Group models, you will need managers as well:

class NameManager(models.Manager):
    def get_by_natural_key(self, name):
        return self.get(name=name)


class Person(models.Model):
    name = models.CharField(max_length=100, unique=True)
    objects = NameManager()

    def natural_key(self):
        return self.name


class Group(models.Model):
    name = models.CharField(max_length=100, unique=True)
    members = models.ManyToManyField(Person, through='Membership')
    objects = NameManager()

    def natural_key(self):
        return self.name

CodePudding user response:

After drilling down more on django source code about loaddata command I found out that it uses django deserialization internally. So I thought to read about serialization and deserialization which has this section in the docs that talks about dependencies during serialization.

So based on that following changes fixed the issue:

Solution:

Update the through model natural_key method and add natural_key dependencies:

def natural_key(self):
    return self.person.natural_key(), self.group.natural_key()

natural_key.dependencies = ['room.Person', 'room.Group']

I also updated the natural_key of both Person and Group models to return tuple rather than single element i.e return self.name, . Moreover added managers for both the models.

  • Related