Django 2.0: Get queries dynamically from search - django

I am new to Django framework and as a practice project I am trying to build an ecommerce website using Django. I have a class based view for my search page. I wrote the view to get the query for a particular query:
views.py
class SearchProductView(ListView):
template_name = "template.html"
queryset = Product.objects.filter(title__icontains='book')
print(queryset)
I would like to know how I can write a function to get the search queries dynamically. For e.g.: If i search book, then my queryset should contain all things about book and if I search car, then I should get all things about car.
template.html
{% extends "base.html" %}
{% block content %}
<div class='row'>
{% for object in object_list %}
<div class='col'>
{{ forloop.counter }}
{% include 'products/snippets/card.html' with instance=object %}
</div>
{% endfor %}
</div>
{% endblock %}
urls.py
from django.urls import path
from . import views
from .views import SearchProductView
app_name = 'search'
urlpatterns = [
path('', SearchProductView.as_view(), name='search_page'),
]

You need to define the get_queryset method, rather than the class-level queryset attribute. This can use your querystring parameters to filter the queryset dynamically.
You haven't shown your search form or said what your parameter is, but assuming it submits a GET parameter named q, you would do:
def get_queryset(self):
return Product.objects.filter(title__icontains=self.request.GET['q'])

Although the answer above will work I believe a better solution would be the following:
models.py
class Category(models.Model):
title = models.CharField(...)
class Product(models.Model):
...
category = models.ForeignKey(Category)
views.py
from django.db.models import Q
def get_queryset(self):
querystr = self.request.GET['q']
Product.objects.filter(
Q(title__icontains=querystr) | Q(category__title__icontains=querystr)
)

Related

Django Filter Queryset Model

