将查询集传递给 django 内联表单中的外键字段

Posted

技术标签:

【中文标题】将查询集传递给 django 内联表单中的外键字段【英文标题】:Passing queryset to foreignkeyfield in django inlineform 【发布时间】:2013-11-20 23:26:49 【问题描述】:

我遇到了一个小问题,我认为这很常见。 这是描述的非常笼统的问题:

class Ownable(models.Model):
    user = models.ForeignKey(django.contrib.auth.models.User)

    class Meta:
        abstract = True


class Bowl(Ownable):
    pass


class Pea(Ownable):
    bowl = models.ForeignKey(bowl)

关系是:User [1:n] Bowl, User [1:n] Pea Bowl [1:n] Pea

现在,当我想创建一个新的 Pea 时,我还需要将其分配给 Bowl,如下所示:

def create_new_pea(request):
    PeaFrom = inlineformset_factory(django.contrib.auth.models.User, Pea)
    return render(request, 'app/pea/create.html', 'formset': PeaFrom())

在这个过程中,我如何能够将QuerySet 传递给bowl 字段,因为用户只能将豆子放入自己的碗中。

我很乐意提出建议。我尝试为 formset-factory 创建一个自定义表单,但我需要 request 实例来了解当前用户。

【问题讨论】:

我现在知道应该使用formfield_callback 完成,但我无法正确获取回调函数。 实际上,我认为formfield_callback 在实例化过程中发生得太早,无法访问用户。唔。也许你可以用一个闭包作为回调来做到这一点。 【参考方案1】:

一种简单的方法是在实例化表单集之后进行。

def create_new_pea(request):
    PeaFormset = inlineformset_factory(django.contrib.auth.models.User, Pea)
    formset = PeaFormset(instance=request.user)
    for form in formset:
        form.fields['bowl'].queryset = request.user.bowl_set.all()
    return render(request, 'app/pea/create.html', 'formset': formset

我认为可以将此行为构建到自定义 Formset 类中,覆盖 _construct_forms 方法:

class UserLimitedFormset(BaseInlineFormset):
    def _construct_forms(self):
        super(UserLimitedFormset, self)._construct_forms()
        for form in self:
            form.fields['bowl'].queryset = self.instance.bowl_set.all()

PeaFormset = inlineformset_factory(django.contrib.auth.models.User, Pea, formset=UserLimitedFormset)

要使用回调,我认为您需要一个闭包或functools.partial 在创建表单集之前记录用户。可能是这个,虽然它未经测试而且我对闭包很生疏:

def create_new_pea(request):
    user = request.user
    def set_queryset(f, **kwargs):
        formfield = f.formfield(**kwargs)
        if f.name == 'bowl':
            formfield.queryset = user.bowl_set.all()
        return formfield
    PeaFormset = inlineformset_factory(django.contrib.auth.models.User, Pea, formfield_callback=set_queryset)

【讨论】:

谢谢,效果很好。我想我只是要向OwnableForm 添加一个方法,该方法使用当前类作为model 参数来指向工厂。这样我应该能够将此行为传递给所有子类。【参考方案2】:

不需要那么复杂 - 只需在表单初始化中添加一个 kwarg 'person'。我称这个新表单为 PeaForm,因为您列出的 PeaForm 实际上是 Formset 类型。

class PeaForm(ModelForm):

    def __init__(self, *args, **kwargs):

        person = kwargs.pop('person')
        super(PeaForm, self).__init__(*args, **kwargs)
        qs = Bowl.objects.filter(user=person)
        self.fields['bowl'].queryset = qs


    class Meta():
        model = Pea

当你在工厂中使用它时,你只需要在制作表单时对方法进行柯里化:

from django.utils.functional import curry

person = request.user
PeaFormset = inlineformset_factory(django.contrib.auth.models.User, Pea, form=PeaForm)
PeaFormSet.form = staticmethod(curry(PeaForm, person=person))

现在只有属于用户的碗才会出现在 PeaForm 的 QS 中。

【讨论】:

以上是关于将查询集传递给 django 内联表单中的外键字段的主要内容,如果未能解决你的问题,请参考以下文章

最初在 django 内联表单集中设置不同的外键值

django admin中的外键表单字段

带有内联表单集的 Django 表单验证

姜戈。从表单对象访问模板中的外键字段

Django:使用原始查询来限制 ModelForm 中的外键选择字段

如何使用Django中的外键关系中的任何/ exists / all逻辑检索查询集?