将查询集传递给 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 内联表单中的外键字段的主要内容,如果未能解决你的问题,请参考以下文章