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">