QuerySets in Dictionaries

Steven D'Aprano steve at REMOVE-THIS-cybersource.com.au
Sat Nov 14 23:55:51 EST 2009


On Fri, 13 Nov 2009 14:10:10 -0800, scoopseven wrote:

> I actually had a queryset that was dynamically generated, so I ended up
> having to use the eval function, like this...
> 
> d = {}
> for thing in things:
>         query_name = 'thing_' + str(thing.id) 
>         query_string = 'Thing.objects.filter(type=' + str(thing.id) +  
> ').order_by(\'-date\')[:3]'
>         executable_string = query_name + ' = Thing.objects.filter
> (type=' + str(thing.id) + ').order_by(\'-date\')[:3]'
>         exec(executable_string)
>         d[query_name] = eval(query_string)


What an unmaintainable mess.

If I've understood it, you can make it less crap by (1) getting rid of 
the unnecessary escaped quotes, (2) avoiding generating the same strings 
multiple times, and (3) avoiding string concatenation.

d = {}
for thing in things:
    expr = "Thing.objects.filter(type=%s).order_by('-date')[:3]"
    expr = rhs % thing.id
    name = "thing_%s" % thing.id
    exec("%s = %s" % (name, expr))
    d[name] = eval(expr)


What else can we do to fix it? Firstly, why are you creating local 
variables "thing_XXX" (where XXX is the thing ID) *and* dictionary keys 
of exactly the same name? I'm sure you don't need the local variables. 
(If you think you do, you almost certainly don't.) That gets rid of the 
exec.

Next, let's get rid of the eval:

d = {}
for thing in things:
    x = thing.id
    name = "thing_%s" % x
    d[name] = Thing.objects.filter(type=x).order_by('-date')[:3]


About half the size, ten times the speed, and 1000 times the readability.

Unless I've missed something, you don't need either exec or eval.



-- 
Steven



More information about the Python-list mailing list