Django - Две формы на одной странице, как я могу сохранить параметры URL при отправке любой из форм?

Я создаю приложение, которое содержит список блюд, где каждое блюдо имеет различные фильтры, цену и рейтинг.

Фильтры похожи на теги; пользователь может выбрать несколько, и на странице отображаются только те блюда, которые соответствуют выбранным фильтрам.

Цена и рейтинг являются целыми числами, и пользователь может сортировать либо по цене, либо по рейтингу, что сортирует блюда (самые дешевые -> самые дорогие по цене, самые высокие -> самые низкие по рейтингу).

Я создал две формы в Django, одну для фильтров и одну для сортировки, и они обе работают сами по себе. Однако, допустим, я отправляю форму сортировки для сортировки по цене; когда я это делаю, она действительно сортирует по цене, но удаляет все предыдущие фильтры, которые я отправил.

Ниже приведены важные части кода, относящиеся к этой проблеме:

views.py

def meals(request):
    meal_list = Meal.objects.all()
    tags = Tag.objects.all()
    reviews = Review.objects.all()
    filter_form = FilterForm(request.GET or None)
    sorting_form = SortingForm(request.GET or None)
    sort = ""
    active_filters = []

    if filter_form.is_valid():
        tags = filter_form.cleaned_data.get('tags')
        for tag in tags:
            meal_list = meal_list.filter(tags__name=tag)
            active_filters.append(tag)

    if sorting_form.is_valid():
        sort = sorting_form.cleaned_data.get('sort')
        if sort == "price":
            meal_list = meal_list.order_by('price')
        else:
            meal_list = meal_list.order_by('-rating')

    paginator = Paginator(meal_list, 8)
    page_number = request.GET.get('page')
    meals_on_page = paginator.get_page(page_number)

    context = {"meal_list": meal_list,
               "distances": distances,
               "tags": tags,
               "reviews": reviews,
               "active_filters": active_filters,
               "meals_on_page": meals_on_page,
               "filter_form": filter_form,
               "sorting_form": sorting_form,
               }
    return render(request, 'meals/meals.html', context)

forms.py

from django import forms

# Tag is the model for the filters, it is just a ManyToManyField that contains a name attribute
from .models import Tag


class FilterForm(forms.Form):
    tags = forms.ModelMultipleChoiceField(
        queryset=Tag.objects.all(), widget=forms.CheckboxSelectMultiple)


class SortingForm(forms.Form):
    SORT_CHOICES = [
        ('price', 'Price'),
        ('rating', 'Rating'),
    ]
    sort = forms.ChoiceField(choices=SORT_CHOICES, widget=forms.Select)

meals.html

  <form method="get">
    {% for field in filter_form %}
        {{ field.as_widget }} {{ field.label_tag }}
    {% endfor %}
    <input type="submit" value="Filter">
  </form>


  <form method="get">
    {% for field in sorting_form %}
        {{ field.as_widget }}
    {% endfor %}
    <input type="submit" value="Sort">
  </form>

Я слишком долго пытался решить эту проблему, и самое близкое, чего я добился, это использование get_copy = request.GET.copy() и попытка вручную добавить параметры URL обратно в конец URL после отправки формы. Однако, ни один из моих подходов не сработал.

Заранее спасибо за помощь!

В вашем представлении Django вы можете получить доступ к текущим параметрам URL с помощью атрибута GET объекта запроса. Чтобы сохранить эти параметры при отправке любой из форм, вы можете включить их в атрибут действия формы в вашем шаблоне.

Например, в вашем шаблоне вы можете обновить атрибут действия формы, чтобы включить в него текущие параметры URL следующим образом:

<form method="get" action="{% url 'meals' %}?{{ request.GET.urlencode }}">

Это добавит текущие параметры URL к действию формы, так что когда форма будет отправлена, текущие параметры будут включены в запрос.

В вашем представлении вы можете получить доступ к этим параметрам, используя словарь request.GET. Вы можете использовать эти параметры для фильтрации и сортировки вашего набора запросов соответствующим образом перед рендерингом шаблона.

Примечание: перед обработкой данных формы следует также проверить, действительны ли формы, чтобы избежать неожиданного поведения.

Также, вы можете использовать Django's forms.HiddenInput() для включения текущих параметров в ваши формы как скрытые поля, таким образом вам не нужно обновлять атрибут action формы.

В итоге я решил эту проблему, потратив на нее (слишком много) часов.

Я сделал это следующим образом: создал свой собственный словарь параметров, а затем передал эти параметры в формы как скрытые входы.

Ниже представлен добавленный код:

views.py

get_copy = request.GET.copy()
parameters = get_copy.urlencode()
get_copy.pop('page', None)
param_list = parameters.split("&")
param_dict = defaultdict(list)
for param in param_list:
    try:
        key, value = param.split("=")
    except ValueError:
        # Handle the case where there's no "=" in the parameter string
        key = param
        value = ""
    if key == "tags":
        param_dict[key].append(int(value))
    else:
        param_dict[key].append(value)
# Django requires me to turn dictionary to items here, rather than in the template
param_dict_items = param_dict.items()

meals.html

<form method="get" action="{% url 'meals' %}?{{ request.GET.urlencode }}">
{% for field in filter_form %}
    {{ field.as_widget }} {{ field.label_tag }}
{% endfor %}

{% for key, value_list in param_dict_items %}
  {% if key != 'tags' %}
    {% for value in value_list %}
      <input type="hidden" name="{{ key }}" value="{{ value }}">
    {% endfor %}
  {% endif %}
{% endfor %}
<input type="submit" value="Filter">
<form method="get" action="{% url 'meals' %}">
{% for field in sorting_form %}
    {{ field.as_widget }}
{% endfor %}

{% for key, value_list in param_dict_items %}
  {% if key != 'sort' %}
    {% for value in value_list %}
      <input type="hidden" name="{{ key }}" value="{{ value }}">
    {% endfor %}
  {% endif %}
{% endfor %}
<input type="submit" value="Sort">
Вернуться на верх