Django: Query Selected Info in Model Within Class Based Views - python

I created some link with model-slug. But When I click my link, go to the page but return the empty value. I want to When I click any link, It will query value from the selected field like class_name or slug field.
this class list html page
{% extends "result/base.html" %}
{% block title %}Class List Name{% endblock title %} {% block content %}
<div class="row">
<div class="col-md-12 col-xs-offset-4">
<h2>Class List</h2>
{% for object in object_list %}
{{object.class_name}}
{% endfor %}
</div>
</div>
{% endblock %}
this is models.py file
class ClassName(models.Model):
class_name=models.CharField('Class Name', max_length=10)
class_added=models.DateTimeField(auto_now_add=True)
class_updated=models.DateTimeField(auto_now=True)
slug=models.SlugField(max_length=200, unique=True)
def __str__(self):
return self.class_name
this is views.py file
class ClassListView(ListView):
model=ClassName
slug_field = 'slug'
template_name='result/class_list.html'
class ClassDetailView(DetailView):
model=ClassName
slug_field = 'slug'
template_name='result/class_detail.html'
def get_context_data(self,*args, **kwargs):
context = super(ClassDetailView, self).get_context_data(*args,**kwargs)
context['class_wise_std'] = StudentInfo.objects.filter(
student_class_name__class_name__startswith=self.model.slug)
return context
this is class details html page
{% extends "result/base.html" %}
{% block title %}Class Detail's List{% endblock title %}
{% block content %}
<div class="row">
{% for object in class_wise_std %}
<div class="col-lg-12 col-sm-offset-4" style="margin:20px 10px">
<p>Name: {{object.student_name}}</p>
<p>Class: {{object.student_class_name}}</p>
<p>Father's Name: {{object.student_father_name}}</p>
<p>Mother's Name: {{object.student_mother_name}}</p>
<p>Roll: {{object.student_roll}}</p>
</div>
{% endfor %}
</div>
{% endblock content %}
this code
def get_context_data(self,*args, **kwargs):
context = super(ClassDetailView, self).get_context_data(*args,**kwargs)
context['class_wise_std'] = StudentInfo.objects.filter(
student_class_name__class_name__startswith=self.model.slug)
return context
I found this problem happen with this code. I want to filter with class_name field in ClassName Model.
StudentInfo.objects.filter(student_class_name__class_name__startswith=self.model.slug)
But I can successfully query my targeted info this way.
StudentInfo.objects.filter(student_class_name__class_name__startswith='One')
But It is not an efficient way. Now, How can I implement dynamically this?

Related

Nothing renders on Django Template

building a news aggregator. I am collecting reddit and twitter posts using their APIs, and then I create a model object for each post, which is stored in my database. I'm then passing in these post objects as context into my template, looping through the context in the template with the hope to display the posts 'html' attribute (A model field I created) onto the page, which in turn embeds the post onto the screen.
However, I can't figure out why my template page is still blank. No errors are thrown, and the model objects are being created because I can see them in the admin panel. I'll provide my models.py, views.py, and template to be taken a glance at. I appreciate and am grateful for any help/advice.
models.py
class Post(models.Model):
post_type = models.CharField(
max_length=20, null=True, blank=True)
root_url = models.CharField(max_length=200, default="")
html = models.TextField(default="")
created_at = models.DateTimeField(auto_now_add=True)
views.py
def main(request):
all_posts = Post.objects.all
context = {'posts': all_posts}
return render(request, "test.html", context)
template
{% block content %} {% autoescape off %}
<div class="container">
<div class="row">
<div class="col-6">
<h3 class='text-center'>Twitter News</h3>
{% for post in posts %}
{% if post.post_type == 'twitter' %}
<div class="mdl-card__media" id="timeline"></div>
{{ post.html }}
{% endif %}
<br>
{% endfor %}
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
</div>
<div class="col-6">
<h3 class='text-center'>Reddit News</h3>
{% for post in posts %}
{% if post.post_type == 'reddit' %}
<div class="mdl-card__media" id="timeline"></div>
{{ post.html }}
{% endif %}
<br>
{% endfor %}
<script async src="//embed.redditmedia.com/widgets/platform.js" charset="UTF-8"></script>
</div>
</div>
</div>
{% endautoescape %}{% endblock %}
<script async src="//embed.redditmedia.com/widgets/platform.js" charset="UTF-8"></script>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.0.0-beta3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-JEW9xMcG8R+pH31jmWH6WWP0WintQrMb4s7ZOdauHnUtxwoG2vI5DkLtS3qm9Ekf" crossorigin="anonymous"></script>
In your views.py, your attempt to create a queryset is missing the parenthesis on the all method:
all_posts = Post.objects.all()
You have mentioned in the comments that the <br> tags within the {% for %} loops are being rendered. This would indicate that when you check for {% if post.post_type == 'twitter' %} (and the equivalent for reddit), there are no matches.
Check your Post model in Django admin to ensure you have records with post_type values that equal 'twitter' and 'reddit'.

