Как отфильтровать модель по нескольким формам?
У меня есть одна модель автомобиля, которую я хотел бы отфильтровать через взаимозависимые ModelChoiceField's:
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 с этим make. Но здесь все, что я передаю, - "Нет", и получаю Выберите правильный выбор. Этот выбор не является одним из доступных вариантов. ошибка во второй форме.
Любые предложения или отзывы будут приветствоваться и высоко ценится.
Спасибо.
2 ответа
Первый пункт: формы моделей предназначены для создания / редактирования моделей, поэтому вы должны использовать простые формы здесь. Ваша ошибка связана с тем, что make
поле в вашем ModelSelectForm
но нигде не устанавливая его значение. Также, ModelChoiceField
предназначен для получения экземпляра модели, а не значения поля, так что вы действительно хотите ChoiceField
Вот.
Второй момент, поскольку ваша цель - отображать отфильтрованную информацию, а не создавать или редактировать что-либо, вы должны использовать GET
запросы (как и для любой функции поиска).
Чтобы ваша вторая форма работала как положено (однажды портированная на обычную Form
с одним model
поле), вам нужно будет передать 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)