1

I have a small problem in creating the correct formula with {if} in my html file. I would like to have a view for all table which it was not available if the user doesn't search anything. But in my situation it is always available, and I can not hide it. I will be grateful for any help.

My html file

{% extends 'base.html' %}
{% load widget_tweaks %}

{% block title %}
    <hr class="featurette-divider">
<h1><center>Wyszukiwarka firm</center></h1>
    <hr class="featurette-divider">
{% endblock %}

{% block content %}
  <form method="get">
    <div class="well">
      <h4 style="margin-top: 1"><center>Wpisz nazwę firmy</center></h4>
      <div class="row " >
        <div class="form-group form-group-lg  " >
          <div class="col-sm-12 " >
          {{ filter.form.label_tag }}
          {% render_field filter.form.name class="form-control" %}
          </div>
        </div>
        <div class="form-group col-sm-8 col-md-6">
          {{ filter.form.groups.label_tag }}
        </div>
      </div>
      <button type="submit" class="btn btn-primary btn-block">
        <span class="glyphicon glyphicon-search"></span> Search
      </button>
    </div>
  </form>

  <table class="table table-bordered">
    <thead>
      <tr>
        <th>Nazwa</th>
        <th>Rating</th>
        <th>Ilość opinii</th>
        <th>Dodatkowe informacje</th>
        <th>Miasto</th>
      </tr>
    </thead>
    <tbody>
      {% for wine in filter.qs %}
        <tr>
          <td><h4><a href="{% url 'reviews:wine_detail' wine.id %}">
          {{ wine.name }}
          </a></h4></td>
          <td>{{ wine.average_rating | floatformat }} average rating</td>
          <td>{{ wine.review_set.count }} reviews</td>
          <td>{{ user.date_joined }}</td>
          <td>
            {% for group in user.groups.all %}
              {{ group }}
            {% empty %}
              <em class="text-muted">Brak inf. o miescie</em>
            {% endfor %}
          </td>
        </tr>
      {% empty %}
        <tr>
          <td colspan="5">No data</td>
        </tr>
      {% endfor %}
    </tbody>
  </table>


{% endblock %}

My views.py

def review_list(request):
    latest_review_list = Review.objects.order_by('-pub_date')[:9]
    context = {'latest_review_list':latest_review_list}
    return render(request, 'reviews/review_list.html', context)


def review_detail(request, review_id):
    review = get_object_or_404(Review, pk=review_id)
    return render(request, 'reviews/review_detail.html', {'review': review})


def wine_list(request):
    wine_list = Wine.objects.order_by('-name')
    context = {'wine_list':wine_list}
    return render(request, 'reviews/wine_list.html', context)


def wine_detail(request, wine_id):
    wine = get_object_or_404(Wine, pk=wine_id)
    form = ReviewForm()
    return render(request, 'reviews/wine_detail.html', {'wine': wine, 'form': form})

@login_required
def add_review(request, wine_id):
    wine = get_object_or_404(Wine, pk=wine_id)
    form = ReviewForm(request.POST)
    if form.is_valid():
        rating = form.cleaned_data['rating']
        comment = form.cleaned_data['comment']
        user_name = request.user.username
        review = Review()
        review.wine = wine
        review.user_name = user_name
        review.rating = rating
        review.comment = comment
        review.pub_date = datetime.datetime.now()
        review.save()
        # Always return an HttpResponseRedirect after successfully dealing
        # with POST data. This prevents data from being posted twice if a
        # user hits the Back button.
        return HttpResponseRedirect(reverse('reviews:wine_detail', args=(wine.id,)))

    return render(request, 'reviews/wine_detail.html', {'wine': wine, 'form': form})

def user_review_list(request, username=None):
    if not username:
        username = request.user.username
    latest_review_list = Review.objects.filter(user_name=username).order_by('-pub_date')
    context = {'latest_review_list':latest_review_list, 'username':username}
    return render(request, 'reviews/user_review_list.html', context)

def search(request):
    review_list = Review.objects.all()
    review_filter = ReviewFilter(request.GET, queryset=review_list)
    return render(request, 'reviews/my_search.html', {'filter': review_filter})

def search_wine(request):
    wine_list = Wine.objects.all()
    wine_filter = WineFilter(request.GET, queryset=wine_list)
    return render(request, 'reviews/my_search_1.html', {'filter': wine_filter})

My filtrs.py

from django.db import models
from .models import Review, Wine
import django_filters

class ReviewFilter(django_filters.FilterSet):
    comment = django_filters.CharFilter(lookup_expr='icontains')
    class Meta:
        model = Review
        fields = ['comment', 'wine', 'rating', 'user_name']

class WineFilter(django_filters.FilterSet):
    class Meta:
        model = Wine
        fields = ['name',]

EDIT I think that a good solution the problem can be something like that ( on a simplified example):

{% if a != c %}
{% for wine in filter.qs %}
    <li>{{ wine.name }} - {{ wine.get_full_name }}</li>
{% endfor %}
{% else %}
<p>Search for something</p>
{% endif %}

where a = all indexed 'name' in the database and b = all rows in the table (but how can it be calculated)?

Maddie Graham
  • 2,019
  • 4
  • 25
  • 51

1 Answers1

3

EDITED I think your question edit cleared up the problem you are facing a little bit. You basically want the web page to display

"Search Something"

if there hasn't been any search below the rendered form, and when the user searches, then you want to display all the fitered values, right?

Then your view must handle that logic and the check mustn't be performed in the Template (as suggested by you in the edit a != c then render the filter), as that leads to Coupling in the two modules, which is undesirable. The way to go according to me in this scenario is a slight modification that I would suggest, i.e. the usage of a check based on request.method in your view as well as using a POST as the method for your form. You can read about request.method in Django here.

Solution to your problem:

  1. Change form method to post and add CSRF Token to form for handling POST requests.
<form method="post">
{% csrf_token %}
...

  1. Add the logic below to your search view:
    
    def search_view(request):
        wine_list = Wine.objects.all()
        wine_filter = WineFilter(request.POST, queryset=wine_list)
        if request.method == "POST":
            return render(request, 'reviews/search.html', context={'filter': wine_filter, 'searched': True})
        else:   # Handle GET request
            return render(request, 'reviews/search.html', context={'filter': wine_filter})

3.And your template changes as explained above:

{% if searched %}
{% for wine in filter.qs %}
    <li>{{ wine.name }} - {{ wine.get_full_name }}</li>
{% endfor %}
{% else %}
    <p>Search for something</p>
{% endif %}

The reason for changing the FORM method to POST is because when a GET request is issued, the state of the page is not changed. However, you want to update the state of the page depending on whether a search has occurred or not. This calls for a POST request. I'd redirect you to this excellent answer on HTTP Methods: When do you use POST and when do you use GET?

Working Screenshots:

Initial Page
After Search

saketk21
  • 435
  • 4
  • 13
  • Comment on this answer if I am wrong and that is not what your problem is. – saketk21 Aug 07 '18 at 17:38
  • 1
    The newer approach suggested is strongly advised to use for a view supporting multiple HTTP request verbs. I suggest you follow a well-established tutorial of Django to learn good programming practices. DjangoGirls or TheNewBoston have good Django tutorials. – saketk21 Aug 07 '18 at 18:12
  • Everything works fine, thank you very much for your help! I really appreciate that. – Maddie Graham Aug 07 '18 at 20:53
  • @MaddieGraham But I suggest you don't stop at this quick fix and take my advice. You're welcome. – saketk21 Aug 08 '18 at 01:16