如何过滤django modelformset manytomany成员的查询集

时间:2014-03-13 20:36:49

标签: python django django-forms

我无法使用正确的代码,但我发现了披萨/浇头问题并且它已经关闭了,所以我要修改它来提问我的问题。 Django ModelForm for Many-to-Many fields

我们有Pizza和Topping很棒。让我们说我们经营一系列商店而不是所有商店都有所有成分。这意味着我们需要一个Store类,它具有一个位置,并且有许多用于浇头。然后让我们推出一个引用商店的订单,并且因为您经常订购不止一个,所以在Pizza上有很多。

models.py

from django import models

class Topping(models.Model):
    name = models.TextField()

class Pizza(models.Model):
    size = models.TextField()
    toppings = models.ManyToManyField(Topping)

class Restaraunt(models.Model):
    name = models.TextField()
    toppings = models.ManyToManyField(Topping)


class Order(models.Model):
    customer = models.ForeignKey('User')
    location = models.ForeignKey(Restaraunt)
    pizzas = models.ManyToManyField(Pizza)

forms.py

from django import forms
from django.forms import ModelForm
from models import *

class PizzaForm(ModelForm):

    class Meta:
        model = Pizza

    toppings = forms.ModelMultipleChoiceField(
        widget=forms.CheckboxSelectMultiple(),
        queryset=Topping.objects.all()
    )

views.py

from django.shortcuts import render, redirect, get_object_or_404
from django.forms.models import modelformset_factory
from django.forms import CheckboxSelectMultiple

from models import *
from forms import *


def order_pizzas(request, order_id):
    order_id = int(order_id)
    order = get_object_or_404(Order, id=order_id)

    restaraunt = order.restaraunt

    PizzaFormSet = modelformset_factory(Pizza, form=PizzaForm, extra=1)

    pizza_form = PizzaFormSet(request.POST or None)

    return render(request, "place_order.html", {
        'restaraunt': restaraunt,
        'pizza_form': pizza_form,
    })

如果那些人实际上并没有打败我,请不要打扰我。就像我说的那样,由于种种原因我不能在这里发布真实的代码,部分原因是因为它只是大量的。

为了这个例子,假设该人已经找到了离他们家最近的餐厅,并开始了订购过程。

我尝试使用正确的名称和选项将一个小部件传递给modelformset_factory但是没有传播。

widgets = {'toppings': 
    CheckboxSelectMultiple(
        choices=[t.id for t in restaraunt.toppings.all().order_by('-id')]
    )}

我还尝试扩展BaseModelFormSet来传递额外的数据并尝试将其导入PizzaForm,但是我被卡住了。

基本上我可以告诉我,我需要以某种方式将信息从视图传播到formset到表单,以便可以正确初始化表单上的查询集。我只是无法弄清楚如何做到这一点。

所以这是我找到答案的最接近的答案,但我似乎无法弄清楚如何确保这项工作: Django filter ModelFormSet field choices... different from limiting the Formset's queryset

回应那些想要发布非工作示例的人

关键在于,我并没有试图要求某人发布完全解决我问题的10行代码,而是说明了我的知识缺乏的地方。我知道我生成了一个PizzaFormSet,并最终在该代码的内部某处使用我指定的PizzaForm。但我不知道如何成功地将信息从PizzaFormSet传递到PizzaForm。

基本上,我愿意放弃一笔赏金,以获得关于我错过这个难题的哪一部分的建议。

问题出在哪里

我在forms.py(PizzaForm)中定义了一个表单,它需要为Topping获取一个依赖于情境的查询集。视图order_pizzas决定了哪家餐厅将制作和提供比萨饼,该餐厅的配料可能与其他餐厅不同。

我不知道如何将这些信息从视图传播到表单,通常你只是将表单子类化并添加一些额外的init kwargs来做你想做的事。

但在这种情况下,我使用的是formset而不是单一的形式。这意味着我必须找到(或制作)一些频道将餐馆信息和/或特定查询集从视图传递到表单到表单。我认为这是我的主要困惑和/或无知。

2 个答案:

答案 0 :(得分:3)

这是一个解决方案。可能有一个更好的,但这应该工作。遍历表单集中的所有表单,并更改choices字段上的toppings变量。像这样:

pizza_form = PizzaFormSet(request.POST or None)

choices = [(t.pk, unicode(t)) for t in restaraunt.toppings.all().order_by('-id')]

for form in pizza_form:
    form.fields['toppings'].choices = choices

你也可以覆盖BaseModelFormset并覆盖_contruct_forms方法,将restaraunt对象传递给表单的__init__,然后在那里更改顶部的选择。但我认为上述解决方案是最快捷,最简单的。它只是引入了一个额外的循环。

答案 1 :(得分:1)

您可以拥有一家制作PizzaForms的工厂:

def pizzaform_factory(restaurant):
    class PizzaForm(ModelForm):

        class Meta:
            model = Pizza

        toppings = forms.ModelMultipleChoiceField(
            widget=forms.CheckboxSelectMultiple(),
            queryset=restaurant.toppings.all()
        )
    return PizzaForm

并在您的视图中使用它:

PizzaForm = pizzaform_factory(restaurant)
PizzaFormSet = modelformset_factory(Pizza, form=PizzaForm, extra=1)

或许对您的模型进行约束以确保您无法创建不可能的订单会很好。如果您不使用pizzaform_factory(即使用管理员,shell脚本或API调用),您仍然可以获得无法填充的订单。