如何通过多种形式过滤模型?

How to filter a model through multiple forms?

我有一个汽车模型,我想通过相互依赖的 ModelChoiceField 进行过滤:

class Car(models.Model):
    make = models.CharField(max_length=50)
    model = models.CharField(max_length=50)
    platform = models.CharField(max_length=50)

Forms.py:

class MakeSelectForm(forms.ModelForm):
    make = forms.ModelChoiceField(queryset=Car.objects.values_list('make',flat=True).distinct())
    class Meta:
        model = Car
        fields = ["make"]

class ModelSelectForm(forms.ModelForm):
    model = forms.ModelChoiceField(queryset=Car.objects.values_list('model',flat=True).distinct())
    class Meta:
        model = Car
        fields = ["make", "model"]

Views.py:

def make_select_view(request):
    form = MakeSelectForm()
    make = None
    if request.method == "POST":
        form = MakeSelectForm(request.POST)
        if form.is_valid():
            make = form.cleaned_data['make']
    return render(request, "reviews/makeselect.html", {"form": form, "make": make})

def model_select_view(request, make):
    form = ModelSelectForm()
    model = None
    if request.method == "POST":
        form = MakeSelectForm(request.POST)
        if form.is_valid():
            model = form.cleaned_data['model']
    return render(request, "reviews/modelselect.html", {"form": form, "model": model})

URL的:

urlpatterns = [
    url(r'^$', views.make_select_view, name="make-select"),
    url(r'^(?P<make>\w+)/$', views.model_select_view, name="model-select"),
]

Makeselect.html:

<form action="{% url 'reviews:model-select' make %}" method="POST">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="Select" />
</form>

现在,我必须在发布时将第一个表单的 "make" 参数传递给第二个视图,然后使用它来过滤具有该品牌的 Car 实例。但这里我只传递 "None",得到 Select 一个有效的选择。该选项不是可用选项之一。 第二种形式出错。

任何建议或反馈都将受到欢迎,我们将不胜感激。

谢谢。

由于我在评论中发布的代码片段的格式弄乱了,所以我在这里写下它作为答案。

def __init__(self, *args, **kwargs):
    self.request = kwargs.pop('request', None)
    super(GameForm, self).__init__(*args, **kwargs)
    if not self.request.user.is_staff:
        self.fields['publisher'].queryset = Publisher.objects.filter(id=self.request.user.id)

第一点:模型表单是用来创建/编辑模型的,所以这里应该使用普通表单。您的错误来自于 ModelSelectForm 中的 make 字段但未在任何地方设置其值。另外,ModelChoiceField 是为了检索模型实例,而不是字段的值,所以你真的想要一个 ChoiceField 在这里。

第二点,由于您的目标是显示过滤后的信息 - 而不是创建或编辑任何内容 - 您应该使用 GET 查询(实际上就像任何 "search" 功能一样)。

为了使您的第二个表单按预期工作(一旦移植到具有单个 model 字段的普通 Form),您需要将 make 值传递给表单,并在表单的 __init__() 中将 model 字段选项更新为过滤后的查询集。

此外,由于您将使用 GET 作为表单的方法,因此在决定使用或不使用 request.GET 数据实例化之前,您必须检查表单是否已经提交,否则用户会在第一次显示时收到错误消息,甚至在他们有机会提交任何内容之前。这通常使用表单提交按钮的名称和值或表单本身的隐藏字段来解决:

表格:

class  ModelSelectForm(forms.Form):
    model = forms.ChoiceField()

    def __init__(self, *args, **kwargs):
        make = kwargs.pop("make", None)
        if not make:
            raise ValueError("expected a 'make' keyword arg")
        super(ModelSelectForm, self).__init__(*args, **kwargs)
        qs = Car.objects.filter(make=make).values_list('model',flat=True).distinct()
        choices = [(value, value) for value in qs]
        self.fields["model"].choices = choices

观看次数:

def model_select_view(request, make):
    model = None
    if request.GET.get("submitted", None):
        form = ModelSelectForm(request.GET, make=make)
        if form.is_valid():
            model = form.cleaned_data['model']
    else:
        form = ModelSelectForm(make=make)
    context = {"form": form, "model": model, "make: make}
    return render(request, "reviews/modelselect.html", context)

模板:

<form action="{% url 'reviews:model-select' make %}" method="GET">
    {% csrf_token %}
    <input type="hidden" name="submitted" value="1" />
    {{ form.as_p }}
    <input type="submit" value="Select" />
</form>

wrt/ 你关于 "passing 'make' to the second view" 的问题:在你的代码片段中没有任何地方你将用户引导到 model-select 视图,但我假设你想要的是用户被重定向到它一旦他在第一个视图中成功选择了 "make"。如果是,您的第一个视图的代码应该处理成功提交表单的情况,即:

def make_select_view(request):
    if request.GET.get("submitted", None):
        form = MakeSelectForm(request.GET)
        if form.is_valid():
            make = form.cleaned_data['make']
            # send the user to the model selection view
            return redirect("reviews:model-select", make=make)

    else:
        form = MakeSelectForm()
    context = {"form": form}
    return render(request, "reviews/makeselect.html", context)