Jerry Bi
Jerry Bi

Reputation: 341

Making a proper search function in Django

I'm trying to make a search function that allows me to enter the value of an object in an instance so I can display that instance and several of it's objects on the same page as the search page. Here's what I have so far:

#models.py

class Student(models.Model):
    # STEP 1 BASIC INFO
    student_id = models.CharField(max_length=128, unique=True)
    first_name = models.CharField(max_length=128)
    last_name = models.CharField(max_length=128)
    ssn = USSocialSecurityNumberField(null=False)
    gender = models.CharField(max_length=128, choices=GENDER_CHOICES)
    dob = models.DateField(auto_now=False, auto_now_add=False, db_column="date of birth")
    contact_number = models.CharField(max_length=128)
    address = models.CharField(max_length=128)
    city = models.CharField(max_length=128)
    state = USStateField(choices=STATE_CHOICES, default='NJ')
    zipcode = USZipCodeField(blank=True)
    country = CountryField(default='US', blank=True)
    home_phone = models.CharField(max_length=128)
    cell_phone = models.CharField(max_length=128)
    email = models.EmailField(max_length=254, validators=[validate_email])

    def __str__(self):
        return self.first_name + self.last_name

#views.py

def search_Student(request):
    context_dict = {}
    if request.method == 'POST':
        query = request.POST['last_name_search']
        results = Student.objects.filter(last_name=query)
        if query:
            context_dict['results'] = results
        else:
            context_dict['no_results'] = query
    return render(request, "students/search_student.html", context_dict)

#search_student.html

{% block main_content %}
<form method="post" action="/students/search_student/">
    {% csrf_token %}
    <label for="last_name_search">Last Name:</label>
    <input type="text" name="last_name_search" id="last_name_search">
    <input type="submit" name="submit">
</form>
<div id="result_panel">
    {% if no_results %}
        No results returned for <q>{{ no_results }}</q>
    {% else %}
        {% for result in results %}
            {{ result.last_name }}
        {% endfor %}
    {% endif %}
</div>
{% endblock %}

#urls.py

urlpatterns = [
    url(r'^$', students_views.Students, name='students'),
    url(r'^add_student/$', students_views.add_Student, name='add_student'),
    url(r'^id=(?P<identify>[\w]+)/add_studentcourse/$', students_views.add_StudentCourse, name='add_studentcourse'),
    url(r'^id=(?P<identify>[\w]+)/add_studentemployment/$', students_views.add_StudentEmployment, name='add_studentemployment'),
    url(r'test/$', students_views.test, name='test'),
    #URL for the search page.
    url(r'^search_student/$', students_views.search_Student, name='search_student'),
    url(r'^current_student/$', students_views.current_Student, name='current_student'),
    url(r'^all_my_student/$', students_views.all_My_Student, name='all_my_student'),
    url(r'^public_student/$', students_views.public_Student, name='public_student'),
    url(r'^sales_reports/$', students_views.sales_Reports, name='sales_reports'),
    url(r'^switch_counselor/$', students_views.switch_Counselor, name='switch_counselor'),
    url(r'^source_admin/$', students_views.source_Admin, name='source_admin'),
    url(r'^super_tool/$', students_views.super_Tool, name='super_tool'),
    url(r'^help_and_settings/$', students_views.help_And_Settings, name='help_and_settings'),
]

Basically, if I type in a last name in the input, I want it to be able to grab all model instances that have a last name equal to that, and to be able to put any information I want about all the matching instances, like the name, gender, and address on the exact same page as the search. The POST method may be confusing, but someone insists that I do this (It didn't work when I changed the method to GET either). Could someone please point out any errors or missing pieces in my code? Thanks.

Edit: Added the urls.py.

Upvotes: 0

Views: 1185

Answers (3)

user1600649
user1600649

Reputation:

How to debug this

  1. Remove the action from the tag. The browser will automatically post to the same URL. This rules out any issues with redirects, mappings or upstream urlconfs.
  2. Use View Source in the browser to make sure nothing is returned, as opposed to the div being hidden by CSS or JS.
  3. Make sure results are found:

views.py

if query:
    results = Student.objects.filter(last_name=query)
    if results.count():
        context_dict['results'] = results
    else:
        context_dict['no_results'] = query

template

<div id="result_panel">
    {% if no_results %}
        No results returned for <q>{{ no_results }}</q>
    {% else %}
        {% for result in results %}
            {{ result.last_name }}
       {% endfor %}
    {% endif %}
</div>

Note that the above cannot ever be empty. It should always show something. If it does not, it is a display error and the data is there. See point 2.

If it shows the "no results" part, then try to replicate the query in the django shell:

python manage.py shell
>>> from yourapp.models import Student
>>> Student.objects.filter(last_name='what you typed')
>>> Student.objects.filter(last_name__iexact='what you typed')
>>> Student.objects.filter(last_name__icontains='what you typed')
>>> Student.objects.count()

If none of the first three returns results, then you made a typo or number 4 will show you have no students.

Upvotes: 1

Risadinha
Risadinha

Reputation: 16666

Of course you can use a view method and try to find the error in your custom search and template code.

OR you can do it in a more Django way, relying on pre-built features of Django:

Use Django's ListView to query on and display the Student Model objects. You get paging, error handling, context setup for free with this. See other StackOverflow questions like https://stackoverflow.com/a/33350839/621690 for example code.

Your code that filters the Students would go into get_queryset - or you can use django-filter.

For nice usability, you can add Select2 to your form input which allows autocomplete/lookahead. See django-select2

Upvotes: 0

Christian Brintnall
Christian Brintnall

Reputation: 73

SO won't let me comment quite yet, so here to say that you can indeed (and are) supposed to treat dicts like a list in django templating seen here.

Can you please post your error? I'm not sure I'm seeing whats wrong. Your method adapts over to my code just fine, so I'm a bit lost.

Editing for formatting:

You should maybe make a compile time dictionary, IE:

context_dict = {
    "results": results
}
return render(request, "students/search_student.html", context_dict)

Make sure to return in the if, so the scope is in the proper place, and if your if finds nothing return a variable saying so.

If you're bent on sticking to your answer, trying referencing it in the template as so..

{% for result in results.results %}

Your dictionary has a results entry, that points to the variable results. I believe you are iterating through the dictionary in your current example, as opposed to all the queries.

Upvotes: 0

Related Questions