Как отфильтровать модель по нескольким формам?

У меня есть одна модель автомобиля, которую я хотел бы отфильтровать через взаимозависимые 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)
Другие вопросы по тегам