Anshul Gupta
Anshul Gupta

Reputation: 331

How to create multiple filters in Django?

I want to create multiple filters in Django.

The screen shot below explains it.

enter image description here

The user may opt not to select some criteria.

If the user does not select Available from, Available till and Advance then I will get the value as None in views.py. If he does not select Category or Capacity then I will get an empty list otherwise I will get a list of Category or Capacity.

The problem arises when there is None or empty list.

Although I have written the code and it works fine but I want to know if there is a better way to do it? My code may have problems if the complexity increases.

forms.py

class RoomForm(forms.Form):

    ROOM_CATEGORIES = (
        ('Regular', 'Regular'),
        ('Executive', 'Executive'),
        ('Deluxe', 'Deluxe'),
        ('King', 'King'),
        ('Queen', 'Queen'),
    )

    category = forms.MultipleChoiceField(
        required=False,
        widget=forms.CheckboxSelectMultiple,
        choices=ROOM_CATEGORIES,
    )

    ROOM_CAPACITY = (
        (1, '1'),
        (2, '2'),
        (3, '3'),
        (4, '4'),
    )
    capacity = forms.MultipleChoiceField(
        required=False,
        widget=forms.CheckboxSelectMultiple,
        choices=ROOM_CAPACITY,
        )

    class TimeInput(forms.TimeInput):
        input_type = 'time'
        default=datetime.time()

    available_from = forms.TimeField(
        required=False,
        widget=TimeInput(),
        )

    available_till = forms.TimeField(
        required=False,
        widget=TimeInput(),
        )

    advance = forms.IntegerField(
        required=False,
    )

    """Function to ensure that booking is done for future and check out is after check in"""
    def clean(self):
        cleaned_data = super().clean()
        available_from = cleaned_data.get("available_from")
        available_till = cleaned_data.get("available_till")
        str_available_from = str(available_from)
        str_available_till = str(available_till)
        format = '%H:%M:%S'
        if str_available_from != 'None':
            try:
                datetime.datetime.strptime(str_available_from, format).time()
            except Exception:
                raise ValidationError(
                    _('Wrong time entered.'),
                    code='Wrong time entered.',
                )
        if str_available_till != 'None':
            try:
                    datetime.datetime.strptime(str_available_till, format).time()
            except Exception:
                raise ValidationError(
                    _('Wrong time entered.'),
                    code='Wrong time entered.',
                )
        if available_till is not None and available_from is not None:
            if available_till <= available_from:
                raise ValidationError(
                    "Available till should be after available from.", code='Available till after available from'
                )

views.py

    if categories == []:
        categories = ['Regular', 'Executive', 'Deluxe', 'King', 'Queen']
    if capacities == []:
        capacities = [1, 2, 3, 4]
    if advance is None:
        advance = 0
    if available_from is None and available_till is None:
        room_list = Room.objects.filter(
        category__in=categories,
        capacity__in=capacities,
        advance__gte=advance
    )
    elif available_from is None:
        room_list = Room.objects.filter(
        category__in=categories,
        available_till__gte=available_till,
        capacity__in=capacities,
        advance__gte=advance
    )
    elif available_till is None:
        room_list = Room.objects.filter(
        category__in=categories,
        available_from__lte=available_from,
        capacity__in=capacities,
        advance__gte=advance
    )
    else:
        room_list = Room.objects.filter(
            category__in=categories,
            available_from__lte=available_from,
            available_till__gte=available_till,
            capacity__in=capacities,
            advance__gte=advance
        )
    return room_list

enter image description here

Link to GitHub repo

I wrote the code but I think there should be a better way.

Upvotes: 3

Views: 236

Answers (2)

JDODER
JDODER

Reputation: 54

To query multiple things at the same time, use the | symbol to separate them

varName = Room.objects.filter(category__in=categories) | Room.objects.filter(capacity__in=capacities) | Room.objects.filter(capacity__in=capacities)...

I recently used this in a search view like this

object_list = Post.objects.filter(title__icontains=query) | Post.objects.filter(content__icontains=query) | Post.objects.filter(category__icontains=query) | Post.objects.filter(date_posted__icontains=query) | Post.objects.filter(author__username__icontains=query)

Upvotes: 1

haduki
haduki

Reputation: 938

A simple solution to your problem is to create complex queries with Q objects. You can find more information on that matter here.

Otherwise you can try multiple filters at once:

varName = Room.objects.filter(category__in=categories) | Room.objects.filter(capacity__in=capacities) | Room.objects.filter(capacity__in=capacities)...

use the | symbol to separate the queries

Upvotes: 0

Related Questions