How to create a Class Based Views search bar on Django - python

I'm making a search bar in Django using Class-Based Views on my Views.py. When I try to get the value submitted in the input it's not showing the query name and if I don't submit anything is NOT giving me an error. Also what is the way to get the post showed by the world searched? Thanks for all. My code is:
Models.py:
class Post(models.Model): # Post core
title = models.CharField(max_length=299)
author = models.ForeignKey(User,default=ANONYMOUS_USER_ID, on_delete=models.CASCADE)
category = models.CharField(max_length=50)
image = models.ImageField(blank=True)
desc = models.TextField()
text = RichTextField(blank = True, null = True )
date = models.DateTimeField(auto_now=False, auto_now_add=True)
slug = models.SlugField(null = True, blank = True, unique=True)
class Meta: # Order post by date
ordering = ['-date',]
def __str__(self): # Display title
return self.title
def get_absolute_url(self): # #TODO da cambiare
return reverse("listpost")
Views.py:
class SearchView(TemplateView):
model = Post
template_name = "admin/search.html"
def get(self, request, *args, **kwargs):
q = request.GET.get('q', '')
self.results = Post.objects.filter(text__icontains=q, desc__icontains = q, title__icontains = q)
return super().get(request, *args, **kwargs)
Urls.py:
path('dashboard/listpost/search/', SearchView.as_view(), name="search")
ListPost.html:
{% extends "admin/layout.html" %}
{% block title %}
<title>Post List</title>
{% endblock title %}
{% block content %}
<form action="{% url 'search' %}" method="GET">
<input type="text" placeholder="Cerca" name="search"> <button>Cerca</button>
</form>
<div class="postlist">
<div class="cards"> <!-- Card Container -->
{% for post in object_list %}
<div class="card"> <!-- Single Card -->
{% if post.image %}
<img src="{{post.image.url}}" alt="">
{% endif %}
<h3>{{ post.title }}</h3>
<p>
{{ post.category }}
{{ post.author }}
<data class="data"> {{ post.date|date:'d-m-Y' }} </data>
</p>
<p class="desc-list">{{ post.desc|truncatewords:10 }}</p>
edit
delate
</div>
{% endfor %}
</div>
</div>
{% endblock content %}
Search.html
{% extends "admin/layout.html" %}
{% block title %}
<title>Search</title>
{% endblock title %}
{% block content %}
{% if query %}
{{Query}}
{% else %}
<p>Nope</p>
{% endif %}
{% endblock content %}

Your are trying to display the variable query, Query (two different variables since the template language is case sensitive).
Your do not pass any of those two variables in the template context.
I don't see any query nor Query variable in your view.
It seems that your want to show the results variable, so I will assume this.
Your need to send the results from your queryset in the template results.
The get_context_data method ContextMixin (one of the TemplateView derived classes) is useful for this.
class SearchView(TemplateView):
model = Post
template_name = "admin/search.html"
def get(self, request, *args, **kwargs):
q = request.GET.get('q', '')
self.results = Post.objects.filter(text__icontains=q, desc__icontains = q, title__icontains = q)
return super().get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
"""Add context to the template"""
return super().get_context_data(results=results, **kwargs)
And in your template:
{% extends "admin/layout.html" %}
{% block title %}
<title>Search</title>
{% endblock title %}
{% block content %}
{% if results.exists %}
{% for result in results %}
{{ result }}
{% endfor %}
{% else %}
<p>Nope</p>
{% endif %}
{% endblock content %}

Related

List View is not working but get_context_data() works

