views:

3028

answers:

4

Given a class:

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=20)

Is it possible (and if so how) to have a QuerySet that filters based on dynamic arguments, likeso:

 # instead of 
 Person.objects.filter(name__startswith='B')
 # and 
 Person.objects.filter(name__endswith='B')

 # is there some way, given:
 filter_by = '%s__%s' % ('name', 'startswith')
 filter_value = 'B'
 # You can run the equiv. of
 Person.objects.filter(filter_by=filter_value)
 # which throws an exception

Help is much appreciated & thank you in advance.

+2  A: 

A really complex search forms usually indicates that a simpler model is trying to dig it's way out.

How, exactly, do you expect to get the values for the column name and operation? Where do you get the values of 'name' an 'startswith'?

 filter_by = '%s__%s' % ('name', 'startswith')
  1. A "search" form? You're going to -- what? -- pick the name from a list of names? Pick the operation from a list of operations? While open-ended, most people find this confusing and hard-to-use.

    How many columns have such filters? 6? 12? 18?

    • A few? A complex pick-list doesn't make sense. A few fields and a few if-statements make sense.
    • A large number? Your model doesn't sound right. It sounds like the "field" is actually a key to a row in another table, not a column.
  2. Specific filter buttons. Wait... That's the way the Django admin works. Specific filters are turned into buttons. And the same analysis as above applies. A few filters make sense. A large number of filters usually means a kind of first normal form violation.

A lot of similar fields often means there should have been more rows and fewer fields.

S.Lott
Thanks for the reply. The data model given as an example is just that, for easy illustration. I prefer not to imagine anyone putting anything that heinous into a real project. ;) I want to decouple generic relations and factor out some reusable logic.
Brian M. Hunt
Django already *is* generic. Writing more generic stuff on top of Django is a bit too much genericity. My recommendation is to simply implement your application, avoiding over-generalization an already generic framework.
S.Lott
With respect, it's presumptuous to make recommendations without knowing anything about the design. To "simply implement" this application would beget astronomical (>200 apps ^21 foos) functions to meet the requirements. You're reading purpose and intent into the example; you shouldn't. :)
Brian M. Hunt
I meet a lot of people who feel that their problem would be trivial to solve if only things were (a) more generic and (b) worked the way they imagined. That way lies endless frustration because things aren't the way they imagined. I've seen too many failures stem from "fixing the framework".
S.Lott
@BMH: If I don't know anything about the design, then, well, um, perhaps the question is incomplete. If I'm not supposed to presume, how am I supposed to get enough information? If you don't like my presumption, perhaps you could provide facts to show me how wrong I am.
S.Lott
Things work as expected and desired per Daniel's response. My question was about syntax, not design. If I had time to write out the design, I'd have done that. I'm sure your input would be helpful, however it's just not a practical option.
Brian M. Hunt
@BMH: here's the point: Please don't call it presumptuous when the question could be incomplete. You can't complain about answers on SO to begin with, much less complain that they aren't spot on a question that appears unsound.
S.Lott
S.Lott, your answer doesn't even remotely answer this question. If you don't know an answer, please leave the question alone. Don't respond with unsolicited design advice when you have absolutely zero knowledge of the design!
slypete
@slypete: If a change to the design removes the problem, then the problem's solved. Continuing along the path based on a poor design is more expensive and complex than necessary. Solving root-cause problems is better than solving other problems that stem from bad design decisions. I'm sorry you don't like root-cause analysis. But when something's really hard, it usually means you're trying the wrong thing to begin with.
S.Lott
+18  A: 

If, as S.Lott asks, this is the best way to solve your problem - you can use Python's argument expansion to achieve what you're looking for:

kwargs = {'%s__%s' % ('name', 'startswith'): 'A',
          '%s__%s' % ('name', 'endswith'): 'Z' }

Person.objects.filter(**kwargs)
Daniel
Thanks, that's it; seems obvious now. :)
Brian M. Hunt
+2  A: 

A simplified example:

In a Django survey app, I wanted an HTML select list showing registered users. But because we have 5000 registered users, I needed a way to filter that list based on query criteria (such as just people who completed a certain workshop). In order for the survey element to be re-usable, I needed for the person creating the survey question to be able to attach those criteria to that question (don't want to hard-code the query into the app).

The solution I came up with isn't 100% user friendly (requires help from a tech person to create the query) but it does solve the problem. When creating the question, the editor can enter a dictionary into a custom field, e.g.:

{'is_staff':True,'last_name__startswith':'A',}

That string is stored in the database. In the view code, it comes back in as self.question.custom_query . The value of that is a string that looks like a dictionary. We turn it back into a real dictionary with eval() and then stuff it into the queryset with **kwargs:

kwargs = eval(self.question.custom_query)
user_list = User.objects.filter(**kwargs).order_by("last_name")
shacker
I'm wondering what it would take to create a custom ModelField/FormField/WidgetField that implemented the behavior to allow the user to, on the GUI side, basically "build" a query, never seeing the actual text, but using an interface to do so. Sounds like a neat project...
T. Stone
T. Stone - I'd imagine it would be easy to build such a tool in a simplistic way if the models that need querying were simple, but very difficult to do in a thorough way that exposed all possible options, especially if the models were complex.
shacker
A: 

I have a query tool that does this. It is quite a lot of work but it uses dynamic forms, optgroups, and can't handle many to many. I am sure that many to many is possible but i just create functions that i use instead.

Rick