Почему экземпляр модели не передается в `ModelForm` внутри `DeleteView`.

В Django 5.0.2 давайте начнем с простой модели:

from django.db import models


class Book(models.Model):
    title = models.CharField(max_length=100)

Я хочу сделать некоторую проверку формы внутри ModelForm, чтобы определить, должно ли удаление происходить внутри DeleteView. Проще говоря, я думаю, что форма уже способна показывать ошибки, не связанные с полем, и я хочу избежать использования messages.error() для отправки сообщения об ошибке.

Так что я начал с ModelForm:

from django.forms import ModelForm


class DeleteBookForm(ModelForm):
    class Meta:
        model = Book
        fields = []

    def clean(self):
        super().clean()
        if not self.deletable():
            self.add_error(None, 'This object can not be deleted')

    def deletable(self) -> bool:
        # do some evaluation on Book here
        book = self.instance
        if book.title == 'delete me please':
            return True
        else:
            return False

и затем DeleteView:

from django.views.generic import DeleteView
from django.http import HttpResponseRedirect


class DeleteBookView(DeleteView):
    model = Book
    form_class = DeleteBookForm

    def form_valid(self, form):
        self.object.delete()
        messages.success(self.request, 'The object has been deleted successfully.')
        return HttpResponseRedirect(self.get_success_url())

Но self.instance, похоже, находится None внутри DeleteBookForm.deletable(). Это указывает на то, что экземпляр модели Book не поставляется/вставляется в ModelForm при его создании. Я полагаю, что это не относится к UpdateView.

Потратив некоторое время на чтение, я нашел этот код на BaseDeleteView.post() (родитель DeleteView). Обратите внимание, что он действительно не вставляет объект в форму:

class BaseDeleteView(DeletionMixin, FormMixin, BaseDetailView):
    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        form = self.get_form()
        if form.is_valid():
            return self.form_valid(form)
        else:
            return self.form_invalid(form)

В итоге, чтобы заставить его работать, я вручную вставил экземпляр при создании формы:

class DeleteBookView(DeleteView):
    def get_form(self, form_class=None):
        # insert instance so it can be read inside the form
        return self.form_class(
            instance=self.object,
            **self.get_form_kwargs()
        )

Опять же, экземпляр модели поставляется по умолчанию в UpdateView.

Итак, мои вопросы таковы:

  1. Почему экземпляр модели не поставляется по умолчанию на DeleteView?
  2. Неправильно ли делать оценку экземпляра модели с помощью ModelForm на DeleteView? Я не уверен, но мне показалось, что Form - это наиболее логичный способ разместить оценку экземпляра модели, а не на View, например.

Вам нужно только добавить ModelFormMixin [Django-doc], что позволит передать объект форме:

from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.edit import ModelFormMixin


class DeleteBookView(SuccessMessageMixin, ModelFormMixin, DeleteView):
    model = Book
    form_class = DeleteBookForm
    success_message = 'The object has been deleted successfully.'

По умолчанию DeleteView работает с FormMixin, поэтому не передает объект в качестве модели. Обычно это связано с тем, что форма представляет собой скорее подтверждение, возможно, с чекбоксом, а не что-то связанное с объектом.

При импорте формы используется FormMixin, а методы используются следующие.


    def get_form_class(self):
        """Return the form class to use."""
        return self.form_class

    def get_form(self, form_class=None):
        """Return an instance of the form to be used in this view."""
        if form_class is None:
            form_class = self.get_form_class()
        return form_class(**self.get_form_kwargs())

    def get_form_kwargs(self):
        """Return the keyword arguments for instantiating the form."""
        kwargs = {
            "initial": self.get_initial(),
            "prefix": self.get_prefix(),
        }

        if self.request.method in ("POST", "PUT"):
            kwargs.update(
                {
                    "data": self.request.POST,
                    "files": self.request.FILES,
                }
            )
        return kwargs

https://github.com/django/django/blob/main/django/views/generic/edit.py

Информация об объекте не передается. Если вы хотите передать и проверить информацию об объекте, сделайте следующее.

def get_form_kwargs(self):
  kwargs = super().get_form_kwargs()
  kwargs['instance'] = self.object
  return kwargs
Вернуться на верх