Интерфейс администратора Django: Понимание поведения при сохранении формы с помощью параметра commit

В настоящее время я работаю с админским интерфейсом Django и столкнулся с неожиданным поведением, связанным с сохранением формы с параметром commit. Вот что я пытаюсь понять:

Контекст: Я использую пользовательскую форму (DeckCreateForm) с переопределенным методом save для сохранения данных колоды. В этом методе я установил параметр commit в значение False, чтобы предотвратить немедленную фиксацию данных в базе данных.

Наблюдение: Несмотря на то, что для параметра commit установлено значение False, при сохранении экземпляра формы через интерфейс администратора создается впечатление, что данные формы фиксируются в базе данных.

Требуется понимание: Я ищу разъяснения, почему данные формы фиксируются в базе данных, даже если параметр commit явно установлен в False. Кроме того, я хочу понять, какие факторы могут повлиять на поведение параметра commit в контексте интерфейса администратора Django.

Приложенные усилия: Я просмотрел документацию Django, касающуюся сохранения форм и интерфейса администратора, но не нашел четкого объяснения такому поведению. Я также изучил свой код и не обнаружил никаких очевидных ошибок или неправильных конфигураций.

Запрос: Я ищу мнения или объяснения от сообщества Django о том, как интерфейс администратора Django обрабатывает сохранение формы и факторы, которые могут повлиять на поведение параметра commit. Кроме того, любые предложения по устранению неполадок или дальнейшему исследованию будут высоко оценены.

Мой код:

Пользовательская функция сохранения (forms.py):

def save(self, commit=False):
    deck = super().save(commit=False)

    if commit:
        deck.save()
        self.save_m2m()

        cleaned_data = self.cleaned_data
        word_items = cleaned_data.get('word_items')

        with transaction.atomic():
            try:
                for rank, word_item in enumerate(word_items, start=1):
                    deck_entry, created = DeckWord.objects.get_or_create(
                        deck=deck,
                        word_item=word_item,
                        defaults={'rank':1}
                    )
            except IntegrityError as e:
                raise
    return deck

Admin.py

@admin.register(Deck)
class DeckAdmin(admin.ModelAdmin):
    list_display = ['id', 'name', 'description', 'language', 'is_ranked', 'created_by', 'visibility']
    form = DeckCreateForm

Любое руководство или понимание этого вопроса было бы бесценным, чтобы помочь мне лучше понять поведение интерфейса администратора Django. Заранее благодарю за помощь!

Понимание

Да, это может быть немного запутанным. Вы найдете ответы, если углубитесь в логику представления, которое обрабатывает сохранение формы/объекта для панели администратора. Ваш DeckAdmin наследуется от ModelAdmin. Поэтому код логики изменения формы находится здесь:

django>contrib>admin>options.py ModelAdmin._changeform_view

# an extraction from mentioned function
if form_validated:
    new_object = self.save_form(request, form, change=not add)
else:
    new_object = form.instance
if all_valid(formsets) and form_validated:
    self.save_model(request, new_object, form, not add)

Как видите, есть ModelAdmin.save_form и ModelAdmin.save_model, которые называются

    def save_form(self, request, form, change):
        """
        Given a ModelForm return an unsaved instance. ``change`` is True if
        the object is being changed, and False if it's being added.
        """
        return form.save(commit=False)
    def save_model(self, request, obj, form, change):
        """
        Given a model instance save it to the database.
        """
        obj.save()

Как видите, при вызове метода .save_form() не происходит никаких действий с базой данных. Он вызывает ваш пользовательский метод form.save(commit=False), и ваш код соблюдается. Но когда после этого метод ._changeform_view вызывает метод .save_model(), выдается метод obj.save(). И метод obj.save() не уважает код, который вы пишете в вашей пользовательской форме, но уважает логику сохранения, которую вы потенциально можете написать в вашем models.py.

TLDR: Ваша логика form.save выполняется, но после этого метод obj.save() запускает взаимодействие с базой данных независимо от вашей form.save() логики.

Исправление

У вас есть два варианта:

A) Как логическое следствие, вы можете настроить метод .save() вашей модели из файла models.py. Я не знаю, зачем вам это нужно, но в учебных целях вы можете переписать этот метод, чтобы он не фиксировался в базе данных.

В) Вы можете добавить метод save_model() к вашему DeckAdmin.

    def save_model(self, request, obj, form, change):
        # Call the form's save method with commit=False
        deck = form.save(commit=False)

        # Perform any additional logic here
        cleaned_data = form.cleaned_data
        word_items = cleaned_data.get('word_items')

        with transaction.atomic():
            try:
                for rank, word_item in enumerate(word_items, start=1):
                    deck_entry, created = DeckWord.objects.get_or_create(
                        deck=deck,
                        word_item=word_item,
                        defaults={'rank': 1}
                    )
            except IntegrityError as e:
                raise

        # Save the deck instance
        deck.save()
        form.save_m2m()
Вернуться на верх