如何通过多个表单过滤模型?



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

选择.html:

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

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

任何建议或反馈将受到欢迎和高度赞赏。

谢谢。

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

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

为了使第二个窗体按预期工作(一旦移植到具有单个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/您关于"将'make'传递给第二个视图"的问题:您的代码片段中没有任何地方可以将用户定向到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)

由于我在评论中发布的片段的格式搞砸了,所以我在这里写这个作为答案。

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)

最新更新