Search code examples
pythondjangoorm

Why Django ORM filter lists all the users?


I like to make a filter where user can search based on last_name or a regio(region). I made the query but it filters well only if I using the last_name filter, but the regio gives all the users in the db.

I'm used before Sqlite3 and raw queries but now I like to learn ORM with Postgres so I'm not experienced in ORM yet. What am I doing wrong?

models.py

class Profil(models.Model):

    def __str__(self):
        return str(self.user)

    def generate_uuid():
        return uuid.uuid4().hex

    user = models.OneToOneField(User, on_delete=models.CASCADE)
    datetime = models.DateTimeField(auto_now_add=True, auto_now=False)
    coach_uuid = models.CharField(default=generate_uuid, editable=True, max_length=40)
    regio = models.ForeignKey('Regiok', blank=True, null=True, on_delete=models.CASCADE)


class Regiok(models.Model):
    regio = models.CharField(max_length=80)

    def __str__(self):
        return str(self.regio)

views.py

def coachok(request):

    coach = Profil.objects.all()

    nev = request.GET.get('nev')
    regio = request.GET.get('regio')

    if nev != '' and nev is not None or regio != '' and regio is not None:
        coach = Profil.objects.filter(Q(user__last_name__icontains=nev) | Q(regio_id__regio=regio)).select_related('user', 'regio')

    
    context = {
        'coach':coach,
    }

    return render(request, 'coachregiszter/coachok.html', context)

html

<div class="container">
<form method="GET">
    <div class="bg-light p-2">
        <div class="row my-3">
            <h4>Szűrők</h4>
            <div class="col-md-3">
                <h6>Név</h6>
                <input class="form-control" type="search" name="nev"></input>
            </div>
            <div class="col-md-3">
                <h6>Vármegye</h6>
                <select class="form-select" name="regio">
                    <option value="" selected>Bármelyik</option>
                    <option value="Dél-Dunántúl">Dél-Dunántúl</option>
                    <option value="Közép-Dunántúl">Közép-Dunántúl</option>
                </select>
            </div>
        </div>
        <button type="submit" class="btn btn-outline-dark">Keresés</button>
        <a href="/coachok"><button type="submit" class="btn btn-outline-info">Szűrők törlése</button></a>
    </div>
</form>

Solution

  • Make the queries optional:

    from operator import or_
    from functools import reduce
    # ...
    
    nev = request.GET.get('nev')
    regio = request.GET.get('regio')
    
    coach = Profil.objects.all()    
    queries = []
    if nev:
        queries.append(Q(user__last_name__icontains=nev))
    if regio:
        queries.append(Q(regio_id__regio=regio))
    if queries: 
        coach = coach.filter(reduce(or_, queries))
    
    coach = coach.select_related('user', 'regio')