Django: can't access OneToOneField after rendering TemplateView Form

I am new to Django and don't understand what really is causing this:
I have a Model Company which has an OneToOneField, creator.
# models.py
class Company(models.Model):
class Meta:
verbose_name = 'Company'
verbose_name_plural = 'Companies'
creator = models.OneToOneField(User, related_name="company", on_delete=models.CASCADE, unique=False, null=True)
name = models.CharField(max_length=50)
I have a TemplateView class to handle get and post requests for creating a Company model:
# views.py
class create_company(TemplateView):
def get(self, request):
form = CompanyCreateForm()
title = "Some form"
return render(request, "form.html", {"form": form, "title": title})
def post(self, request):
form = CompanyCreateForm(request.POST)
if form.is_valid():
comp = form.save(commit=False)
comp.creator = request.user
comp.save()
return redirect('index')
The form is showing correctly also storing when I submit, the problem I am facing is with base.html where I show {% user.company %}; the form template extends it like:
{% extends "account/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<form method="post" action="">
{% csrf_token %}
{{form|crispy}}
<button class="btn btn-success" type="submit">Save</button>
</form>
<br>
</div>
<br>
{% endblock %}
and in base.html I access
{% if user.is_authenticated %}
{% user.company %}
{% endif %}
But user.company is not showing even it is set; it shows only when I redirect to index but not when I render the form.
Can someone help me understand what causes this?
{% if request.user.is_authenticated %}
{% request.user.company %}
{% endif %}
you are not sending any context to the base.html, thus only user wont work.
This was the error when I simulated your code.
Error during template rendering
In template /home/user/django/drf_tutorial/snippets/templates/base.html, error at line 2
Invalid block tag on line 2: 'user.company', expected 'elif', 'else' or 'endif'. Did you forget to register or load this tag?
1 {% if user.is_authenticated %}
2 {% user.company %}
3 {% endif %}
4 {% block content %}{% endblock %}
It gives hint that the code to show company should be variable {{ }} instead of tag {% %}. So the base.html template should be as below.
{% if user.is_authenticated %}
{{ user.company }}
{% endif %}
{% block content %}{% endblock %}

How do I add pagination to my index.html ?

Here are my codes(it works fine):
#views.py
class IndexView(generic.ListView):
template_name = 'index.html'
context_object_name = 'home_list'
queryset = Song.objects.all()
paginate_by = 1
def get_context_data(self, **kwargs):
context = super(IndexView, self).get_context_data(**kwargs)
context['all_artists']=Artist.objects.all()
context['all_songs']=Song.objects.all()
context['all_albums']=Album.objects.all()
return context
base.html(which is extended by index.html):
#base.html
{% block content %}{% endblock %}
{% block pagination %}
{% if is_paginated %}
<div class="pagination">
<span class="page-links">
{% if page_obj.has_previous %}
Previous
{% endif %}
<span class="page-current">
Page {{page_obj.number}} of {{page_obj.paginator.num_pages }}
</span>
{% if page_obj.has_next %}
Next
{% endif %}
</span>
</div>
{% endif %}
{% endblock %}
And my index.html:
{% extends 'base_generic.html' %}
{% block title %}<title>Listen to songs </title>{% endblock %}
{% block content %}
<h3>Best Songs</h3>
{% for song in all_songs %}
<ol>
<li>{{song.song_title}} <img src="{{song.song_logo}}" heigt=112, width=114/> <br></li>
</ol>
{% endfor %}
<h3>Best Albums</h3>
{% for album in all_albums %}
<ul>
<li title="{{album.album_title}}">
<img id="img_{{album.id}}" src="{{album.album_logo}}" heigt=112, width=114 />
<p>{{album.album_title}}</p>
</li>
</ul>
{% endfor %}
{% endblock %}
So when I compiled this, I got this window :
Image here
But in all pages, it stays the same.What I want is to display 1 song per page.Help guys !!!! :] :] :]
You never use your paginated objects, instead you've made a separate context variable called all_songs.
Simply just use the right context data
{% for song in all_songs %}
should be
{% for song in home_list %}
You may want to apply pagination for your other querysets too although it can get confusing paginating more than one list
Have a look here: https://www.youtube.com/watch?v=q-Pw7Le30qQ
The video explains pagination.
Alternative: https://docs.djangoproject.com/en/1.10/topics/pagination/
If you only want to display one song there is always the option to use a DetailView, which will only show one item.
Here is a stackoverflow question that describes the process for class based views: How do I use pagination with Django class based generic ListViews?
In your example: you don't have to set the queryset. Remove queryset = ### and add model = #YOURMODELNAME#.
If you want to overwrite the queryset you should do it in def get_queryset() which is a function of ListView. Like this:
class SongView(ListView):
model = Song
template_name = 'template_name'
def get_queryset():
queryset = super(SongView, self).get_queryset(**kwargs)
queryset = #aditional filters, ordering, whatever#
return queryset

