4

I have a form which looks like this:

class AddressSearchForm(forms.Form):
    """
        A form that allows a user to enter an address to be geocoded
    """
    address = forms.CharField()

I'm not storing this value, however, I am geocoding the address and checking to make sure it is valid:

def clean_address(self):
    address = self.cleaned_data["address"]
    return geocode_address(address, True)

The geocode function looks like this:

def geocode_address(address, return_text = False):
    """ returns GeoDjango Point object for given address
        if return_text is true, it'll return a dictionary: {text, coord}
        otherwise it returns {coord}
    """ 
    g = geocoders.Google()
    try:
        #TODO: not really replace, geocode should use unicode strings
        address = address.encode('ascii', 'replace')            
        text, (lat,lon) = g.geocode(address)
        point = Point(lon,lat)
   except (GQueryError):
       raise forms.ValidationError('Please enter a valid address')
    except (GeocoderResultError, GBadKeyError, GTooManyQueriesError):
    raise forms.ValidationError('There was an error geocoding your address. Please try again')
    except:
        raise forms.ValidationError('An unknown error occured. Please try again')

    if return_text:
         address = {'text':text, 'coord':point}
    else:
        address = {'coord':point}

    return address

What I now need to do is create a view that will query a model using the address data to filter the results. I'm having trouble figuring out how to do this. I'd like to use CBV's if possible. I can use FormView to display the form, and ListView to display the query results, but how do I pass the form data between the two?

Thanks in advance.

UPDATE: I know how to query my model to filter the results. I just don't know how to properly combine using a Form and Class Based Views so that I can access the cleaned_data for my filter. e.g:

Process should be:

1) Display form on get 2) Submit form and validate (geocode address) on post 3) Run query and display results

address = form.cleaned_data['address']
point = address['coord']
qs = model.objects.filter(point__distance_lte=(point, distance)
Douglas Meehan
  • 167
  • 1
  • 9

2 Answers2

4

Ok, here's a generic version of what ended up working based on psjinx direction:

from django.views.generic.base import TemplateResponseMixin, View
from django.views.generic.edit import FormMixin
from django.views.generic.list import MultipleObjectMixin

class SearchView(FormMixin, MultipleObjectMixin, TemplateResponseMixin, View):
    """
     A View which takes a queryset and filters it via a validated form submission
    """
    queryset = {{ initial queryset }} # you can use a model here too eg model=foo
    form_class = {{ form }}

    def get(self, request, *args, **kwargs):
        form_class = self.get_form_class()
        form = self.get_form(form_class)
        return self.render_to_response(self.get_context_data(form=form))

    def post(self, request, *args, **kwargs):
        form_class = self.get_form_class()
        form = self.get_form(form_class)
        if form.is_valid():
            return self.form_valid(form)
        else:
            return self.form_invalid(form)    

    def form_valid(self, form):
        queryset = self.get_queryset()
        search_param = form.cleaned_data['{{ form field }}']
        object_list = queryset.filter({{ filter operation }}=search_param)
        context = self.get_context_data(object_list=object_list, form=form, search_param=search_param)
        return self.render_to_response(context) 
Douglas Meehan
  • 167
  • 1
  • 9
3

This is similar to a question asked here or I will say a combination of two questions.

  1. Django: Search form in Class Based ListView
  2. Search multiple fields of django model without 3rd party app
  3. django simple approach to multi-field search (if your models similar to one mentioned in this question)

Please have a look at above questions and their answers and if you still have any question then reply in comment to this answer.


Update 1

from django.views.generic.base import TemplateResponseMixin, View
from django.views.generic.edit import FormMixin
from django.views.generic.list import MultipleObjectMixin

class SearchView(FormMixin, MultipleObjectMixin, TemplateResponseMixin, View):
    model = SomeModel
    form_class = AddressSearchForm
    template = "search.html"

    def get_queryset():
        ## Override it here
        pass

    def post():
        ## If your form is invalid then request won't reach here
        ## because FormMixin is in left most position
        ## do something
        ## call self.render_to_response()

    def form_valid():
        ## Your form is valid do something
        ## if form is invalid then next method will be called
        pass

    def form_invalid(self):
        ## form is not valid
        ## render to some template

Useful Links:

  1. https://github.com/django/django/blob/1.4.3/django/views/generic/base.py
  2. https://github.com/django/django/blob/1.4.3/django/views/generic/edit.py
  3. https://github.com/django/django/blob/1.4.3/django/views/generic/list.py

Related Question:

  1. Django - Mixing ListView and CreateView
Community
  • 1
  • 1
pankaj28843
  • 2,458
  • 17
  • 34
  • It is similar to question 1. However, the answer to that question was to use GET in a hard coded form. I want to use a Form class with POST so that I can do validation on the field. That answer also shows a solution using FBVs, but I would like to use CBVs if possible. I found some helpful information about using mixins to do something similar here: https://docs.djangoproject.com/en/dev/topics/class-based-views/mixins/, but nothing quite answers my use case. – Douglas Meehan Jan 21 '13 at 19:47
  • Please see my updated answer. I have added code skeleton well for your view. – pankaj28843 Jan 21 '13 at 20:24
  • Thank you, this is really helpful. I will give it a shot and see if I can get it working. – Douglas Meehan Jan 21 '13 at 21:06
  • Okay. Let me know, if you face any problem. – pankaj28843 Jan 21 '13 at 21:11