在 Django inlineformset_factory 中过滤查询集

Posted

技术标签:

【中文标题】在 Django inlineformset_factory 中过滤查询集【英文标题】:Filter Queryset in Django inlineformset_factory 【发布时间】:2011-02-04 13:55:58 【问题描述】:

我正在尝试使用 inlineformset_factory 生成表单集。我的模型定义为:

class Measurement(models.Model):
    subject = models.ForeignKey(Subject)
    experiment = models.ForeignKey(Experiment)
    assay = models.ForeignKey(Assay)
    values = models.CommaSeparatedIntegerField(blank=True, null=True)

class Experiment(models.Model):
    date = models.DateField()
    notes = models.TextField(max_length = 500, blank=True)
    subjects= models.ManyToManyField(Subject)

在我看来,我有:

def add_measurement(request, experiment_id):
    experiment = get_object_or_404(Experiment, pk=experiment_id)
    MeasurementFormSet = inlineformset_factory(Experiment, Measurement, extra=10, exclude=('experiment'))
    if request.method == 'POST':
        formset = MeasurementFormSet(request.POST,instance=experiment)
        if formset.is_valid():
            formset.save()
            return HttpResponseRedirect( experiment.get_absolute_url() ) 
    else:
        formset = MeasurementFormSet(instance=experiment)
    return render_to_response("data_entry_form.html", "formset": formset, "experiment": experiment , context_instance=RequestContext(request))

但我想将 Measurement.subject 字段限制为仅在 Experiment.subjects 查询集中定义的主题。我已经尝试了几种不同的方法来做到这一点,但我有点不确定实现这一目标的最佳方法是什么。我试图用新的查询集覆盖 BaseInlineFormset 类,但无法弄清楚如何正确传递实验参数。

更新的答案(我还包括了这里的信息作为将参数传递给表单集link的一种方式):

views.py

def add_measurement(request, experiment_id):    
    experiment = get_object_or_404(Experiment, pk=experiment_id)    
    MeasurementFormSet = inlineformset_factory(Experiment, Measurement, extra=10, can_delete=True, form=MeasurementForm)    
    MeasurementFormSet.form = staticmethod(curry(MeasurementForm, experiment=experiment))
    if request.method == 'POST':
        formset = MeasurementFormSet(request.POST)      
        if formset.is_valid():
        formset.save()
        return HttpResponseRedirect( experiment.get_absolute_url() )    
    else:
        formset = MeasurementFormSet()
        return render_to_response("data_entry_form.html", "formset": formset, "experiment": experiment , context_instance=RequestContext(request))

forms.py

class MeasurementForm(ModelForm):
    class Meta:
        model = Measurement
    def __init__(self, *args, **kwargs):
        experiment = kwargs.pop('experiment')
        super(MeasurementForm, self).__init__(*args, **kwargs)
        self.fields["subject"].queryset = Subject.objects.filter(experiment=experiment)

【问题讨论】:

我以前从未听说过 Python 中的 curry(),它绝对不是内置的。编辑:...啊.. 我刚刚注意到链接的帖子:来自 django.utils .functional 导入咖喱 这在 Django 1.5 中仍然有效吗?我收到以下错误:__init__() got an unexpected keyword argument 'empty_permitted' 【参考方案1】:

我遇到了同样的问题(使用有限的可能值初始化内联表单),更新的答案效果很好。感谢那。 无论如何,我认为有些事情可以做得更好,但我不知道如何做到这一点。该解决方案中的新问题是您在每个内联表单中访问数据库:而不是在所有相同字段中使用相同的查询集,而是在此行中每次重新计算它:

 self.fields["subject"].queryset = Subject.objects.filter(experiment=experiment)

我在这个问题上是对的还是引擎盖后面有一些懒惰的 django 魔法?如果我是对的,我怎么能避免(可能数百次)对数据库的点击? 你好, 佩德罗

【讨论】:

为了避免命中数据库,只需使用@ŁukaszKorzybski 的@ŁukaszKorzybski's trick -- if not hasattr(self, '_queryset'): 包围查询集过滤器【参考方案2】:

(编辑:没有正确读取代码块,这里应该是您的问题的解决方案):

相信你需要:http://docs.djangoproject.com/en/dev/ref/forms/fields/#modelchoicefield

Forms.py:

class MeasurementForm(ModelForm):
 subject = forms.ModelChoiceField(queryset = Expirement.objects.all())
 class Meta:
   model = Measurement

Views.py:

inlineformset_factory(
  Experiment, Measurement, extra=10, 
  exclude=('experiment'), form=MeasurementForm
  )

使用表单参数绑定到表单集。

【讨论】:

我试过了,但运气不佳。过滤器和前缀元素的作用是什么? 昨天编辑了我的答案,modelchoicefield 应该有解决方案的成分。 我更改了我的代码(请参阅问题中的补充内容),但现在错误出现在保存时。现在的错误是“无法分配“”:“Measurement.experiment”必须是“Experiment”实例。” 抱歉,帮不上忙。您问题中的更新代码未使用 ModelChoiceField 方法。我可能没有理解您问题的根本原因,因为我的建议对您没有用处。

以上是关于在 Django inlineformset_factory 中过滤查询集的主要内容,如果未能解决你的问题,请参考以下文章

django-在Django中选择文件后预览照片

在没有 django 表单的 django 模板上工作 [关闭]

在标题 Django 管理工具中隐藏 Django 徽标/名称

如何在 Django 之外使用 Django 模型?

如何在django系统外使用django的ORM

Django入门----在pycharm上面构建django遇见的问题