Как в 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 шт):

Автор решения: Medvedev

Если в вашей форме это поле является необязательным, и вы хотите возвращать все объекты если 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 отфильтрованные по заполненным полям.

Для понимания ** здесь есть ответ

→ Ссылка
Автор решения: user656458
filters = {k:v for k,v in request.POST.items() if v != ''}

abs = ABS.objects.filter(**filters)
→ Ссылка