I have a ListView but when I call it only the get_context_data method works (the news and category model, not the product) when I try to display the information of the models in the templates.
view:
class HomeView(ListView):
model = Product
context_object_name='products'
template_name = 'main/home.html'
paginate_by = 25
def get_context_data(self, **kwargs):
categories = Category.objects.all()
news = News.objects.all()
context = {
'categories' : categories,
'news' : news,
}
context = super().get_context_data(**kwargs)
return context
There is also this piece of code:
context = super().get_context_data(**kwargs)
If it's written before:
categories = Category.objects.all()
The Product model is show but not the others.
base.html
<body>
...
{% include "base/categories.html" %}
{% block content %}{% endblock %}
</body>
home.html
{% extends 'main/base.html' %}
{% block content %}
<div>
...
<div>
{% for product in products %}
{% if product.featured == True %}
<div>
<div>
{{ product.author }}
<small>{{ product.date_posted|date:"F d, Y" }}</small>
</div>
<p>Some text..</p>
</div>
{% endif %}
{% endfor %}
</div>
</div>
{% endblock content %}
categories.html
<div>
...
<div>
{% for category in categories %}
<p>{{ category.name }}</p>
{% endfor %}
</div>
<div>
{% for new in news %}
<p>{{ new.title }}</p>
{% endfor %}
</div>
</div>
You can also try this:
class HomeView(ListView):
model = Product
context_object_name='products'
template_name = 'main/home.html'
paginate_by = 25
def get_context_data(self, **kwargs):
categories = Category.objects.all()
news = News.objects.all()
context = super().get_context_data(**kwargs)
context["categories"]=categories
context["news"]=news
return context
The problem is that you override context, but you need to update it. Try this:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
categories = Category.objects.all()
news = News.objects.all()
context.update({
'categories' : categories,
'news' : news,
})
return context

Like button is working but not changing to Unlike in django

I've been trying to debug this issue for a day together with my expert coder but we were unable to identify the issue here. The like button works perfectly all right and I am able to query the total likes, but the Clap button does not change to unlike and we are unsure why. I believe the error lies in either the html line:
{% if liked and post.slug == blog_post.slug %} Unlike {% else %} Like {% endif %}
or the django line:
context["liked"] = True if blog_post.likes.filter(id=request.user.id).exists() else False
Big thanks if you can identify the issue here!
views.py
def home_feed_view(request, *args, **kwargs):
context = {}
blog_posts = BlogPost.objects.all()
context['blog_posts'] = blog_posts
if request.method=="POST":
slug=request.POST["submit_slug"]
blog_post = get_object_or_404(BlogPost, slug=slug)
context['blog_post'] = blog_post
context["liked"] = True if blog_post.likes.filter(id=request.user.id).exists() else False
context['total_likes'] = blog_post.total_likes()
type_of_post = TypeofPostFilter(request.GET, queryset=BlogPost.objects.all())
context['type_of_post'] = type_of_post
paginated_type_of_post = Paginator(type_of_post.qs, 13 )
page = request.GET.get('page')
post_page_obj = paginated_type_of_post.get_page(page)
context['post_page_obj'] = post_page_obj
return render(request, "HomeFeed/snippets/home.html", context)
def LikeView(request, slug):
context = {}
user = request.user
if not user.is_authenticated:
return redirect('must_authenticate')
post = get_object_or_404(BlogPost, slug=slug)
liked = False
if post.likes.filter(id=request.user.id).exists():
post.likes.remove(request.user)
liked = False
else:
post.likes.add(request.user)
liked = True
return redirect(request.META.get('HTTP_REFERER', ''))
<td class="table-primary">
<form action="{% url 'HomeFeed:like_post' post.slug %}" method="POST">
{% csrf_token %}
<button type="submit" name="submit_slug" value="{{ post.slug }}" class='btn btn-primary btn-sm'>
{% if liked and post.slug == blog_post.slug %} Unlike {% else %} Like
{% endif %}
</button>
{{ post.total_likes }} Clap {{ post.total_likes|pluralize }}
</form>
</td>
urls.py
path('<slug>/like/', LikeView, name='like_post'),
models.py
class BlogPost(models.Model):
chief_title = models.CharField(max_length=50, null=False, blank=False)
body = models.TextField(max_length=5000, null=False, blank=False)
likes = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='blog_posts', blank=True)
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
slug = models.SlugField(blank=True, unique=True)
Ok so here's a much simpler way to perform a check if a model exist in a m2m field in another model:
html
{% if request.user in blog_post.likes.all %} Unlike {% else %} Like {% endif %}
view
if request.user in blog_post.likes.all():
P.S. In your model you should rename the field likes to likers since its a relation with a User model not a Like model :)
EDIT
So, to easily display a list of posts and their like buttons accordingly on a single page you wanna do this in your template:
views.py
def posts(request):
blog_posts = BlogPost.objects.all()
return render(request, 'index.html', {'blog_posts': blog_posts})
index.html
{% for post in blog_posts %}
<h1>{{ post.chief_title }}</h1>
<p>{{ post.author }} says: </p>
<b>{{ post.body }}</b>
<p> This post is liked by: </p>
{% for user in post.likes %}
<p>{{ user.username }}</p>
{% endfor %}
{% if request.user not in post.likes.all %}
Like
{% else %}
Unlike
{% endif %}
{% endfor %}

