2

I am trying to iterate over form results and I can't help but think that I am re-inventing the wheel here.

filterlist = []

if request.POST:
        form = FilterForm(request.POST)
        if form.is_valid():
            for key, value in form.cleaned_data.iteritems():
                filterlist.append(key)
                filterlist.append(value)

This works, but seems very awkward and creates lots of other problems. For example the values come back with u' so I have to use value.encode("utf8") but then if a value is None it throws in error. So now I have to check if it is None, if not then encode. There has to be a better way.

EDIT: What I am trying to do.

I am trying to filter what is shown on a page. The problem I am running into is that if a value is empty (the user don't fill the box because they only want to filter against one object) then I get no results. For example a user wants to search for all books by the author name "Smith" but doesn't want to search against a genre.

results = Books.objects.filter(author=author, genre=genre) 

The user would get no results because this is an AND search. But, if a user put in "Smith" for the author and "mystery" for the genre then it works exactly like I want it to, only giving results where both are true.

So, I am trying to eliminate the empty stuff by iterating over the form results. Like I said I am probably re-inventing the wheel here.

VT_Drew
  • 374
  • 5
  • 10
  • why do you have to convert it back to "normal" string? – GwynBleidD Feb 02 '15 at 18:43
  • A better way to do what? You haven't explained why you're doing this, or how you're using the output in a way that it matters that it's Unicode. – Daniel Roseman Feb 02 '15 at 18:43
  • what exactly are you trying to do ? Looks like your `filterlist` is a list of keys followed by values. – karthikr Feb 02 '15 at 18:47
  • I edited my post to give more context. – VT_Drew Feb 02 '15 at 19:33
  • Have you tried using Q from django.db.models along with setting the required field in the modelForm as False to make it work `class myForm(forms.ModelForm): myField = forms.CharField(required=False) class Meta: model = myModel` – cmidi Feb 02 '15 at 20:01

3 Answers3

4

In Python 3 use:

for key, value in form.cleaned_data.items():
ims
  • 135
  • 3
  • 10
3

If the field names are the same in the model and the form, try this:

filter = {}

if request.method == 'POST':
    form = FilterForm(request.POST)
    if form.is_valid():
        for key, value in form.cleaned_data.iteritems():
            if value:
                filter[key] = value
        results = Books.objects.filter(**filter)

Python is one of the few languages having named parameters. You can assemble a dict with the non-empty form fields and pass it to the filter method using the kwargs unpacking operator **.

For example:

kwargs = {"author": "Freud"}
results = Books.objects.filter(**kwargs)

Is the same as:

results = Books.objects.filter(author="Freud")
Paulo Scardine
  • 73,447
  • 11
  • 124
  • 153
  • Perfect! This is exactly what I was looking for. Before I saw your answer I also found this [blog post](http://www.nomadjourney.com/2009/04/dynamic-django-queries-with-kwargs/), which talks about using key word arguments in the filter. This is what makes me love python. – VT_Drew Feb 03 '15 at 17:04
0

I think the problem is that by default the Model form is not valid if a form field does not have a value entered by the user, if you don`t require the field every time from the user you need to set the required field to false in the ModelForm class in forms.py as shown in the code below. Remember that the field is set false only in the model form not in the model itself

class myForm(forms.ModelForm):
    myfield_id = forms.CharField(required=False)
    myfield_foo = forms.CharField(required=False)
    myfield_bar = forms.CharField(required=False)
    myfield_name = forms.CharField(required=False)
    class Meta:
        model = myModel
        exclude = ('myfield_ex','myfield_file')
        fields  = ['myfield_id','myfield_foo','myfield_bar','myfield_name',]

After you have the form entered by the user what you need is use the Q object which can be used to create complex queries as described in the manula page here https://docs.djangoproject.com/en/1.7/topics/db/queries/#complex-lookups-with-q

A simple example code would look like

if form.is_valid():
        qgroup = []
        for key,value in form.cleaned_data.iteritems():
            if value:
                q_name = Q(**{"%s"%format(filterKey[key]) : value})
                qgroup.append(q_name)
        q = None
        # can use the reduce as shown here qgroup = reduce(operator.or_, (Q(**{"{0}".format(filterKey[key]): value}) for (key,value) in form.cleaned_data.iteritems()))
        for key,value in form.cleaned_data.iteritems():
            if value:
                q_name = Q(**{"%s"%format(filterKey[key]) : value})
                qgroup.append(q_name)
        for x in qgroup:
                q &= x ### Or use the OR operator or 
        if q:
            resultL = myModel.objects.filter(q).select_related()

The filterKey can look something on the lines of

 filterKey = {'myfield_id'          : "myfield_id",
             'myfield_foo'          : "myfield_foo__icontains",
             'myfield_bar'           : "myfield_bar__relative_field__icontains",
             }
cmidi
  • 1,880
  • 3
  • 20
  • 35
  • there is a good answer on using Q in this question by using reduce by @Ignacio Vazquez-Abrams here http://stackoverflow.com/questions/852414/how-to-dynamically-compose-an-or-query-filter-in-django – cmidi Feb 02 '15 at 20:42
  • cmidi, thanks for your answer. I am sure this would work, and if I was doing more complex filters I probably would be using Q objects. However, I went with the solution provided by Paulo Scardine. His solution was simpler and fit my needs. – VT_Drew Feb 03 '15 at 17:01