Единственное большое предостережение в вышеупомянутом подходе - это делегирование сохранения в метод post (), который в представлении по умолчанию выполняется в методе form_valid (), поэтому вам также необходимо переопределить form_valid (), в противном случае post (), например, один выше увидит, что вы сохраняете форму дважды. Что является пустой тратой времени на UpdateView, но довольно пагубным для CreateView.

тим, у меня естьBasket модель, и я хочу подтвердить, что не более5 Itemк нему можно добавить:

class Basket(models.Model):
    items = models.ManyToManyField('Item')

    def save(self, *args, **kwargs):
        self.full_clean()
        super(Basket, self).save(*args, **kwargs)

    def clean(self):
        super(Basket, self).clean()
        if self.items.count() > 5:
            raise ValidationError('This basket can\'t have so many items')

Но при попытке сохранитьBasket a RuntimeError брошен, потому что максимальная глубина рекурсии превышена.

Ошибка заключается в следующем:

ValueError: "<Basket: Basket>" needs to have a value for field "basket" before this many-to-many relationship can be used.

Это происходит вif self.items.count() > 5: линия.

Очевидно, сложности Django просто не позволят вам проверить отношения m2m при сохранении модели. Как я могу проверить их тогда?

 Daniel Roseman22 сент. 2017 г., 12:16
Эти вещи не имеют ничего общего друг с другом. Если у вас есть ошибка рекурсии, вы, вероятно, где-то вызываете метод. Это не имеет ничего общего с проверкой. Пожалуйста, покажите трассировку.
 Louis25 февр. 2019 г., 16:32
Возможный дубликатПроверка модели Django ManyToMany
 Uvar22 сент. 2017 г., 12:44
возможно я что-то не правильно понимаю, но может быть проблема с вызовомself.full_clean() в вашей корзине.
 Daniel Roseman22 сент. 2017 г., 12:26
Ну, вам все равно нужно показать ту часть трассировки, которая показывает эту ошибку. Так как мы не можем понять, что происходит не так.
 dabadaba22 сент. 2017 г., 12:24
Я проследил и да, проблема максимальной глубины рекурсии происходит в другом месте: при вызове__unicode__() метод в самой трассировке. Но ведь проблема происходит вclean() метод, потому что я пытаюсь получить доступ к отношениям m2m, поэтому ничего не меняется.

Ответы на вопрос(2)

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

В принципе это зависит от:

Используя PostgreSQL в качестве движка базы данных (мы уверены, что он не будет работать на Lightdb или MySQL, но стремимся к тому, чтобы это кто-нибудь протестировал), введите код здесь

Переопределение метода post () вашего представления (на основе классов), чтобы оно:

Открывает атомарную транзакциюСохраняет формуСохраняет все формы, если таковые имеютсяВызывает Model.clean () или что-то еще, например Model.full_clean ()Тогда в вашей модели, в методе, названном в п. 2.4 выше, вы увидите все ваши отношения многие ко многим и отношения один ко многим. Вы можете проверить их и выдать ValidationError, чтобы увидеть откат всей транзакции и не повлиять на базу данных.

Это прекрасно работает для меня:

def post(self, request, *args, **kwargs):
    # The self.object atttribute MUST exist and be None in a CreateView. 
    self.object = None
    self.form = self.get_form()     
    self.success_url = reverse_lazy('view', kwargs=self.kwargs)

    if connection.vendor == 'postgresql':
        if self.form.is_valid():
            try:
                with transaction.atomic():
                    self.object = self.form.save()
                    save_related_forms(self) # A separate routine that collects all the formsets in the request and saves them

                    if (hasattr(self.object, 'full_clean') and callable(self.object.full_clean)):
                        self.object.full_clean()
            except (IntegrityError, ValidationError) as e:
                if hasattr(e, 'error_dict') and isinstance(e.error_dict, dict):
                    for field, errors in e.error_dict.items():
                        for error in errors:
                            self.form.add_error(field, error)
                return self.form_invalid(self.form)                    

            return self.form_valid(self.form)
        else:
            return self.form_invalid(self.form)

    else:
        # The standard Djangop post() method
        if self.form.is_valid():
            self.object = self.form.save()
            save_related_forms(self)
            return self.form_valid(self.form)
        else:
            return self.form_invalid(self.form)

И разговор в списке разработчиков здесь:

https://groups.google.com/forum/#!topic/django-developers/pQ-8LmFhXFg

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

Единственное большое предостережение в вышеупомянутом подходе - это делегирование сохранения в метод post (), который в представлении по умолчанию выполняется в методе form_valid (), поэтому вам также необходимо переопределить form_valid (), в противном случае post (), например, один выше увидит, что вы сохраняете форму дважды. Что является пустой тратой времени на UpdateView, но довольно пагубным для CreateView.

Решение Вопроса

никогда проверить отношения в чистом методе модели. Это потому, что в чистое время модель может еще не существовать, как в случае с вашей корзиной. То, чего не существует, также не может иметь отношения.

Вам либо нужно сделать свою проверку наданные формы как указано @bhattravii, или позвонитеform.save(commit=False) и реализовать метод под названиемsave_m2m, который реализует предел.

Чтобы применить ограничение на уровне модели, вы должны слушатьm2m_changed сигнал. Обратите внимание, что обеспечить обратную связь с конечным пользователем намного сложнее, но она предотвращает переполнение корзины различными способами.

 dabadaba22 сент. 2017 г., 13:47
Я возьму это. Хотя оба решения связаны с формами, я искал ответ на создание модели, которое не обязательно было связано с формами, но с самим созданием, независимо от того, как вы это делаете.

Ваш ответ на вопрос