Django Image not uploading

I am unable to have the file in a inlineformset_factory actually upload to the DB or static folder. The form completes then executes succeful_url. Im just not understanding why this isn't uploading the files after submit. Any help is greatly appreciated.
Edit: I am not getting any errors at all.
[12/Mar/2020 15:04:02] "POST /agent/listings/new/ HTTP/1.1" 302 0
[12/Mar/2020 15:04:02] "GET /agent/dashboard/ HTTP/1.1" 200 6915
Model.py
class Listing(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4,editable=False)
agent = models.ForeignKey(CustomUser, on_delete=models.DO_NOTHING)
class Images(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
image = models.ImageField(upload_to="listing/images/")
listing_id = models.ForeignKey(Listing, on_delete=models.DO_NOTHING)
Forms.py
class ListingImage(forms.ModelForm):
class Meta:
model = Images
exclude = ()
ListingImageFormSet = inlineformset_factory(Listing, Images, fields=['image'], form=ListingImage, extra=2)
Views.py
class AgentNewListing(CreateView):
model = Listing
fields = ['agent']
template_name = 'agent/new_listing.html'
success_url = '/agent/dashboard/'
def get_context_data(self, **kwargs):
data = super(AgentNewListing, self).get_context_data(**kwargs)
if self.request.POST:
data['listing_form'] = ListingImageFormSet(self.request.POST)
else:
data['listing_form'] = ListingImageFormSet()
return data
def form_valid(self, form):
context = self.get_context_data()
listingform = context['listing_form']
with transaction.atomic():
form.instance.agent = self.request.user
self.object = form.save(commit=False)
if listingform.is_valid():
listingform.instance = self.object
listingform.save()
return super(AgentNewListing, self).form_valid(form)
def dispatch(self, request, *args, **kwargs):
if not is_valid_agent(self.request.user):
return redirect('/agent/signin/')
return super(AgentNewListing, self).dispatch(request, *args, **kwargs)
Template.htlm
{% extends 'agent/agent_base.html' %}
{% block agent_body %}
{% load crispy_forms_tags %}
<!-- Page Heading -->
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">New Listing</h1>
</div>
<div class="col-sm-6 col-lg-6 ml-2">
<form action="" method="post" enctype="multipart/form-data">{% csrf_token %}
{{ form|crispy }}
<table class="table">
{{ listing_form.management_form|crispy }}
{% for form in listing_form.forms %}
{% if forloop.first %}
<thead>
<tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr>
</thead>
{% endif %}
<tr class="">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<input class="btn btn-primary" type="submit" value="Submit"/> back to the list
</form>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.formset/1.2.2/jquery.formset.js"></script>
<script type="text/javascript">
$('.formset_row').formset({
addText: 'add family member',
deleteText: 'remove',
prefix: 'familymember_set'
});
</script>
{% endblock %}
Settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
Urls.py
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
The problem was I wasn't calling the self.request.FILES in the inlineformeset. The solution is in the view and looks like this.
def get_context_data(self, **kwargs):
data = super(AgentNewListing, self).get_context_data(**kwargs)
if self.request.POST:
data['listing_form'] = ListingImageFormSet(self.request.POST, self.request.FILES)
else:
data['listing_form'] = ListingImageFormSet()
return data

How to retrieve object that a different object is linked to in template? Django

I have a quiz model and a view where I'm displaying a list of all the quizzes on the page. I want to show the score that they got alongside each quiz. Currently, I have a score model that links to each quiz and the user that took the quiz. I want to do something like Score.objects.filter(user=request.user, quiz=quiz).get_percentage() to show the percentage achieved for that quiz. However this doesn't work in template code of course.
def learn(request): #Quiz Screen
context = {
'quizzes':Quiz.objects.all(),
'scores':Score.objects.filter(user=request.user),
}
return render(request, 'quiz_app/learn.html', context)
{% extends "quiz_app/base.html" %}
{% block content %}
New Quiz
{% for quiz in quizzes %}
<a id="quiz-anchor" href="{% url 'quiz-detail' quiz.id %}">
<article class="quiz-thumbnail-large">
<h2>{{ quiz.title }}</h2>
<h3>{{ quiz.question_amount }} Questions</h3>
<p>Score: {{ quiz.score.get_percentage }}%</p>
</article>
</a>
{% endfor %}
{% endblock content %}
class Quiz(models.Model):
title = models.CharField(max_length=100) #or TextField
#slug = models.SlugField(max_length=200, default=1) #url
video_link = models.CharField(max_length=100, default="https://www.youtube.com/watch?v=p60rN9JEapg")
question_amount = models.IntegerField()
author = models.ForeignKey(User, default=1, on_delete=models.CASCADE, related_name='quiz_author',)
date_created = models.DateTimeField(default=timezone.now)
class Meta:
verbose_name_plural = "Quizzes"
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('quiz-detail', kwargs={'pk': self.pk})
class Score(models.Model):
quiz = models.ForeignKey(Quiz, default=1, on_delete=models.CASCADE)
user = models.ForeignKey(User, default=1, on_delete=models.CASCADE)
correct_answers = models.IntegerField()
incorrect_answers = models.IntegerField()
def __str__(self): #returns as a/b
score = str(self.correct_answers) + '/' + str(self.get_total_answers())
return score
def add_correct_answers(self, amount):
self.correct_answers = self.correct_answers + amount
self.save()
def add_incorrect_answers(self, amount):
self.incorrect_answers = self.incorrect_answers + amount
self.save()
def get_total_answers(self):
total = self.correct_answers + self.incorrect_answers
return total
def get_percentage(self):
percentage = round(self.correct_answers / self.get_total_answers() * 100)
return percentage
So as you have a foreign Key relationship between Quiz and Score, you can have multiple scores for a single quiz, therefore you should use the backward relationship from the quiz model like this:
{% extends "quiz_app/base.html" %}
{% block content %}
New Quiz
{% for quiz in quizzes %}
<a id="quiz-anchor" href="{% url 'quiz-detail' quiz.id %}">
<article class="quiz-thumbnail-large">
<h2>{{ quiz.title }}</h2>
<h3>{{ quiz.question_amount }} Questions</h3>
{% for score in quiz.score_set.all %}
<p>Score: {{ score.get_percentage }}%</p>
{% endfor %}
</article>
</a>
{% endfor %}
{% endblock content %}
Edit
This is a quick fix:
{% extends "quiz_app/base.html" %}
{% block content %}
New Quiz
{% for quiz in quizzes %}
<a id="quiz-anchor" href="{% url 'quiz-detail' quiz.id %}">
<article class="quiz-thumbnail-large">
<h2>{{ quiz.title }}</h2>
<h3>{{ quiz.question_amount }} Questions</h3>
{% for score in quiz.score_set.all %}
{% if score.user == request.user %}
<p>Score: {{ score.get_percentage }}%</p>
{% endif %}
{% endfor %}
</article>
</a>
{% endfor %}
{% endblock content %}
But it's not a good idea, because you will retrieve all the score for all the users first, and then filter them; It's better to use a template tag here.

Django post not showing on homepage, but in category list

I made a blog with Django. The blog lets the admin create posts and categories. I used bootstrap to align the posts next to each other.
This is the homepage of my blog. When the posts reach the other end of the screen. look at the Example below:
post1 post2 post3 post4 post5 post6 post7 post8 post9 post10 post11 post12 post 13 post14 post16.
post 16 wouldnot show on the homepage, but if you go to its category it will show on the category list but not on the homepage which is index.html
index.html
{% extends 'base.html' %}
{% block content %}
{% if categories %}
<div class="tab">
{% for category in categories %}
<button class="tablinks"><a href="{{ category.get_absolute_url }}">{{
category.title }}</a></button>
{% endfor %}
{% else %}
<p>There are no posts.</p>
{% endif %}
</div>
<br><br><br>
<div class="container ">
{% if posts %}
<div class="row ">
{% for post in posts %}
<div class="poosts col-md-2">
<p class="past"><a class="link" href="{{ post.get_absolute_url }}"><span
class="tda"> {{post.title}}</span><br><br><span class="postbody">
{{post.body|truncatewords:13}}</a></span></p>
</div>
{% endfor %}
{% else %}
<p class="text-center">There are no posts.</p>
</div>
{% endif %}
</div>
{% endblock %}
categories page
{% extends 'base.html' %}
{% block head_title %}Viewing category {{ category.title }}{% endblock %}
{% block title %}{{ category.title }}{% endblock %}
{% block content %}
<br><br><br>
<div class="container">
{% if posts %}
<div class="row">
{% for post in posts %}
<div class="poosts col-md-4">
<p class="past"><a class="link" href="{{ post.get_absolute_url }}"><span
class="tda"> {{post.title}}</span><br><br>{{post.body|truncatewords:13}}</a>
</p>
</div>
{% endfor %}
{% else %}
<p class="text-center">There are no posts.</p>
</div>
{% endif %}
</div>
{% endblock %}
views.py
from django.shortcuts import render, redirect, get_list_or_404
from django.http import HttpResponse
from .models import Blog, Category
from django.shortcuts import render_to_response, get_object_or_404
def index(request):
return render_to_response('account/index.html', {
'categories': Category.objects.all(),
'posts': Blog.objects.all()[:5]
})
def view_post(request, slug):
return render_to_response('account/view_post.html', {
'post': get_object_or_404(Blog, slug=slug)
})
def view_category(request, slug):
category = get_object_or_404(Category, slug=slug)
return render_to_response('account/view_category.html', {
'category': category,
'posts': Blog.objects.filter(category=category)[:5]
})
models.py
from django.db import models
from django.db.models import permalink
class Blog(models.Model):
title = models.CharField(max_length=100, unique=True)
slug = models.SlugField(max_length=100, unique=True)
body = models.TextField()
posted = models.DateTimeField(db_index=True, auto_now_add=True)
category = models.ForeignKey('Category')
def __unicode__(self):
return '%s' % self.title
#permalink
def get_absolute_url(self):
return ('view_blog_post', None, { 'slug': self.slug })
def __str__(self):
return self.title
class Category(models.Model):
title = models.CharField(max_length=100, db_index=True)
slug = models.SlugField(max_length=100, db_index=True)
def __unicode__(self):
return '%s' % self.title
#permalink
def get_absolute_url(self):
return ('view_blog_category', None, { 'slug': self.slug })
def __str__(self):
return self.title
you should try this 'posts': Blog.objects.all() in views.py

Categories