views:

50

answers:

3

I have a Django form setup using GET method. Each value corresponds to attributes of a Django model. What would be the most elegant way to generate the query? Currently this is what I do in the view:

def search_items(request):
    if 'search_name' in request.GET:
        query_attributes = {}

        query_attributes['color'] = request.GET.get('color', '')
        if not query_attributes['color']: del query_attributes['color']

        query_attributes['shape'] = request.GET.get('shape', '')
        if not query_attributes['shape']: del query_attributes['shape']

        items = Items.objects.filter(**query_attributes)

But I'm pretty sure there's a better way to go about it.

A: 

well, the basic way you are approaching the problem seems sound, but the way you wrote it out looks a little funny. I'd probably do it this way:

def search_items(request):
    if 'search_name' in request.GET:
        query_attributes = {}

        color = request.GET.get('color', '')
        if color:
            query_attributes['color'] = color

        shape = request.GET.get('shape', '')
        if shape:
            query_attributes['shape'] = shape

        items = Items.objects.filter(**query_attributes)
TokenMacGuy
+3  A: 

You could do it with a list comp and and "interested params" set:

def search_items(request):
    if 'search_name' in request.GET:
        interested_params = ('color', 'shape')
        query_attrs = dict([(param, val) for param, val in request.GET.iteritems() 
                            if param in interested_params and val])

        items = Items.objects.filter(**query_attrs)

Just for fun (aka don't actually do this) you could do it in one line:

def search_items(request):
    items = Items.objects.filter(
        **dict([(param, val) for param, val in request.GET.iteritems() 
                if param in ('color', 'shape') and val])
    ) if 'search_name' in request.GET else None 
sdolan
+1 How can you not love list comprehension?
Nimmy Lebby
Just make sure you clean the input.
Andrew Sledge
@Andrew Sledge: What's the attack vector are you suggesting cleaning against?
sdolan
@sdolan: Never trust user input. Django request objects are stored in a dict. Dicts can hold any type of information. So if you're expecting certain strings but get other "unhealthy" strings, you need to clean them up or dispose of them.
Andrew Sledge
A: 

If you want it to be fully dynamic, you can use a little bit of model introspection to find out what fields you can actually query, and filter only using those.

Though, this solution won't allow you to use __lookups in GET parameters, don't know if you need it.

def search_items(request):
    if 'search_name' in request.GET:
        all_fields = Items._meta.get_all_field_names()
        filters = [(k, v) for k, v in request.GET.items() if k in all_fields]

        items = Items.objects.filter(*filters)
Dmitry Shevchenko