Django Изменение метода сохранения модели «многие ко многим» не работает - PullRequest
0 голосов
/ 22 января 2020

Концепция

Прежде всего, у меня есть довольно сложная модель ... Идея состоит в создании звездолета в качестве модели. Таким образом, каждый Корабль имеет тип (ship_type), основанный на Плане этого Типа Корабля. Поэтому, когда вы создаете корабль, вы должны решить, какую ship_type модель следует использовать (внешний ключ).

Потому что я хочу сменить корабль (пример: купить другое программное обеспечение), но не сам план. каждый корабль имеет те же поля в базе данных, что и корабль чертежей (оба наследуются от ShipField). Когда кто-то устанавливает ship_type или меняет его, я хочу, чтобы с Django до go план получил всю информацию и переписал информацию о корабле. Поэтому я попытался выполнить sh такое поведение в методе сохранения.

Поиск функций и ошибок

Функция, которую я написал, всегда запускается при изменении self.ship_type, пока все хорошо. И все «нормальные» поля тоже изменены, только поля многие ко многим не работают.

Я нырнул в это и запутался. Предположим, что в нашем корабле нет записей в self.software, а в новом ship_type - 3. Если я распечатал self.software до сохранения (1.), я получил, как и ожидалось, пустой набор запросов. Когда я делаю это после super.save (2.), я получаю набор запросов из трех элементов. Так что вроде бы все работает. Но если я взгляну на корабль в меню администратора, то на корабле вообще не будет программного обеспечения.

Заключение

Так что я пришел к выводу, что где-то после метода сохранения (возможно, в post_save) событие) программное обеспечение снова удаляется ... На данный момент мне нужна помощь.

Идеи

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

Модели (упрощенно):

class ShipFields(models.Model):
    body = models.ForeignKey(to=Body, verbose_name="Body", on_delete=models.SET_NULL,
                         null=True, blank=False, default=None)
    software = models.ManyToManyField(Software, default=None, blank=True)
    ...

class ShipBlueprints(ShipFields, models.Model):
    class Meta:
        ordering = ['name']
        verbose_name = "Ship Blueprint"
        verbose_name_plural = "Ship Blueprints"

class Ship(ShipFields, models.Model):
    name = models.CharField(max_length=256, unique=True)
    ship_type = models.ForeignKey(to=ShipBlueprints, on_delete=models.SET_NULL, null=True)
    ...
    __original_ship_type = None

    def __init__(self, *args, **kwargs):
        super(Ship, self).__init__(*args, **kwargs)
        self.__original_ship_type = self.ship_type

    def save(self, force_insert=False, force_update=False, *args, **kwargs):
        # check if the ship type is changed
        if self.ship_type != self.__original_ship_type:
            ...
            # copy all fields from the related ShipBlueprints to the fields of Ship
            # 1. 
            print(self.software.all())
            self.body = self.ship_type.body
            self.software.set(self.ship_type.software.all())
            # or
            # for soft in self.ship_type.software.all():
            #     self.software.add(soft)
            # 2. 
            print(self.software.all())
            ...
        super(Ship, self).save(force_insert, force_update, *args, **kwargs)
        # 2. 
        print(self.software.all())
        print(Ship.objects.get(name=self.name).software.all())
        self.__original_ship_type = self.ship_type

Думаю, я сузил проблему. Когда я изменяю ship_type через администраторскую часть, many_to_many_fields не обновляется. НО, когда я изменяю ship_type через оболочку django, он работает отлично!

Когда я использую форму в представлении, он тоже работает. Так что мой код работает просто отлично, но страница администратора почему-то является проблемой ... Для меня это нормально, но похоже на ошибку в методе сохранения администратора, возможно, я сообщу об этом.

Спасибо всем за идеи.

1 Ответ

0 голосов
/ 22 января 2020

Хм, я думаю, это из-за __original_ship_type. Это также должен быть внешний ключ для ShipBlueprints. Тип __original_ship_type не сохраняет ничего внутри каждой строки, поскольку он не является атрибутом / столбцом базы данных. Тип __original_ship_type является просто частью модели для записи в базе данных.

Это на самом деле один вариант. Другой вариант заключается в использовании Django сигналов, в частности, pre_save. Когда вы используете pre_save, вы получаете экземпляр NEW и используете идентификатор этого экземпляра NEW, чтобы выполнить запрос БД для старого экземпляра. После этого вы сохраняете объект таким образом. Сигнал pre_save не обязательно означает, что вы должны что-то там сохранять. Это просто сигнал вызова функции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...