How can I filter my table to only show the quotes from a project.
In order to display all quotes, I am using {% for quote in quotes.all %}
Now, I would like to display quote relative to a site. Which means when selecting my site_id, I`ll be able to only see the quote relative to this site_id. I could write something like
{% for quote in quotes.all %}
{% if quote chantier.id %}
{% endif %}
{% endfor %}
but this is wrong.
Here is my model for quote:
models.py
class Quote(models.Model):
name = models.CharField(max_length=30)
site = models.ForeignKey(Site, on_delete=models.CASCADE)
How can I display all quotes from this site?
Many Thanks,
You can make a url path that contains the site_id:
# app_name/urls.py
from django.urls import path
from app_name import views
urlpatterns = [
path('site/<int:site_id>/', views.quotes_of_site, name='quotes_of_site'),
# …
]
in the view, you can then filter by site_id:
# app_name/views.py
from django.shortcuts import render
from app_name.models import Quote
def quotes_of_site(request, site_id):
quotes = Quote.objects.filter(site_id=site_id)
return render(request, 'name_of_template.html', {'quotes': quotes})
and then iterate over this QuerySet:
{% for quote in quotes %}
…
{% endfor %}

Django Crispy forms input out of order

I am building some forms using django-crispy_forms, I need them to be in the some specific order (specifically the order I stablished on the fields inside forms.py).
forms.py:
class RegistroRelatorio(forms.ModelForm):
class Meta:
model = RelatorioVendas
fields = {"razao_social","codigo_interno","nome_fantasia","endereco","bairro","uf","telefones","cnpj","fundacao",
"negocios","cidades_negocios","c1_nome","c1_whats","c1_email","c1_cargo","c1_comunicacao","c1_preferencia"}
views.py
from django.shortcuts import render
from .models import RelatorioVendas
from django.http import HttpResponse
from .forms import RegistroRelatorio
# Create your views here.
def novo_relatorio(request):
form = RegistroRelatorio(request.POST or None)
if form.is_valid():
form.save()
return HttpResponse('Ok...')
return render(request,'river/novo_relatorio.html',{'form':form})
html:
{% extends 'river/base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<h3>Novo Relatorio</h3>
<form method="post" >
{% csrf_token %}
{{form | crispy}}
<button type="submit">Ok</button>
</form>
{% endblock %}
I tried to use label_order inside this class, it worked, but when I use the crispy form it stops working...
I even tried to use the Layout method from the crispy library.
Use a 'list' instead of a 'set' for the fields in forms.py:
class RegistroRelatorio(forms.ModelForm):
class Meta:
model = RelatorioVendas
fields = ["razao_social", "codigo_interno", "nome_fantasia", "endereco", "bairro", "uf", "telefones", "cnpj",
"fundacao", "negocios", "cidades_negocios", "c1_nome", "c1_whats", "c1_email", "c1_cargo",
"c1_comunicacao", "c1_preferencia"]
According to the documentation:
A set is an unordered collection with no duplicate elements.
I don't see any problems here for crispy to render fields in the order that you provided. Show your html and views.

Trying to include a form on another app's template with {% include %}, but getting "TemplateDoesNotExist"

Currently, I can leave a comment about a post, but I have to go to a separate comment create page. I want to include the comment form right under the post in the group detail page. I have tried to use {% include %}, but it cant seem to find the form. This is probabaly because I am trying to render the form on a different app's template than where the form.py and comment_form.html are (I create the form and the template for the form in the 'comments' app, and I'm trying to include the form in the the 'groups' app on the detail page. here are the relevant files.
comments/forms.py
from django import forms
from .models import Comment
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('body',)
comments/comment_form.html
<h2>this is the comment form</h2>
<form class="post-form" method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Save</button>
</form>
groups/views.detail :
def detail(request, group_id):
group = get_object_or_404(Group, pk= group_id)
posts = Post.objects.filter(group__id = group_id)
form = CommentForm()
return render(request, 'groups/detail.html', {'group': group, 'posts':posts, 'form':form})
groups/detail.html:
{% include form %}
this is the url that takes care of creating a comment (comments/urls.py):
from . import views
from django.urls import path
app_name = 'comments'
urlpatterns = [
path('<int:post_id>/create/', views.create, name='create'),
path('delete/<int:group_id>/<int:post_id>/<int:comment_id>', views.delete, name='delete'),
]
again, it can't find the template, probabaly because I have to make the 'groups' app aware of 'comment_form.html' 's existence. how do I accomplish this?
If you want to include comments/comment_form.html you should do:
{% include 'comments/comment_form.html' %}
Currently, you have {% include form %} which will use the variable form - that doesn't make sense, since form is a form instance CommentForm(), it is not a template name.

Query on generic display views - Django

For the below url routing for blog app,
from django.conf.urls import url, include
from django.views.generic import ListView, DetailView
from blog.models import Post
urlpatterns=[
url(r'^$', ListView.as_view(
queryset=Post.objects.all().order_by("-date")[:25],
template_name="blog/blog.html",
)
)
]
template blog.html is,
{% extends "personal/header.html" %}
{% block content %}
{% for post in object_list %}
<h5>{{post.date|date:"Y-m-d"}} {{post.title}} </h5>
{% endfor %}
{% endblock %}
where model for blog app is defined as,
class Post(models.Model):
title = models.CharField(max_length=140)
body = models.TextField()
date = models.DateTimeField()
def __str__(self):
return self.title
MTV of blog app is structures as,
../blog
admin.py
apps.py
__init__.py
migrations
models.py
templates
tests.py
urls.py
views.p
Question:
{{post.id}} is internally created as primary key, for every row in the table, but,
What does /blog/{{post.id}} mean in the template(blog.html)?
When you want to go to a particular blog, you need to have a link to that. That's what /blog/{{post.id}} does as a link.
so /blog/1 gives you the first blog. Only that you have to define the url pattern, the view and the template for that.
url(r'^(?P<id>[^/]+)/$', views.get_blog, name='one_blog'),
Then in views:
def get_blog(request, id):
blog = Blogs.objects.get(id=id)
return render(request, 'blogs/one_blog.html', locals())
And then in templates folder, create a 'blogs/one_blog.html' file. Simplest example being:
{% extends "personal/header.html" %}
{% block content %}
<h5>{{blog.title}}</h5>
{{blog.body}}
{% endblock %}
Just make sure that you understand the folder structure for templates.
it's just a prefix /prefix/id/. It is also possible /article/1... it doesn't matter
urls.py
urlPatterns=[
url(r'^$', ListView.as_view(
model=Post,
template_name="blog/blog_list.html",
)
)
url(r'blog/(?P<pk>[\w-]+)/$', DetailView.as_view(
model=Post,
template_name="blog/blog_detail.html",
)
)
]

ListView for Django not Working but function based view is working

When I am using function based view using the following code:
from django.views import View
from django.views.generic import TemplateView, ListView
from .models import Restaurant
def restaurant_listview(request):
template = 'restaurants/restaurants_list.html'
context = {
"queryset" : Restaurant.objects.order_by('-updated')
}
return render (request, template, context)
it is working with the url file kept as follows:
from django.conf.urls import url
from django.contrib import admin
from restaurants import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^restaurants/$', views.restaurant_listview),
]
But when I am trying to do the same thing with class based views it is not working only the following portion doesn't seem to work:
<ul>
{% for obj in queryset %}
<li>{{obj.name}}, {{obj.location}}, {{obj.category}}, {{obj.timestamp}}</li>
{% endfor %}
</ul>
the following part works fine:
{% extends 'base.html' %}
{% block title %}
Restaurants List {{ block.super }}
{% endblock %}
{% block content %}
<h1>Restaurants</h1>
for class based view my views.py is:
class RestaurantListView(ListView):
queryset = Restaurant.objects.all()
template_name = 'restaurants/restaurants_list.html'
and urls.py is:
url(r'^restaurants$', RestaurantListView.as_view(), name='Home')
P.S. I am following this guide : https://www.youtube.com/watch?v=yDv5FIAeyoY&t=25471s
For a list view, you should change the template to:
{% for obj in restaurant_list %}
Or, if you really want to use the variable queryset in the template, then set context_object_name.
class RestaurantListView(ListView):
queryset = Restaurant.objects.all()
template_name = 'restaurants/restaurants_list.html'
context_object_name = 'queryset'