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.