Django Category Model and Template View Issue

My question is two phased but it's from the same Django Model. I am trying to get my Category model to work properly with my Petition model. Currently,
I get this 'FieldError' error when I define a queryset for my 'CategoryView' class:
Cannot resolve keyword 'created_on' into field. Choices are: description, id, petition, slug, title
Here is the code for the 'CategoryView' class:
class CategoryView(generic.ListView):
model = Category
template_name = 'petition/category.html'
context_object_name = 'category_list'
def get_queryset(self):
return Category.objects.order_by('-created_on')[:10]
Here is the code in my models.py:
class Category(models.Model):
title = models.CharField(max_length=90, default="Select an appropriate category")
slug = models.SlugField(max_length=200, unique=True)
description = models.TextField(null=False, blank=False)
class Meta:
verbose_name_plural = "Categories"
def __str__(self):
return self.title
def get_absolute_url(self):
return "/categories/%s/"%self.slug
class Petition(models.Model):
title = models.CharField(max_length= 90, default="Enter petition title here")
created_on = models.DateTimeField(auto_now_add=True)
image = models.ImageField(null=False, upload_to=imageupload)
video = models.CharField(max_length=600, default="Enter an external video link")
petition = models.TextField(null=False, default="Type your petition here")
created_by = models.ForeignKey(User)
category = models.ManyToManyField(Category)
def total_likes(self):
return self.like_set.count()
def __str__(self):
return self.title[:50]
def get_signatures(self):
return self.signature_set.all()
I get the 'FieldError' on my category view template (category.html) when the 'get_queryset()' has been defined.
When I comment it out, the page displays but posts are not retrieved; I get a list of the categories instead. Here is my category view template(category.html):
{% include 'layout/header.html' %}
{% load humanize %}
<div class="container content">
<div class="row">
<div class="col-md-8 post-area">
{% if category_list %}
{% for petition in category_list %}
<div class="petition-block">
<h2 class="home-title">{{petition.title}}</h2>
<span class="petition-meta">
Created {{petition.created_on|naturaltime}} by
{% if petition.created_by == user %}
You
{% else %}
#{{ petition.created_by }}
{% endif %}
{% if petition.created_by == user %}
Edit
{% endif %}
{% if petition.created_by == user %}
Delete
{% endif %}
</span>
{% if petition.image %}
<img src="{{ petition.image.url }}" alt="petition image" class="img-responsive" />
{% endif %}
</div><!--//petition-block-->
{% endfor %}
{% else %}
<p>Sorry, there are no posts in the database</p>
{% endif %}
</div>
<div class="col-md-4">
<h3>Topics</h3>
<ul>
{% for petition in category_list %}
<li>{{petition.title}}</li>
{% endfor %}
</ul>
</div>
</body>
</html>
What am I doing wrong? Please help.
That's because you are trying to order the Category's queryset by created_on and your Category model doesn't have any field called created_on. That field is within Petition model. If you want to order the categories by petition's created_on you should do a "join" between tables, but with Django's ORM is easy.
For simplicity on your code is better to name the relation
class Petition(models.Model):
...
categories = models.ManyToManyField(Category, related_name='petitions')
...
Now you can refer to the category's petitions as 'petitions'
Category.objects.order_by('-petitions__created_on')
For use the petitions within the template the best way is prefetch the petitions on the queryset to avoid hitting the database every time in the for loop.
Category.objects.prefetch_related('petitions').order_by('-petitions__created_on')
That would bring every petition for each category. So in your template you can use:
{% for category in category_list %}
{% for petition in category.petitions.all %}
...
#{{ petition.created_by }}
...
{% end for %}
{% end for %}

is_paginated not working for django Generic Views

