The code below paginates the queryset but how can I paginate context['guser1']
class AuthorList(ListView):
template_name = 'authorList.html'
paginate_by = 10
queryset = Author.objects.order_by('date')
def get_context_data(self, **kwargs):
context = super(AuthorList, self).get_context_data(**kwargs)
context['guser1'] = Author.objects.order_by('date')
return context
You'll have to use a Paginator object like you would in a view. Here are the docs.
The paginator for the view can be accessed under the context variable page_obj in your template. The page number is passed as a GET parameter in the url, page. Here's a simple example:
{% if page_obj.has_previous %}
Previous page
{% endif %}
{% if page_obj.has_next %}
Next page
{% endif %}
Related
My html
{% if post.is_liked %}
<i class="fa fa-check" aria-hidden="true"></i>
{% else %}
<i class="fa fa-times" aria-hidden="true"></i>
{% endif %}
My views.py
class PostListView(ListView):
queryset = Post.objects.filter(created__range=['2020-03-01', '2020-03-31'])
template_name = 'main/problems.html'
context_object_name = 'posts'
ordering = ['-created']
def get_liked(self):
post = self.get_object()
user = get_object_or_404(User, username=post.kwargs.get('username'))
if post.likes.filter(username=user).exists():
post.annotate(is_liked=True)
else:
post.annotate(is_liked=False)
Even if I set both conditionals to return true my html does not read is_liked as true.
Your for loop will simply run when you construct the class, and furthermore you will simply define a function (multiple times), not execute the function. Finally note that this should alter the attribute of a Post, not just a general object.
You can annotate your queryset with:
from django.db.models import Exists, OuterRef
from app.models import Post, Like
class PostListView(ListView):
model = Post
template_name = 'main/problems.html'
context_object_name = 'posts'
ordering = ['-created']
def get_queryset(self, *args, **kwargs):
Post.objects.filter(
created__range=['2020-03-01', '2020-03-31']
).annotate(
is_liked=Exists(Like.objects.filter(
user_id=self.request.user.pk, post_id=OuterRef('pk')
))
)
The user_id and post_id might have different names, depending on how you constructed your Like model.
Then in the template, you can check the is_liked attribute of your Post objects:
{% for post in posts %}
{% if post.is_liked %}
…
{% else %}
…
{% endif %}
{% endfor %}
I'm still getting used to class based views, while I get the general purpose, some things still get past me. I'm following a tutorial which basically walks you through the motions but tends to ignore the fuzzy details, such as this piece of code:
class LoanedBooksByUserListView(LoginRequiredMixin,generic.ListView):
"""Generic class-based view listing books on loan to current user."""
model = BookInstance
template_name ='books/bookinstance_list_borrowed_user.html'
paginate_by = 1
def get_queryset(self):
return BookInstance.objects.filter(
borrower=self.request.user
).filter(status__exact='o').order_by('due_back')
I get the model, template_name and paginate_by parts, they're attributes of the ListView class, but what I don't get is the get_queryset part, where is it executed? As seen in the code below it's called nowhere. Where is it returned to? I guess my first question can be chalked down to "What do functions in class based views do?"
{% extends "base_generic.html" %}
{% block content %}
<h1>Borrowed books</h1>
{% if bookinstance_list %}
<ul>
{% for bookinst in bookinstance_list %}
<li class="{% if bookinst.is_overdue %}text-danger{% endif %}">
{{bookinst.book.title}} ({{ bookinst.due_back }})
</li>
{% endfor %}
</ul>
{% else %}
<p>There are no books borrowed.</p>
{% endif %}
So, two issues, first, where did the get_queryset return to, and second, what is
bookinstance_list? It's not a context variable, but seems to be used out of the blue, why is this variable usable?
Class Based Views calls get_queryset() in the get() method of your view, I'll present some example code from Django 1.11.
# django/views/generic/list.py:159
def get(self, request, *args, **kwargs):
self.object_list = self.get_queryset()
allow_empty = self.get_allow_empty()
...
context = self.get_context_data()
return self.render_to_response(context)
The ListView class sets self.object_list = self.get_queryset() in this method, however this doesn't explain where it sets it in the context passed to your template. If we take a quick look at get_context_data():
# django/views/generic/list.py:127
def get_context_data(self, **kwargs):
"""
Get the context for this view.
"""
queryset = kwargs.pop('object_list', self.object_list)
page_size = self.get_paginate_by(queryset)
context_object_name = self.get_context_object_name(queryset)
if page_size:
paginator, page, queryset, is_paginated = self.paginate_queryset(queryset, page_size)
context = {
'paginator': paginator,
'page_obj': page,
'is_paginated': is_paginated,
'object_list': queryset
}
else:
context = {
'paginator': None,
'page_obj': None,
'is_paginated': False,
'object_list': queryset
}
if context_object_name is not None:
context[context_object_name] = queryset
context.update(kwargs)
return super(MultipleObjectMixin, self).get_context_data(**context)
context is assigned a dictionary with 'object_list': queryset, so when you're trying to access the resulting QuerySet from get_queryset in your template you should access object_list.
The Django documentation on class-based generic views has a section on extending your context data with additional info. https://docs.djangoproject.com/en/2.1/topics/class-based-views/generic-display/#dynamic-filtering
I am trying to display data from a model inside my post.html which worked before but now dosen't for some reason I can't figure out.
urls.py
urlpatterns = [
path('', ListView.as_view(
queryset=Tutorials.objects.all().order_by("-date")[:25],
template_name="tutorials/blog.html"
)),
path('<int:pk>', DetailView.as_view(
model=Tutorials,
template_name="tutorials/post.html")),
]
blog.html
{% block python %}
{% for Tutorials in object_list %}
{% if Tutorials.categories == "pythonbasics" %}
<h1>{{ Tutorials.title }}</h1>
<br>
Created On : {{ Tutorials.date|date:"Y-m-d" }}
{% endif %}
{% endfor %}
{% endblock %}
post.html
{% block content %}
<h3>{{ Tutorials.title }}</h3>
<h6> on {{ Tutorials.datetime }}</h6>
<div class = "code">
{{ Tutorials.content|linebreaks }}
</div>
{% endblock %}
You should use object name in template:
{% block content %}
<h3>{{ object.title }}</h3>
<h6> on {{ object.datetime }}</h6>
<div class = "code">
{{ object.content|linebreaks }}
</div>
{% endblock %}
Or if you want to use Tutorial variable, you need to pass context_object_name=Tutorials to the view:
path('<int:pk>', DetailView.as_view(
model=Tutorials,
template_name="tutorials/post.html",
context_object_name='Tutorials')),
plus for #neverwalkaloner's answer.
The reason why you can use object or context_object_name is cause you inherit DetailView.
In DetailView, it has get_object() method.
def get_object(self, queryset=None):
"""
Return the object the view is displaying.
Require `self.queryset` and a `pk` or `slug` argument in the URLconf.
Subclasses can override this to return any object.
"""
# Use a custom queryset if provided; this is required for subclasses
# like DateDetailView
if queryset is None:
queryset = self.get_queryset()
# Next, try looking up by primary key.
pk = self.kwargs.get(self.pk_url_kwarg)
slug = self.kwargs.get(self.slug_url_kwarg)
if pk is not None:
queryset = queryset.filter(pk=pk)
# Next, try looking up by slug.
if slug is not None and (pk is None or self.query_pk_and_slug):
slug_field = self.get_slug_field()
queryset = queryset.filter(**{slug_field: slug})
# If none of those are defined, it's an error.
if pk is None and slug is None:
raise AttributeError("Generic detail view %s must be called with "
"either an object pk or a slug."
% self.__class__.__name__)
try:
# Get the single item from the filtered queryset
obj = queryset.get()
except queryset.model.DoesNotExist:
raise Http404(_("No %(verbose_name)s found matching the query") %
{'verbose_name': queryset.model._meta.verbose_name})
return obj
It will return model object, and also you can override this method.
And in get() method in DetailView,
def get(self, request, *args, **kwargs):
self.object = self.get_object()
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
As you see, it define self.object to self.get_object() so you can use self.object inside DetailView.
Finally, you can use object in your template because of get_context_data().
DetailView basically add 'object' to context, so you can use it.
def get_context_data(self, **kwargs):
"""Insert the single object into the context dict."""
context = {}
if self.object:
context['object'] = self.object
context_object_name = self.get_context_object_name(self.object)
if context_object_name:
context[context_object_name] = self.object
context.update(kwargs)
return super().get_context_data(**context)
It's true that CBV has little bit learning curve, but when you see django source code and read about docs, it's easy to follow.
+I highly recommend ccbv.co.kr - you can see which view and mixin that CBV inherit, and it's methods too.
{% block content %}
{{ object.title }}
<h6> on {{ object.datetime }}</h6>
<div class = "code">
{{ object.content|linebreak }}
</div>
{% endblock %}
My URL
/tags/{slug}
points to this view:
class TagsDetailList(request, ListView):
queryset = Link.objects.filter(tags__name__in=['request.slug'])
template_name = "links/tags_detail_list.html"
So i have the request.slug object in the url.
Now i want to make a ListView that filters Link.objects by request.slug and respond to given template with the queried result.
All works but no queries are given on my template.
response template is:
{% extends "base.html" %}
{% block content %}
<h2>Tags Detail List</h2>
<ul>
{% if link in object_list %}
{% for link in object_list %}
<li>
{{ link.title }}
</li>
{% endfor %}
{% else %}
<p> Error: No Links in queryset! </p>
{% endif %}
<ul>
{% endblock %}
i dont get some elements, only the error message. its something bad on the request on my view.
who can help me and give me a hint how i can retrieve the request slug on my view?
EDIT:
nice solutions. i learned the way kwargs work(a small part).
But on the template i get still the error for no queryset. Tried both answers and also changed a bit but never really worked. Any hint what cause this?
If your urls pattern something like this:
r'^list/(?P<slug>[\w,\*]+)?$'
So in the views should be:
class TagsDetailList(ListView):
model = Link
template_name = "links/tags_detail_list.html"
def get_queryset(self):
qs = self.model.objects.all()
if self.kwargs.get('slug'):
qs = qs.filter(tags__name=self.kwargs['slug'])
return qs
What you've done here doesn't make sense: you're just asking for tags whose names are in the list consisting of the literal text " request.slug".
You need to override get_queryset so that it queries on the actual value of the slug, which is in self.kwargs.
def get_queryset(self, *args, **kwargs):
return Link.objects.filter(tags__name=self.kwargs ['slug'])
Also, I don't know what that if statement is doing in your template, but you haven't defined "link" do it will never evaluate to true, so no links will show.
Maybe it will help, but it works for me in Django 2+:
Your urls.py:
...
path('tags/<slug>/', TagsDetailList.as_view(), name='tags_detail'),
...
Your views.py:
...
class TagsDetailList(ListView):
model = Link
template_name = 'links/tags_detail_list.html'
def get_queryset(self, *args, **kwargs):
return Link.objects.filter(tags__slug=self.kwargs['slug'])
...
i am trying to add a search bar on my listview page. it will get all the post items first time, and if a query put in search box, and submitted, it will return a filtered queryset. it renders fine, but only has problem with pagination. for non-filtered queryset, the next page will get the next two posts without any problem, but for filtered queryset, i can see the queryset is correctly reflected by see less pages, but the next page gets me the second page of non-filtered queryset not the filtered queryset. Can anyone give me some pointers on what i am doing wrong here. Thanks
My template looks like this:
{% block content %}
.....
<form action="" method="get">
<input type="text" name='q'>
<input type="submit" value="Search">
</form>
{% for post in object_list %}
....
{% endfor %}
{% if is_paginated %}
<div class="pagination">
<span class="page-links">
{% if page_obj.has_previous %}
<<
{% endif %}
<span class="page-current">
Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
</span>
{% if page_obj.has_next %}
>>
{% endif %}
</span>
</div>
{% endif %}
I have a listview like below.
class Postlist(ListView):
model=post
paginate_by = 2
query_string = ''
def get_queryset(self):
if ('q' in self.request.GET) and self.request.GET['q'].strip():
query_string = self.request.GET['q']
entry_query = get_query(query_string, ['title', 'body',]) ## call get_query() function
queryset = post.objects.filter(entry_query).order_by('-created')
else:
queryset=post.objects.all().order_by('-created')
return queryset
def get_context_data(self):
context = super(ListView, self).get_context_data(**kwargs)
context['q'] = query_string
## do sth here to pre-populate the input text box
return context
Let me close this. Two options: using hidden field to save the user search terms, and get this to filter off queryset on each request; or using session. Here is the session code sample. The only drawback i could think of using session cache is it caches a whole queryset in memory, so it will kill the memory for high traffic website. Since i am using it for personal website, it doesnt hurt at all.
View file
class Postlist(ListView):
model=post
paginate_by = 2
def get_queryset(self):
query_string = ''
if ('search' in self.request.GET) and self.request.GET['search'].strip():
query_string = self.request.GET['search']
entry_query = get_query(query_string, ['title', 'body',]) ## call get_query to clean search terms
queryset = post.objects.filter(entry_query).order_by('-created')
self.request.session['searchset']=queryset
else:
if self.request.session.get('searchset') and ('page' in self.request.GET):
queryset=self.request.session['searchset']
else:
queryset=post.objects.all().order_by('-created')
if self.request.session.get('searchset'):
del self.request.session['searchset']
return queryset
form file
from django import forms
class SearchForm(forms.Form):
search = forms.CharField(max_length=30)