Как в Django проигнорировать objects.filter и достать все объекты?
Делаю сайт по покупке автомобилей, на котором есть форма (фильтр) которая отправляет POST
.
В модели Ads
находятся объекты с такими полями: brand_id, model_id, generation_id.
Проблема: Если на сайте в фильтре позиция "Brand" не будет выбрана, то в request.POST['brand']
вернётся: ''
(пустая строка, потому что <option value="">
, поэтому вывод можно изменить).
Тогда будет эта ошибка: ValueError: Field 'id' expected a number but got ''.
Но суть не в ошибке, мне нужно чтобы если ничего не выбрано, то чтобы Django вернул все объекты, то есть проигнорировал фильтр brand_id
.
Примечание: Не только "Brand" могут не выбрать, а любое из полей. Нужно, чтобы выбрав "Brand", но не выбрав "Model" фильтр проигнорировал model_id а провёл поиск только по "Brand". И так далее. Таких полей (как "Brand" и "Model") для поиска ещё плюс 10 штук. Если написать под каждый случай, будет слишком много.
Вот код из views.py
:
context = {
'Body': Brand.objects.all(),
'EngineType': EngineType.objects.all(),
'BoostType': BoostType.objects.all(),
}
context['Ads'] = Ads.objects.filter(brand_id=request.POST['brand'],
model_id=request.POST['model'],
generation_id=request.POST['generation'],
)
return render(request, 'ADS/main.html', context)
Прошу помочь. Если есть идеи, как сделать это совсем по-другому (лучше), то с удовольствием выслушаю.
Теоретическое идеальное решение: параметр который можно передать в Ads.objects.filter(brand_id=request.POST['brand'])
вместо request.POST['brand']
, допустим all()
, но all()
нельзя.
Тогда можно будет с помощью функции (test) с if
внутри проверять каждый параметр brand_id=test(request.POST['brand'])
. А если этот параметр request.POST['brand']
= ''
(пустой строке), то возвращать этот самый волшебный параметр, который бы отменял фильтрация по этому полю (в данном примере: brand_id
).
Ответы (2 шт):
Если в вашей форме это поле является необязательным, и вы хотите возвращать все объекты если brand
пустое, то:
# если ничего не выбрали, то филтруем без request.POST['brand']
if request.POST['brand'] == '':
ads = Ads.objects.filter(
model_id=request.POST['model'],
generation_id=request.POST['generation'],
)
else:
ads = Ads.objects.filter(
brand_id=request.POST['brand'],
model_id=request.POST['model'],
generation_id=request.POST['generation'],
)
context = {
'Body': Brand.objects.all(),
'EngineType': EngineType.objects.all(),
'BoostType': BoostType.objects.all(),
# мне кажется, глазам легче по коду искать конкретную переменную
# чем непонятно в каком месте добавление нового ключ-значения
'Ads': ads
}
return render(request, 'ADS/main.html', context)
Но на всякий случай напишу, что если поле формы нужно сделать обязательным, то к <select>
необходимо установить атрибут required
:
<!-- например так -->
<select name="brand" required>
UPD:
Сейчас стало понятно, что вам нужен Q класс. Почитать подробно как он работает, можно в оф документации.
Какая логика:
Тк вы пишите, что полей много, больше 10, то необходимо создать цикл, что бы проверять, что поле заполнено. Если этого не сделать то будут искаться пустые поля. Пример:
# создаем словарь и в него передаем все ваши поля
filter_parametr = {
'brand': request.POST['brand'],
'model': request.POST['model'],
'generation': request.POST['generation'],
# остальные ваши поля
}
Далее создаем цикл и фильтр, и проверяем поля, если поле заполнено, то добавляем в фильтр:
filters = Q()
for key, value in filter_parametr.items():
if value:
# распаковываем в аргументы
filters &= Q(**{f"{key}_id": value})
И далее вставляете фильтр в сам запрос:
ads = Ads.objects.filter(filters)
В результате будут возвращаться объекты Ads
отфильтрованные по заполненным полям.
Для понимания **
здесь есть ответ
filters = {k:v for k,v in request.POST.items() if v != ''}
abs = ABS.objects.filter(**filters)