tags:

views:

38

answers:

3

This is a portion of a template tag code, where qs is a queryset.

def foo(qs):
    ...
    context['key'] = qs.order_by('an_invalid_field_coming_from_user')

How can I check if the queryset will be ordered by a valid field before the code execution goes out of the scope of the template tag, other than forcing an evaluation?

The code as is does not raise an error since the queryset is not evaluated. qs.exists() is not the answer either as it will execute the query without being ordered.

EDIT: Please note that the query may be more complex than my pre-edit simple example Foo.objects.all(), eg, it may feature an extra() method which results in joins.

+1  A: 

Using order_by() with a field that doesn't exist will throw a django.core.exceptions.FieldError. Simply catch that and report the error to the user.

Ignacio Vazquez-Abrams
I was trying to avoid evaluating the query, so that a valid one wouldn't be executed twice - one now, one later on where it is really required. But maybe I am being too picky.
shanyu
+1  A: 

I am not sure that's a good idea as there might be security considerations here. I think this will work but I don't have a copy of Django around to test for sure that the moment. I am pretty sure there is an exception thrown when you do that but if you don't want to catch that or check preemptively this should work:

if hasattr(Foo, "fieldname") and isinstance(getattr(Foo, "fieldname"), models.Field):
    print "Safe field"
else:
    print "Invalid field"
hbar
No exception is thrown since adding an order_by doesn't force an evaluation. Regarding your solution, what if the queryset has an extra method and features additional fields other than those of the model?
shanyu
Ooops I wrote that wrong. The clause after the and should make sure that the attribute is a Field of some type (since all types of fields inherit models.Field). I just fixed that. Hopefully that makes more sense.
hbar
+2  A: 

If you're really against catching an exception (which you shouldn't be), you can do this:

if context['key'] in [field.name for field in Foo._meta.fields]:
    qs = Foo.objects.all().order_by(context['key'])
Zach
I am not against catching an exception, I am trying to avoid pre-evaluating the queryset in the templatetag where it is of no use.
shanyu
Yes, that will work if the queryset is based on a single model. If the query is a complex one featuring joins, it won't. It seems best to execute the query and catch the error.
shanyu
Aren't you creating a list from a list comprehension every time this `if` statement runs? That's inefficient. It's also inefficient to run a membership search (`in`) against a list. It's much more efficient to run a membership search against a set or keys in a dictionary.
gotgenes