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
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")
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.
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)
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 %}.
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.