Django BaseGenericInlineFormSet формы не наследуют экземпляр FormSet как экземпляр формы related_object

Я использую Django 1.8, и у меня есть класс Image, который выглядит так:

# The `child` class
class Image(models.Model):
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()

    related_object = GenericForeignKey('content_type', 'object_id')

    image = models.ImageField(...)

    def clean(self):
        related_class = self.content_type.model_class()
        # Do some validation that relies on the related_class

И родительский класс, имеющий GenericRelation к нему:

# The `parent` class
class Product(models.Model):
    ...
    images = GenericRelation('Image')

Это мое (упрощенное) представление:

from django.shortcuts import render, get_object_or_404
from django.views.generic import View
from django.contrib.contenttypes.forms import generic_inlineformset_factory

ProductImageInlineFormset = generic_inlineformset_factory(
    Image, extra=1)

class ProductImageView(View):
    ...
    def post(self, request, id):
        product = get_object_or_404(Product.objects.by_id(id))
        image_formset = ProductImageInlineFormset(
            request.POST, request.FILES, instance=product)
        # I SHOULDN'T NEED THE FOLLOWING TWO LINES ->
        # for form in image_formset:
        #     form.instance.related_object = product
        import ipdb; ipdb.set_trace()

        if image_formset.is_valid():
            image_formset.save()

        return render(request, self.template,
                      context={'cid': id, 'formset': image_formset})

Когда я проверяю formset в ipdb, это то, что я получаю:

ipdb> image_formset.forms[0].instance.related_object is None
True

Это вызывает проблемы, потому что, когда я добираюсь до Image.clean() Я получаю ошибку:

django.db.models.fields.related.RelatedObjectDoesNotExist: Image has no content_type.

Если я раскомментирую те две строки, которые я упомянул, они мне не нужны, это работает, и я больше не получаю ошибку. Но не является ли автоматическое связывание форм с их моделями и родственными моделями смыслом использования BaseGenericInlineFormSet? Если мне придется вручную взломать экземпляры ImageForm и прикрепить Product экземпляр его related_objectтогда я мог бы также использовать простой ModelFormSet. Я что-то пропустил?

ОБНОВИТЬ

Если я прокомментирую Image.clean Код работает даже без ручного присоединения связанных объектов. Это означает, что BaseGenericInlineFormSet все-таки обрабатывает связывание, но делает это ПОСЛЕ того, как он вызывает clean на дочерней модели, которая на самом деле не в порядке, учитывая, что Model.clean " должен использоваться для обеспечения пользовательской проверки модели". Я смотрю на источник Django, но еще не выяснил, где именно он делает ссылки. Советы приветствуются.

ОБНОВЛЕНИЕ 2

По-видимому, связывание выполняется в InlineFormSet save_new метод:

def save_new(self, form, commit=True):
    setattr(form.instance, self.ct_field.get_attname(),
        ContentType.objects.get_for_model(self.instance).pk)
    setattr(form.instance, self.ct_fk_field.get_attname(),
        self.instance.pk)
    return form.save(commit=commit)

https://github.com/django/django/blob/master/django/contrib/contenttypes/forms.py

В качестве эксперимента я переместил этот код в пользовательский _construct_form метод:

 def _construct_form(self, i, **kwargs):
     form = super()._construct_form(i, **kwargs)
     setattr(form.instance, self.ct_field.get_attname(),
         ContentType.objects.get_for_model(self.instance).pk)
     setattr(form.instance, self.ct_fk_field.get_attname(),
         self.instance.pk)
     return form

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

На данный момент я держу свое решение с ручной связью, хотя. Не хочу работать со взломанной версией Django.

1 ответ

Решение

Поскольку я не получил обратной связи, я предполагаю, что это ошибка Django, и это действительно так. Я подал заявку здесь: https://code.djangoproject.com/ticket/25488

Решение до тех пор, пока это не будет решено, - это либо то, что я предложил ранее (то есть, перебирая формы в представлении и связывая их с продуктом вручную), либо используя фиксированный класс FormSet, что-то вроде:

class FixedBaseGenericInlineFormSet(BaseGenericInlineFormSet):
    def _construct_form(self, i, **kwargs):
        form = super()._construct_form(i, **kwargs)
        setattr(form.instance, self.ct_field.get_attname(),
            ContentType.objects.get_for_model(self.instance).pk)
        setattr(form.instance, self.ct_fk_field.get_attname(),
            self.instance.pk)
        return form

ProductImageInlineFormset = generic_inlineformset_factory(
    Image,
    form=ProductImageForm,
    formset=FixedBaseGenericInlineFormSet,
    extra=1
)
Другие вопросы по тегам