django-filter no matches - django

I couldn't find it in documentation so Im asking here. I've created filter with django-filter and it works properly, but if someone will select filters that none object has, then user will get empty page. I would like to add simple paragraph
None criteria matches
if filtered object don't exist.
I've tried with template tags like
{% if obj in filter.qs != none %}
{% endif %}
But it doesn't work. Does someone know how to make it?
filters.py
import django_filters
from .models import Company, COMPANY_TECHNOLOGIES
from django_filters import ChoiceFilter
class CompanyFilter(django_filters.FilterSet):
class Meta:
model = Company
fields = ['type', 'city', 'students']
def __init__(self, *args, **kwargs):
super(CompanyFilter, self).__init__(*args, **kwargs)
self.filters['type'].extra.update(
{'empty_label': 'All'})
self.filters['city'].extra.update(
{'empty_label': 'All'})
self.filters['students'].extra.update(
{'empty_label': 'All'})
comp_list.html
{% extends 'company/base.html' %}
{% block content %}
<div id="filter">
<form action="" method="get" id="submit">
{{ filter.form.as_p }}
<input type="submit"/>
</form>
{% for obj in filter.qs %}
{{ obj.name }}
<p>Image {% if obj.image != None %}
<img src="{{ obj.image.url }}">
{% endif%}</p>
<p>Icon {% if obj.icon != None %}
<img src="{{ obj.icon.url }}" width="30" height="30">
{% endif%}</p>
<br> Type: {{ obj.type }} City: {{ obj.city }} Stack: {{ obj.stack }}
{% if obj not in filter.qs %}
<p>no matches</p>
{% endif %}
{% endfor %}
{% endblock %}
</div>
views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.utils import timezone
from .models import Company
from .filters import CompanyFilter
from rest_framework import viewsets
from .serializers import CompanySerializer
# Create your views here.
def comp_list(request):
f = CompanyFilter(request.GET, queryset=Company.objects.all())
return render(request, 'company/comp_list.html', {'filter': f})
##def brands(request, slug):
##brands = Company.objects.all()
##return render(request, 'company/comp_view.html', {'brands': brands})
def brands(request, pk):
brand = get_object_or_404(Company, pk=pk)
return render(request, 'company/comp_view.html', {'brand': brand})
#rest api
class CompanyViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows groups to be viewed or edited.
"""
queryset = Company.objects.all()
serializer_class = CompanySerializer

Sorry for taking your time, I should go straight away to django docs and use built in template tag
{% empty %}
I've just added this into my comp_list.html like below
{% extends 'company/base.html' %}
{% block content %}
<div id="filter">
<form action="" method="get" id="submit">
{{ filter.form.as_p }}
<input type="submit"/>
</form>
{% for obj in filter.qs %}
{% if obj in filter.qs %}
{{ obj.name }}
<p>Image {% if obj.image != None %}
<img src="{{ obj.image.url }}">
{% endif%}</p>
<p>Icon {% if obj.icon != None %}
<img src="{{ obj.icon.url }}" width="30" height="30">
{% endif%}</p>
<br> Type: {{ obj.type }} City: {{ obj.city }} Stack: {{ obj.stack }}
{% endif %}
{% empty %}
<p>no matches</p>
{% endfor %}
{% endblock %}</div>
And now everything works as it should, if user will try to filter object with criterias that dont match he will get response as paragraph <p> No Matches </p>

Related

Django simple search with Class based views and forms.py

I have been trying to do a variation of what Williams Vincent did on this page: https://learndjango.com/tutorials/django-search-tutorial .
I am using Django 3.2 so if there are modifications, I need to make I have not identified them. I am having some troubles.
This what I made which worked just fine.
my_search.html:
{% extends "base.html" %}
{% block body %}
{% for city in object_list %}
<li>
{{city.name}}   {{city.city_no}}
</li>
{% endfor %}
{% endblock %}
views.py:
from django.views.generic import ListView
from .models import City
class SearchResutlsView(ListView): # test version
model = City
template_name = "search_results.html"
def get_queryset(self):
return City.objects.filter(name__icontains='Boston')
Now it is time to add forms.py, but when I made the below changes to the code it does not work. What am I missing? There are no errors displayed. I get a blank html.
{% extends "base.html" %}
{% block body %}
<form class="d-flex" method='get' action="{% url 'city:search_results' %}">
{{ form }}
<button class="btn btn-outline-success" type="submit" value="qu">Search Name</button>
</form>
{% for city in city_list %}
<li>
{{city.name}}   {{city.city_no}}
</li>
{% endfor %}
{% endblock %}
forms.py
from django import forms
class SearchForm(forms.Form):
q = forms.CharField(label='Search label', max_length=50, strip=True)
views.py
from django.views.generic import FormView, ListView
from .models import City
class SearchResutlsView(FormView):
model = City
form_class = SearchForm
template_name = "city/search_results.html"
def get_queryset(self):
query = self.request.Get.get("q")
if query:
city_list = City.objects.filter(name__icontains=query)
else:
city_list = City.objects.none()
return city_list
First, Your method should be POST not get.
Second, you need to add CSRF token.
something like that:
{% extends "base.html" %}
{% block body %}
<form class="d-flex" method='post' action="{% url 'city:search_results' %}">
{% csrf_token %}
{{ form }}
<button class="btn btn-outline-success" type="submit" value="qu">Search Name</button>
</form>
{% for city in city_list %}
<li>
{{city.name}}   {{city.city_no}}
</li>
{% endfor %}
{% endblock %}
and in views.py
query = self.request.POST.get("q")

No Post matches the given query

I am using Django version 2.
I am working on a blog web app using PostgreSQL database.
I am trying to add a search feature to the web app but when I open the url (http://localhost:8000/search/) to make a search, I get the error below.
Page not found (404)
Request Method: GET
Request URL: http://localhost:8000/search/
Raised by: blog.views.post_detail
No Post matches the given query.
here is the blog/urls.py
from django.contrib.postgres.search import SearchVector
from .forms import CommentForm, SearchForm
from .models import Post, Comment
from django.shortcuts import render, get_object_or_404
from django.urls import path
from . import views
urlpatterns = [
path('', views.PostListView.as_view(), name='home'),
path('<slug:post>/', views.post_detail, name='post_detail'),
path('<int:post_id>/share', views.post_share, name='share'),
path('search/', views.post_search, name='post_search'),
]
here is the views.py
def post_detail(request, post):
post = get_object_or_404(Post, slug=post)
comments = post.comments.filter(active=True)
new_comment = None
if request.method == 'POST':
comment_form = CommentForm(data=request.POST)
if comment_form.is_valid():
new_comment = comment_form.save(commit=False)
new_comment.post = post
new_comment.save()
else:
comment_form = CommentForm()
return render(request, 'detail.html', {'post': post,
'comments': comments, 'new_comment':
new_comment,'comment_form': comment_form})
def post_search(request):
form = SearchForm()
query = None
results = []
if 'query' in request.GET:
form = SearchForm(request.GET)
if form.is_valid():
query = form.cleaned_data['query']
results = Post.objects.annotate(
search=SearchVector('title','body'),
).filter(search=query)
return render(request, 'search.html',
{'form':form,'query':query,'results':results})
Here are the templates files.
For search.html -(page to make query)
{% extends 'base.html' %}
{% block title %}Search{% endblock title %}
{% block page_title %}Search Posts{% endblock page_title %}
{% block content %}
{% if query %}
<h1>Posts containing "{{ query }}"</h1>
<h3>
{% with results.count as total_results %}
Found {{ total_results }} result{{ total_results|pluralize }}
{% endwith %}
</h3>
{% for post in results %}
<h4>{{ post.title }}</h4>
{{ post.body|truncatewords:5 }}
{% empty %}
<p>There is no results for your query.</p>
{% endfor %}
<p>Search again</p>
{% else %}
<h1>Search for posts</h1>
<form action="." method="GET">
{{ form.as_p }}
<input type="submit" value="Search">
</form>
{% endif %}
{% endblock content %}
For the html for post_detail
{% extends 'base.html' %}
{% load blog_tags %}
{% load crispy_forms_tags %}
{% load profanity %}
{% block title %}{{post.title}}{% endblock title %}
{% block navlink %}
<nav>
<ul>
<li class="current">Home</li>
<li>About</li>
<li >Services</li>
</ul>
</nav>
{% endblock navlink %}
{% block page_title %}{{post.title}}{% endblock page_title %}
{% block content %}
{{post.body|markdown}}
<p>Share this post via email</p>
{% with comments.count as total_comments %}
<h2>{{ total_comments }} comment{{ total_comments|pluralize }} </h2>
{% endwith %}
<div class="container">
{% for comment in comments %}
<div class="comment">
<p>Comment {{ forloop.counter }} by <em>{{ comment.name }}</em> - {{comment.created}} </p>
{{ comment.body|censor|linebreaks }}
</div>
{% empty %}
<p>There are no comments yet.</p>
{% endfor %}
{% if new_comment %}
<h2>Your comment has been added</h2>
{% else %}
<h2>Add a new comment</h2>
<form method="post">{% csrf_token %}
{{comment_form|crispy}}
<input class="button_1" type="submit" value="Add comment">
</form>
{% endif %}
</div>
{% endblock content %}
This is probably for one of the strange reason search is here actually matches with slug:post. One of easiest solution for your problem is to re-order urls and make sure /search place before. Like following
urlpatterns = [
path('', views.PostListView.as_view(), name='home'),
path('search/', views.post_search, name='post_search'),
path('<slug:post>/', views.post_detail, name='post_detail'),
path('<int:post_id>/share', views.post_share, name='share')
]
My suggestion for further developement whenever its need to add slug in url we need to make sure there is some prefix before it.

Django forms fields not displaying with include tag

I have spent a few days trying to find an answer to my issue. I have been reading and trying answers given to users with similar problems, but none of them worked.
I created a contact form and it works perfectly if I open the html where the code for the form is. But when I try to display it on index using the include tag, it shows the submit button, the structure and the style, but not the form fields.
This is what I have in my code:
views.py
from django.http import HttpResponse
from django.views import generic
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.core.mail import send_mail
from .forms import ContactForm
from django.shortcuts import render
# Create your views here.
def index(request):
return render(request, 'landingpage/index.html', {})
#Contact form
def contact(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
name = request.POST.get ('name')
email = request.POST.get ('email')
message = request.POST.get ('message')
send_mail('Subject here', content, contact_email, [‘xxx#gmail.com'], fail_silently=False)
return HttpResponseRedirect('/thanks/')
else:
form = ContactForm()
return render(request, 'landingpage/contact.html', {'form': form})
#Thank you message
def thanks (request):
return render(request, 'landingpage/thanks.html', {})
urls.py
app_name = 'landingpage'
urlpatterns = [
# Landingpage urls
url(r'^$', views.index, name='landing-index'),
url(r'^contact/$', views.contact, name='contact'),
url(r'^thanks/$', views.thanks, name='thanks'),
]
index.html
{% block form %}
{% include 'landingpage/contact.html' with form=form %}
{% endblock form %}
contact.html
{% block form %}
<section id="contact">
<div class="container">
<div class="row">
<div class="col-lg-12 text-center">
<h2 class="section-heading text-uppercase">Contact Us</h2>
</div>
</div>
<div class="row">
<div class="col-lg-12">
{% if form.errors %}
<p style="color: red;">
Please correct the error{{ form.errors|pluralize }} below.
</p>
{% endif %}
<form id="contactForm" name="sentMessage" action="" method="post" novalidate>
{% csrf_token %}
{% for hidden_field in form.hidden_fields %}
{{ hidden_field }}
{% endfor %}
{% if form.non_field_errors %}
<div class="alert alert-danger" role="alert">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
{% for field in form.visible_fields %}
<div class="form-group">
{{ field.label_tag }}
{% if form.is_bound %}
{% if field.errors %}
{% render_field field class="form-control is-invalid" %}
{% for error in field.errors %}
<div class="invalid-feedback">
{{ error }}
</div>
{% endfor %}
{% else %}
{% render_field field class="form-control is-valid" %}
{% endif %}
{% else %}
{% render_field field class="form-control" %}
{% endif %}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text }}</small>
{% endif %}
</div>
{% endfor %}
<div class="clearfix"></div>
<div class="col-lg-12 text-center">
<div id="success"></div>
<button id="sendMessageButton" class="btn btn-primary btn-xl text-uppercase" type="submit">Send Message</button>
</div>
</div>
</form>
</div>
</div>
</div>
</section>
{% endblock form%}
This is because your index view does not have form variable in it`s context. You should write it like this:
def index(request):
ctx = {
'form': ContactForm()
}
return render(request, 'landingpage/index.html', ctx)

HTML layout object doesn't work

I quite new with Django Crispy Form and I have tried to add a HTML object in my form but it's not working. All the other elements are rendered but not the HTML.
This is my form:
forms.py
from crispy_forms.layout import Layout, Fieldset, HTML
class MyUserRegistrationForm(forms.ModelForm):
class Meta:
model = MyUser
fields = ['title', 'privacy_disclaimer_accepted']
def __init__(self, *args, **kwargs):
super(MyUserRegistrationForm, self).__init__(*args, **kwargs)
helper = FormHelper()
helper.form_method = 'post'
helper.form_class = 'form-horizontal'
helper.label_class = 'col-sm-2'
helper.field_class = 'col-sm-6'
helper.form_error_title = 'Form Errors'
helper.error_text_inline = True
helper.help_text_inline = True
helper.html5_required = True
helper.form_tag = False
helper.layout = Layout(
Fieldset('Information', 'title'),
Fieldset('Privacy Statement',
HTML("""
<div id="iframe" class="mt-5">
<h6>Notice/Disclaimer:</h6>
<div class="privacy-policy">
{% if privacy_disclaimer %}
{{ privacy_disclaimer }}
{% else %}
{% include "registration/privacy_policy.html" %}
{% endif %}
</div>
</div>
"""),
'privacy_disclaimer_accepted', )
)
I have a basic HTML file, where I have the normal html, header and body tags.
This is the HTML registration page:
register.html
{% extends "base.html" %}
{% block title %}Create an account{% endblock %}
{% block content %}
<div class="registration-page">
<h3 class="text-center">Create an account</h3>
<div class="registration-page__form">
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-error">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<div class="alert alert-error">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endif %}
<form action="" method="post">
{% csrf_token %}
{% load crispy_forms_tags %}
{{ form_myuser|crispy }}
<br/>
<div class="text-right">
<input type="submit" class="btn btn-primary btn--action" value="Create the account">
</div>
</form>
</div>
</div>
{% endblock %}
EDIT:
views.py
class RegisterView(View):
template_name = 'registration/register.html'
def _get_context_data(self, **kwargs):
context = {}
privacy_disclaimer = ''
context['privacy_disclaimer'] = privacy_disclaimer
if kwargs:
context.update(kwargs)
return context
def get(self, request, *args, **kwargs):
extra_context = {
'form_myuser': MyUserRegistrationForm(),
}
context = self._get_context_data(**extra_context)
return render(request, self.template_name, context)
I fixed the issue and the problem was the form loading.
I was loading it in the wrong way.
Instead of this {{ form_myuser|crispy }} I have to use this code {% crispy form_tolauser %}.

haystack on existing template

How to implement working SearchView in existing views.py?
I already have CBV, and added in urls.py as /moderate and want to apply search form in it. but always got "Results No results found."
This is my /moderate page with 3 forms, using SearchView and piece of code from tutorial in template.
And this from /search page, with urls(r'^search/$', include('haystack.urls'))
urls.py
urlpatterns= [
url(r'^search/', include('haystack.urls')),
url(r'^moderate/', Moderate.as_view(), name='moderate'),
]
views.py
class Moderate(SearchView):
#method_decorator(staff_member_required)
def dispatch(self, *args, **kwargs):
return super(Moderate, self).dispatch(*args, **kwargs)
#model = Ad
template_name = 'adapp/ad_moderate.html'
#template_name = 'search/search.html'
paginator_class = DiggPaginator
paginate_by = 10
ad_type = None
ad_sub_type = None
def get_queryset(self):
qs = super(Moderate, self).get_queryset().filter(ad_type__isnull=False,
ad_sub_type__isnull=False)
return qs
def get_context_data(self, **kwargs):
context = super(Moderate, self).get_context_data(**kwargs)
context['filter'] = ModerateFilter(self.request.GET)
return context
# define method to recieve fields from form, and change data accordings
def post(self, request, *args, **kwargs):
selected = request.POST['selected']
record = Ad.objects.get(pk=int(selected))
form = ModerateForm(request.POST, instance=record)
if form.is_valid():
form.save(commit=True)
return HttpResponseRedirect('')
template/ad_moderate.html
{% extends 'base.html' %}
{% load i18n url_tags %}
{% block content %}
<div id="casing">
<div id="content">
{# filter form, to show only models with moderated=True #}
<form action="" method="get">
{{ filter.form.as_p }}
<input type="submit">
</form>
<h2>Search</h2>
{# search form right from tutorial #}
<form method="get" action="">
<table>
{{ form.as_table }}
<tr>
<td> </td>
<td>
<input type="submit" value="Search">
</td>
</tr>
</table>
{% if query %}
<h3>Results</h3>
{% for result in page.object_list %}
<p>
{{ result.object.title }}
</p>
{% empty %}
<p>No results found.</p>
{% endfor %}
{% else %}
{# Show some example queries to run, maybe query syntax, something else? #}
{% endif %}
</form>
{% for object in filter %}
{# a lot of template tags and third form to change value of model #}
<form action="" method="POST">
{% csrf_token %}
<input type="radio" name="moderated" value="True">Accept
<br>
<input type="radio" name="moderated" value="False">Decline
<input type="hidden" value="{{ object.id }}"
name="selected">
<input class="btn" type="submit" value="moderate">
</form>
search_indexes.py
from .models import Ad
class AdIndex(indexes.SearchIndex, indexes.Indexable):
text = indexes.CharField(document=True, use_template=True)
def get_model(self):
# my model, with one search should be
return Ad
templates/search/indexes/app/ad_text.txt
{{ object.title }}
{{ object.short_desc }}
{{ object.description }}
{{ object.experience }}
{{ object.skills }}
{{ object.name }}
{{ object.city }}
get_context_data():
context['search'] = SearchForm(self.request.GET).search()
Would solve a problem.
That means, I should create form and send return from .save() method, rather that django-like form instance.