Manu
Manu

Reputation: 4137

Refactoring Django query

I wrote some instructions in order to extract data from my database. I have two values; a city name and a keyword, which are attributes of Address and Museum:

class Museum(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=200)
    address = models.ForeignKey(Address)
    description = models.CharField(max_length=200)

class Address(models.Model):
    id = models.AutoField(primary_key=True)
    streetAddress = models.CharField(max_length=200)
    city = models.CharField(max_length=200)

Now I am receiving two optional parameters: a city and and a keyword. I want to filter out museums according to such city (exact match) AND such keyword (partial match in name OR description)

This is what I ended up writing:

if city is not None and keyword is None:
    city_data = Address.objects.all().filter(city=city)
    museum_list = Museum.objects.all().filter(address__in=city_data)

elif city is None and keyword is not None:  
    museum_list = Museum.objects.all().filter(
        Q(name__contains=keyword) | Q(description__contains=keyword)
    )

elif city is not None and keyword is not None:      
    city_data = Address.objects.all().filter(city=city)
    museum_list = Museum.objects.all().filter(
        Q(address__in=city_data) & (
            Q(name__contains = keyword) | Q(description__contains=keyword)
        )
    )
else:
    museum_list = Museum.objects.all()  

I don't like this code, because I am accounting for all possible combinations. How can I use Django filtering to improve such code to something like:

results = Museum.objects.all()
if city not null
   results = results.filterByAddress_City
if keyword not null
   results = results.filterByKeywordLikeNameOrLikeDescription

Thanks.

Upvotes: 0

Views: 130

Answers (1)

Daniel Roseman
Daniel Roseman

Reputation: 599956

Queries are composable, so you can pretty much do exactly what you state in your pseudocode.

results = Museum.objects.all()
if city:
   results = results.filter(address__city=city)
if keyword:
   results = results.filter(Q(name__contains = keyword) | Q(description__contains = keyword))

Upvotes: 2

Related Questions