I've been using django built-in pagination (is_paginated) in few of my pages. They are all working fine. Except for the search page where the pagination should only appear based on the filtered queryset.
I've checked through few other thread but it ain't helping much.
How do I use pagination with Django class based generic ListViews?
Django template tag exception
Here's a mini version of what I have so far:-
1)views.py
class SearchBookView(ListView):
template_name = 'books/search_book.html'
paginate_by = '2'
context_object_name = 'book'
form_class = SearchBookForm
def get(self, request):
form = self.form_class(request.GET or None)
if form.is_valid():
filtered_books = self.get_queryset(form)
context = {
'form' : form,
'book' : filtered_books,
}
else:
context = {'form': form}
return render(request, self.template_name, context)
def get_queryset(self, form):
filtered_books = Book.objects.all()
if form.cleaned_data['title'] != "":
filtered_books = filtered_books.filter(
title__icontains=form.cleaned_data['title'])
return filtered_books
def get_context_data(self):
context = super(SearchBookView, self).get_context_data()
return context
2) search_book.html (template)
{% crispy form %}
{% if book %}
<p>Found {{ book|length }} book{{ book|pluralize }}.</p>
{% for book in book %}
<div class="card">
<div style="height:170px; border:solid #111111;" class="col-md-3">
Ima
</div>
<div class="whole-card col-md-9">
<div class="title">"{{ book.title }}"</div>
<div>{{ book.description }}</div>
Read More
</div>
</div>
{% endfor %}
{% else %}
<p>No book matched your searching criteria.</p>
{% endif %}
{% if is_paginated %}
<div class="pagination">
<span class="page-links">
{% if page_obj.has_previous %}
previous
{% endif %}
<span class="page-current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
</span>
{% if page_obj.has_next %}
next
{% endif %}
</span>
</div>
{% endif %}
forms.py
class SearchBookForm(forms.Form):
title = forms.CharField(max_length=20)
def __init__(self, *args, **kwargs):
self.helper = FormHelper()
self.helper.add_input(Submit('search', 'Search', css_class='btn'))
self.helper.form_method = 'GET'
self.helper.layout = Layout('title')
super(SearchBookForm, self).__init__(*args, **kwargs)
------------------UPDATE------------------
Though I understand Daniel Roseman's answer but as I am fairly new to django, I am not sure how to implement the whole thing, hitting plenty of "X not accessible, X is not attribute of Y" and etc. After much digging, I found some other useful posts on this same matter.
Django: Search form in Class Based ListView
Updating context data in FormView form_valid method?
Django CBV: Easy access to url parameters in get_context_data()?
Django class based view ListView with form
URL-parameters and logic in Django class-based views (TemplateView)
Another problem I encounter is I am unable to access the parameters in URL using self.kwargs as what suggested in most of the posts. In the final link I posted above, Ngenator mentioned that URL parameters has to be accessed using request.GET.get('parameter'). I used that and it's working fine for me.
By combining everything, here's the revised piece of coding I have. Just in case anyone is having the same problem as me.
1) views.py
class SearchBookView(ListView):
template_name = 'books/search_book.html'
paginate_by = '3'
context_object_name = 'book_found'
form_class = SearchBookForm
model = Book
def get_queryset(self):
object_list = self.model.objects.all()
title = self.request.GET.get('title', None)
if title is not None and title != "":
object_list = object_list.filter(title__icontains=title)
else:
object_list = []
return object_list
def get_context_data(self):
context = super(SearchBookView, self).get_context_data()
form = self.form_class(self.request.GET or None)
context.update({
'form': form,
})
return context
2) search_book.html (template)
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% load staticfiles %}
{% load bootstrap_pagination %}
{% block title %}Search Page{% endblock %}
{% block content %}
<div class="container">
{% if form.errors %}
<p style="color: red;">
Please correct the error{{ form.errors|pluralize }} below.
</p>
{% endif %}
{% crispy form %}
{% if book_found %}
<p>Found {{ paginator.count }} book{{ book_found_no|pluralize }}.</p>
{% for book in book_found %}
<div class="wholecard">
<div style="height:170px; border:solid #111111;" class="col-md-3">
Image
</div>
<div class="card col-md-9">
<div class="card-title">"{{ book.title }}"</div>
<div>{{ book.description }}</div>
Read More
</div>
</div>
{% endfor %}
{% else %}
<p>No book matched your searching criteria.</p>
{% endif %}
{% bootstrap_paginate page_obj %}
</div>
{% endblock %}
And I ended up using jmcclell's bootstrap-pagination also for pagination. Saved me lots of time! Good stuff...
You've specifically overridden the get method so that it defines its own context, and never calls the default methods, so naturally none of the default context bars are available.
Don't do that; you should almost never be overriding the get and post methods. You should probably move all the form stuff directly into get_queryset.
It's working
views.py
class UserListView(ListView):
model = User
template_name = 'user_list.html'
context_object_name = 'users'
paginate_by = 10
def get_queryset(self):
return User.objects.all()
templates/user_list.html
{% if is_paginated %}
<nav aria-label="Page navigation conatiner">
<ul class="pagination justify-content-center">
{% if page_obj.has_previous %}
<li>« PREV </li>
{% else %}
<li class="disabled page-item"><a class="page-link">PREV !</a></li>
{% endif %}
{% for i in %}
{{ i }}
{% endfor %}
{% if page_obj.has_next %}
<li> NEXT »</li>
{% else %}
<li class="disabled page-item"><a class="page-link">NEXT !</a></li>
{% endif %}
</ul>
</nav>
</div>
{% endif %}

Categories

Resources