Django BaseGenericInlineFormSet表单不将FormSet实例作为表单实例related_object继承 [英] Django BaseGenericInlineFormSet forms not inheriting FormSet instance as form instance related_object

查看:185
本文介绍了Django BaseGenericInlineFormSet表单不将FormSet实例作为表单实例related_object继承的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用的是Django 1.8,我有一个Image类,如下所示:

I'm using Django 1.8 and I have an Image class that looks like this:

# 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给它:

And a "parent" class that has a GenericRelation to it:

# 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})

在ipdb中,这是我得到的:

When I inspect the formset in ipdb, this is what I get:

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

这是造成问题因为当我到达 Image.clean()我收到错误:

This is causing problems because when I get to Image.clean() I get an error:

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

如果我取消注释我提到的两行,我不需要,它的作品,我不得到错误了。但是不是使用 BaseGenericInlineFormSet 的整个角度自动链接表单到他们的模型和相关模型?如果我必须手动黑客的ImageForm实例,并附加一个产品实例到其 related_object ,那么我也可以使用一个简单的ModelFormSet。我错过了什么吗?

If I uncomment those two lines I mentioned I don't need, it works and I don't get the error anymore. But isn't the automatic linking of forms to their models and related models the whole point of using the BaseGenericInlineFormSet? If I have to manually hack the ImageForm instances and attach a Product instance to its related_object, then I might as well use a simple ModelFormSet. Am I missing something?

更新

如果我发表评论 Image.clean out,即使没有相关对象的手动附件,代码也可以工作。这意味着BaseGenericInlineFormSet完全处理链接,但是在它的子模型上调用 clean 之后,这样做是真的,考虑到Model.clean来提供自定义模型验证。我正在看一下Django源码,但还没有弄清楚链接的位置。

If I comment Image.clean out, the code works even without the manual attachment of related objects. This means the BaseGenericInlineFormSet does handle the linking after all, but it does so AFTER it calls clean on the child model, which is really not ok considering that Model.clean "should be used to provide custom model validation". I'm taking a look at the Django source but haven't yet figured out exactly where it does the linking. Tips are welcome.

更新2

显然链接是在inlineFormSet save_new 方法:

Apparently the linking is done in the InlineFormSet save_new method:

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#L46

作为一个实验,我将该代码移动到自定义 _construct_form 方法:

As an experiment, I've moved that code to a custom _construct_form method:

 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

它解决了我的问题。这样我就不用这个手册链接了。我没有运行测试或编写补丁,但是如果有人决定在未来做出决定,那可能是第一步(也许我自己在某一点上)。

It solved my problem. This way I don't have to do that manual linking. I haven't run the tests or written a patch but this could be a first step if someone decides to do it in the future (perhaps myself at one point).

现在我用手动链接来保持我的解决方案。不想使用恶意版本的Django。

For now I'm keeping my solution with manual linking though. Don't want to work with a hacked version of Django.

推荐答案

由于我没有任何反馈,我会假设这是一个Django的错误,真的好像是这样。我在这里提交了一个机票: https://code.djangoproject.com/ticket/25488

Since I've got no feedback, I'll assume it's a Django bug, and it really seems to be the case. I filed a ticket here: https://code.djangoproject.com/ticket/25488

解决这个问题的解决方案是我之前提到的(即,在视图中迭代窗体并手动链接到产品)或使用固定的FormSet类,喜欢:

The solution until this is solved is either what I suggested earlier (i.e. iterating over the forms in the view and linking them to the product manually) or using a fixed FormSet class, something like:

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
)

这篇关于Django BaseGenericInlineFormSet表单不将FormSet实例作为表单实例related_object继承的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