Reputation: 8722
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.
Upvotes: 1
Views: 1356
Reputation: 6013
There is quite a good package dedicated to filering based on user input:
https://django-filter.readthedocs.io/en/stable/
Upvotes: 0
Reputation: 23064
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.
Upvotes: 7