【问题标题】:How to filter a model through multiple forms?如何通过多种形式过滤模型?
【发布时间】:2017-12-21 22:26:32
【问题描述】:

我有一个汽车模型,我想通过相互依赖的 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})

网址:

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”参数传递给第二个视图,然后使用它来过滤具有该 make 的 Car 实例。但是这里我通过的只是“无”,并得到选择一个有效的选择。该选项不是可用选项之一。第二种形式的错误。

欢迎和非常感谢任何建议或反馈。

谢谢。

【问题讨论】:

  • 您需要通过第二个视图将您从第一个表单获得的信息传递到第二个表单。我认为您需要覆盖第二种形式的__init__ 来调整所需字段的查询集。以下是我当天所做的 def __init__(self, *args, **kwargs): self.request = kwargs.pop('request', None) super(GameForm, self).__init__(*args, ** kwargs) 如果不是 self.request.user.is_staff: self.fields['publisher'].queryset = Publisher.objects.filter(id=self.request.user.id) `

标签: python django forms modelform modelchoicefield


【解决方案1】:

由于我在评论中发布的 sn-p 的格式被弄乱了,所以我在这里写下它作为答案。

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)

【讨论】:

    【解决方案2】:

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

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

    要使您的第二个表单按预期工作(一旦移植到带有单个 model 字段的普通 Form),您需要将 make 值传递给表单,并在表单的 @987654331 @,将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/您关于“将'make'传递给第二个视图”的问题:在您的代码 sn-p 中没有任何地方可以将用户定向到 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)
    

    【讨论】:

    • 感谢您的详尽回答。我进行了更改,然后编辑了我的 MakeSelectFormmake_select_view accordingly。但是,我仍然无法将选定的“make”传递给第二个视图,例如它的 URL:/None/?submitted=1&make=BMW 如果我手动更改“None”,则过滤有效。
    • 您没有指定如何从第一个视图转到第二个视图,并且它不在您的代码 sn-p 中。但是我在答案中添加了一个规范示例,成功提交后从第一个视图重定向到第二个视图。
    • 再次感谢。我认为重定向是由form action="{% url 'reviews:model-select' make %}" 在模板中完成的。无论如何,I edited my code 但仍然得到“无”而不是选定的品牌。
    • 表单的action 参数告诉浏览器在哪里提交表单,而不是在表单提交成功时将用户重定向到哪里。大多数时候,您希望表单由显示它的视图处理(通常通过将表单的操作设置为 "#" 或只是一个普通的 "" 来完成)。
    • wrt/ 您的“仍然没有”问题,在 makeselect.html 中,您将表单的操作设置为 {% url 'reviews:model-select' make %}'. That's NOT what you want, cf my above comment, and at this point make` 确实保证为 None。只需将表单的操作设置为空字符串并按照我的回答处理视图中的重定向。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-11
    • 2012-01-29
    • 2021-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多