Thursday, October 26, 2017

Re: Passing 3 user inputs as argument in url, for a search form

I managed to get the code functional.  I haven't done extensive testing but it's certainly working right now with your code plus some of my own fixes.  I should read the documentation more carefully in the future.

Thanks again for the super detailed response James.

On Wednesday, October 25, 2017 at 7:50:58 PM UTC-4, James Schneider wrote:


On Wed, Oct 25, 2017 at 11:29 AM, Jack <valac...@gmail.com> wrote:
I am building a search form where the user inputs 3 values on a form, and I search the database for all records that match the 3 inputs.  Right now I am stuck on the user input part.

If you look at my urls.py, you will see that I tried to put 3 keywords arguments in there.  Then I tried to pass the 3 keyword arguments in the url tag with variables from my forms.py.  I think this is where things went wrong but I'm not sure.  I don't know if the .html file can use variables from forms.py

Basically what I'm trying to do is to take the 3 user inputs, and pass them as arguments in the URL.  Right now I'm getting:

NoReverseMatch at /

Reverse for 'search-results' with keyword arguments '{'pt': '', 'nb': '', 'nw': ''}' not found. 1 pattern(s) tried: ['search?(?P<pt>\\w+)&(?P<nb>\\w+)&(?P<nw>\\w+)']


urls.py
url(r'^search?(?P<pt>\w+)&(?P<nb>\w+)&(?P<nw>\w+)', views.IndexSearchResults.as_view(), name='search-results')


This is not the correct usage of the URL dispatcher. It should look something like this:

url(r'^search$', views.IndexSearchResults.as_view(), name='search-results')

The URL dispatcher does not match on GET arguments within the URL, that's left for the request object to parse.


 

views.py
class IndexSearchResults(generic.ListView):
    template_name = 'search-results.html'


The view is where your GET arguments will be processed. Note that if you are submitting the form directly to the URL above, then no validation of the form values will be performed. If you want those values to be validated, you should submit to a FormView to validate the results and redirect to this search page with the GET arguments in tow. I'd imagine that both strategies are common, since sending bad values to search against usually just results in bad searches. The ORM takes care of most of the security concerns in this case.

You will want to override get_queryset() to add in your GET arguments for filtering after retrieving them from the request object. The GET arguments are automatically captured in to a QueryDict attached to the request:


So, in your IndexSearchResults view:

def get_queryset(self):
    queryset = super().get_queryset()

    # I can do something fancy with lists of tuples to cut down on the code, but this is more readable
    pt = self.request.GET.get('pt')
    nb = self.request.GET.get('nb')
    nw = self.request.GET.get('nw')
    
    if pt:
        queryset = queryset.filter(property_type=pt)
    if nb:
        queryset = queryset.filter(number_of_bedrooms=nb)
    if nw:
        queryset = queryset.filter(number_of_washrooms=nw)

    return queryset


This should return a queryset to the view that will filter down the list of objects matching the GET arguments.

 

models.py
Class BuyerListing(models.Model):
     BEDS_OPTION = (
        (BEDS_0, '0 [Studio/Bachelor]'),
        (BEDS_1, '1'),
        (BEDS_1_1, '1+1'),
        (BEDS_2, '2'),
        (BEDS_2_1, '2+1'),
        (BEDS_3, '3'),
        (BEDS_3_1, '3+1'),
        (BEDS_4, '4'),
        (BEDS_5, '5'),
        (BEDS_5_1, '5+'),    
    )

     WASH_OPTION = (
        (WASH_1, '1'),  
        (WASH_2, '2'),  
        (WASH_3, '3'),  
        (WASH_4, '4'),  
        (WASH_5, '5'),  
        (WASH_5_1, '5+'),  
    )

    PROPERTY_TYPE = (
        (CONDO_APARTMENT, 'Condo Apartment'),
        (DETACHED_HOUSE, 'Detached House'),
        (SEMI_DETACHED, 'Semi-detached'),
        (TOWNHOUSE, 'Townhouse'),
    )

    property_type = models.CharField(max_length=50)
    number_of_bedrooms = models.CharField(max_length=50, default=BEDS_0)
    number_of_washrooms = models.CharField(max_length=50, default=WASH_1)

This might be correct, but generally you would use an IntegerField or FloatField here rather than CharFields for the number of beds/baths. I'm not aware of any exceptions where a number would not be used, but you may know of some. Searching will be slightly faster using real number fields, and you'll be able to filter using queries for 'more than 2 bedrooms' etc., which you can't do with CharFields. I'd imagine that is a desired feature. 

 -James

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/e1bdd963-68ed-4d4a-96e2-be68912d01d6%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

No comments:

Post a Comment