Constructing a dynamic query using user input in Django

Question:

I have a big model which stores 10 text based values, and 10 numerical values. Something like this:

class Dataset(models.Model):
    text_field_1 = models.CharField(max_length=255)
    .
    .
    .
    text_field_10 = models.CharField(max_length=255)

    number_field_1 = models.IntegerField()
    .
    .
    .
    number_field_10 = models.IntegerField()

Now, what I want to do is give users to a way to filter, and order datasets using these fields, by passing in an object to a view. Hopefully, the example below shows what I want to do:

obj = {
    "filters" : [
        "text_field_1='vaccines'",
        "number_field_5__gte=1000",
    ],
    "order_by" : "text_field_3, -number_field_7",
    "excludes" : [],
}

generate_query(obj) # Dataset.objects.filter(text_field_1='vaccines', number_field_5__gte=1000).order_by('text_field_3', '-number_field_7')

So by calling the generate_query(obj), we get the queryset in the comment. Now, due to the nature of this model, its impossible for me to do this manually, by accounting for every possible combination of filters, orders, and excludes.

What is the best, and safest way to implement this? The only thing that comes to my mind is creating a big string, and then using exec or eval to execute the string.

Asked By: darkhorse

||

Answers:

Use dictionaries for filters and excludes and a list for order_by, and you can use argument unpacking with * or **.

obj = {
    "filters" : {
        "text_field_1": "vaccines",
        "number_field_5__gte": "1000",
    },
    "order_by" : ["text_field_3", "-number_field_7"],
    "excludes" : {},
}

Dataset.objects.filter(**obj['filters']).exclude(**obj['excludes']).order_by(*obj['order_by'])

It should be pretty safe, as long as you don’t permit user to construct their own __ arguments. For example the filter event__owner__password='hunter2' could be used to indirectly query fields in related models.

Answered By: Håken Lid

There is quite a good package dedicated to filering based on user input:

https://django-filter.readthedocs.io/en/stable/

Answered By: dyve
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.