Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python

BRIAN WEBER
BRIAN WEBER
21,570 Points

How to add search bar properly in view for "Filtering and Searching Mineral Catalog" project

Here is the task description:

Allow text search.

Add a search box and button. The search box and button should be implemented as a form. When the search button is clicked, the site will search for minerals whose name contains the search text. The names of the minerals that match the search will be displayed in the list view. Add the search form to the layout template so that searching can be performed from any page in the site.

I am unsure how I would implement the search form properly in each view and the layout.html template without repeating myself.

My questions are as follows:

Should I create a separate view to handle the search query?

If so, how would I do this? Use the action attribute on the form to send the data to the search view and then send the results back to the mineral_list view?

I am trying to avoid breaking the DRY principle.

I am creating a search form in forms.py:

from django import forms


class MineralSearchForm(forms.Form):
    search = forms.CharField(
        label='',
        max_length=100,
        widget=forms.TextInput(
            attrs={'placeholder': 'Search minerals by name...'}
        )
    )

Here is my view:

def mineral_list(request):
    """Returns a list of all minerals."""
    # minerals = Mineral.objects.all()
    minerals = Mineral.objects.filter(name__istartswith=letter_start)
    random_mineral = random.choice(minerals)

    form = forms.MineralSearchForm()
    if request.method == 'POST':
        form = forms.MineralSearchForm(request.POST)
        if form.is_valid():
            query = form.cleaned_data['search']
            minerals = Mineral.objects.filter(name__icontains=query)
            return render(
                request,
                'minerals/mineral_list.html',
                {
                    'minerals': minerals,
                    'random_mineral': random_mineral,
                    'letters': letters,
                    'form': form
                }
            )
    return render(
        request,
        'minerals/mineral_list.html',
        {
            'minerals': minerals,
            'random_mineral': random_mineral,
            'letters': letters,
            'active_letter': letter_start,
            'form': form
        }
    )

Any help and guidance would be greatly appreciated!

Thank you,

Brian

2 Answers

Kenneth Love
STAFF
Kenneth Love
Treehouse Guest Teacher

I'd create a view to handle the search query and returning the results.

As for the display of the form, custom template tag?

BRIAN WEBER
BRIAN WEBER
21,570 Points

Thanks Kenneth! I was able to add the form to the layout template using a simple_tag. Currently working on the separate view to handle the search query and returning the results. Will post once I am finished!

UPDATE:

Here is the code I have for the search view so far. For some reason, I am not able to capture the query string q via the request object. Also, my search.html template is not updating. Do you seen anything wrong with my code Kenneth Love ?

<!-- Search form -->
<form id="searchform" method="get" action="{% url 'minerals:search' %}" accept-charset="utf-8">
    {% search_form %}
    <input type="submit" class="button" value="Search">
</form>
def mineral_search(request):
    """Returns a list of minerals that match the search query."""
    result = []
    query = request.GET.get('q', '')
    print(query)

    if query:
        result = Mineral.objects.filter(name__icontains=query)
    return render(
        request,
        'minerals/search.html',
        {'query': query, 'result': result}
    )
url(r'results', views.mineral_search, name='search')
class MineralSearchForm(forms.Form):
    q = forms.CharField(
        label='',
        max_length=100,
        widget=forms.TextInput(
            attrs={'placeholder': 'Search minerals by name...'}
        )
    )
BRIAN WEBER
BRIAN WEBER
21,570 Points

I fixed the problem by reordering my urls with the search view first:

urlpatterns = [
    url(r'^results$', views.mineral_search, name='search'),
    url(r'^$', views.mineral_list, name='list'),
    url(r'(?P<letter>[a-zA-Z]+)$', views.mineral_list_letter_filter,
        name='letter_filter'),
    url(r'(?P<pk>\d+)$', views.mineral_detail, name='detail')
]