Невозможно загрузить приспособление для сквозной модели django

Что я пытаюсь сделать?

Я создал приспособление для сквозной модели и теперь хочу загрузить его в свою базу данных.

В чем проблема?

При загрузке приспособления с помощью команды Django loaddata для сквозной модели я получаю эту ошибку:

django.core.serializers.base.DeserializationError: Problem installing fixture 
'<fixture-path>/fixtures/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

Я создаю приспособление следующим образом:

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

что создает следующий json:

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

Я также добавил get_by_natural_key метод в менеджере для сквозной модели следующим образом:

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)

И загрузка приспособления

python manage.py loaddata m.json

Другие модели работают нормально. Я могу загрузить их без каких-либо проблем, только сквозная модель не работает.

Вы создаете модель, а не менеджер. Вы также должны убедиться, что комбинация полей уникальна:

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

Для моделей Person и Group вам также понадобятся менеджеры:

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

После более детального изучения исходного кода django о команде loaddata я обнаружил, что она использует внутреннюю десериализацию django. Поэтому я решил почитать о сериализации и десериализации, где есть это раздел в документации, который говорит о зависимостях во время сериализации.

Исходя из этого, следующие изменения устранили проблему:

Решение:

Обновите сквозной метод модели natural_key и добавьте natural_key зависимости:

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

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

Я также обновил natural_key обеих моделей Person и Group, чтобы возвращать кортеж, а не один элемент, т.е. return self.name, . Кроме того, я добавил менеджеры для обеих моделей.

Вернуться на верх