Django how to pass user object in class based view pagenation? - django

The default class based view pagination returning all object. I have an my account page for my each individual user where I need to show pagination number. By default pagination is same for all user. Let you explain little bit. If user A have 10 items and I listed 2 items per page then user A will be see total 5 page in his account which is fine but user B haven't any items and he is currently seeing only pagination number like page1,page2,page3... in his account because the current pagination returning all items from database. here is my code:
views.py
class BlogMyAccount(ListView):
paginate_by = 10
model = Blog
template_name = 'blog/my-account.html'
ordering = ['-id']
#html
{% for blog in object_list %}
{% if user.id == blog.author.id %}
....my code
{% endif %}
{% endfor %}
<!---pagination code---->
<ul class="pagination justify-content-center mb-4">
{% if page_obj.has_previous %}
<li class="page-item"><a class="page-link" href="?page=1">First Page</a></li>
<li class="page-item"><a class="page-link" href="?page={{ page_obj.previous_page_number }}">← Back</a></li>
{% endif %}
{% if page_obj.has_next %}
<li class="page-item"><a class="page-link" href="?page={{ page_obj.next_page_number }}">Next Page →</a></li>
{% endif %}
{% for i in paginator.page_range %}
{% if page_obj.number == i %}
<li class="page-item"><a class="page-link" href="#!">{{ i }}</a></li>
{% elif i > page_obj.number|add:'-3' and i < page_obj.number|add:'3' %}
<li class="page-item"><a class="page-link" href="?page={{ i }}">{{ i }}</a></li>
{%endif%}
{% endfor %}
<li class="page-item"><a class="page-link" href="?page={{ page_obj.paginator.num_pages }}">Last Page</a></li>
</ul>
in function based view I am passing user object like this in pagination:
notifications = Notifications.objects.all().filter(user=user).order_by('-date')
paginator = Paginator(notifications, 20) # Show 25 contacts per page.
I tried to apply same method by using context in my classed view but didn't work. Pagination is same for all user in class based view.

You need to filter the queryset the same way you did in FBV. Override get_queryset() to only return objects associated with your requesting user.
class BlogMyAccount(ListView):
paginate_by = 10
model = Blog
template_name = 'blog/my-account.html'
ordering = ['-id']
def get_queryset(self):
queryset = Blog.objects.filter(author=self.request.user)
return queryset
Also you dont need to call .all() on Notifications.objects, it is already a queryset, Notifications.objects.filter() is okay.
If possible, move ordering to the models.py file, as in:
class Meta:
ordering = ['-id']

You can override the get_querset method, and by using a super() call, let Django do the .order_by clause for us:
from django.contrib.auth.mixins import LoginRequiredMixin
class BlogMyAccount(LoginRequiredMixin, ListView):
paginate_by = 10
model = Blog
template_name = 'blog/my-account.html'
ordering = ['-id']
def get_queryset(self, *args, **kwargs):
# &downarrow; will take care of the ordering
return super().get_querset(*args, **kwargs).filter(
author=self.request.user
)
Note: You can limit views to a class-based view to authenticated users with the
LoginRequiredMixin mixin [Django-doc].

Related

In Dajngo, Class based views does not render HTML in template-Is context missing?

``I started to work with class based views, but the codes that I've written initially does not render in the template when I use class based views. The data that is in main_c.main_categories.url, main_c.name (see template) simply does not show up when I use class based views.
When I change back to function based views and refresh the page, the HTML code in the template shows up without any problems. Could someone explain what causes this error?
I've read something about context not been found by the template but I don't really understand what it means and how context resolves the issue.
Thanks in advance!`
views.py
class MainCategoryDeleteView(DeleteView):
model = MainCategory
success_url = reverse_lazy("main_category")
template_name = 'admin_dashboard/categories/maincategory_confirm_delete.html'
class MainCategoryUpdateView(UpdateView):
model = MainCategory
fields = '__all__'
success_url = reverse_lazy("main_category")
template_name = 'admin_dashboard/categories/main_category_edit.html'
class MainCategoryCreateView(CreateView):
model = MainCategory
fields = '__all__'
success_url = reverse_lazy("main_category")
template_name = 'admin_dashboard/categories/main_category.html'
def get_context_data(self, **kwargs):
context = super(MainCategoryCreateView, self).get_context_data(**kwargs)
context['main_categories'] = MainCategory.objects.all()
print(MainCategory.objects.all())
return context
urls.py
path('BaitSouq/MainCategory/', views.MainCategoryCreateView.as_view(), name="main_category"),
path('BaitSouq/MainCategory/<int:pk>/Delete/', views.MainCategoryDeleteView.as_view(), name="main_category_delete"),
path('BaitSouq/MainCategory/<int:pk>/Change/', views.MainCategoryUpdateView.as_view(), name="main_category_update"),
templates
<div class="col-lg-2 d-none d-lg-flex">
<div class="categories-dropdown-wrap style-2 mt-30">
<div class="d-flex categori-dropdown-inner" style="font-size: x-small">
<ul>
{% for main_c in main_categories %}
<li>
<a href="#"> <img
src="{{ main_c.main_categories.url }}"
alt=""/>{{ main_c.name }}</a>
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
This is Template folder
In your template you have:
{% for main_c in main_categories %}
...
src="{{ main_c.main_categories.url }}"
...
{% endfor %}
As you havent't given that model I guess it should be shorter: {{ main_c.url }}.

Accessing additional fields from a grouper in a Django group_by

How can I access fields other than the grouper in a Django group_by function?
class dateEvent(models.Model):
event = models.ForeignKey('Event', on_delete=models.CASCADE)
start_date_time = models.DateTimeField(auto_now=False, auto_now_add=False)
def __str__(self):
return "%s" % (self.event.title)
def description(self):
return "%s" % (self.event.description)
class Event(models.Model):
description = RichTextUploadingField(max_length=200)
view:
def my_view(request):
events = dateEvent.objects.all()
context = {
'events': events,
}
return render(request, 'view.html', context)
template:
<ul>
{% for event in dateEvents_list %}
<li><h5>Event: {{ event.grouper }}</h5>
<h6>Description: {{ event.description }}</h6> #How can access the description of the main event?
<ul>
{% for dateEvent in event.list %}
<li>date: {{ dateEvent.start_date_time }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
I'd like to have the title, which is the grouper so it's fine, but also the description.
You can also access your grouper's list objects to get details about the "first record" in your group. So using:
event.grouper.list.0.<any_model_field_you_want>
Will work just fine. I have this in production and it solved my needs immediatly. I found this in a rejected feature request for Django here: https://code.djangoproject.com/ticket/13452
If you need to traverse your model's relationship tree you can do the following.
event.grouper.list.0.<related_model_name>.<any_model_field_you_want>
You can access model attributes from the grouper directly using event.grouper.<any-model-attribute>.
In your example here is how it would look:
<ul>
{% for event in dateEvents_list %}
<li><h5>Event: {{ event.grouper }}</h5>
<h6>Description: {{ event.grouper.description }}</h6>
<ul>
{% for dateEvent in event.list %}
<li>date: {{ dateEvent.start_date_time }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
I can't find any documentation around this usage.

Django: Access num_pages in view to generate pagination

I want to generate a range of pages in my template when using a ListView and it's pagination, to generate dynamically the number of pages for the pagination:
https://getbootstrap.com/docs/4.0/components/pagination/
My first attempt was to make a for loop for everyelement in page_obj.paginator.num_pages, getting error int is not iterable:
{% if is_paginated %}
<ul class="pagination">
{% for i in page_obj.paginator.num_pages %}
<li class="page-item">
<span class="page-link">
<a href="/catalogo?page={{i}}">
<span class="sr-only">(current)</span>
</span>
</li>
{% endfor %}
</ul>
{% endif }
Then I've discovered that there isn't and wont be a range template filter because this calculation should be done in the view and send the range to the template, not generated in the template. See:
https://code.djangoproject.com/ticket/13088
So how can I access the page_obj.paginator.num_pages inside the
view???
My LisView:
class CatalogoListView(ListView):
model = UnitaryProduct
template_name = "shop/catalogo.html"
paginate_by = 10
def get_queryset(self):
filter_val = self.request.GET.get('filtro', 'todas')
order = self.request.GET.get('orderby', 'created')
if filter_val == "todas":
context = UnitaryProduct.objects.all().filter(available=True).order_by('-created')
return context
else:
context = UnitaryProduct.objects.filter(
subcategory2=filter_val,
).filter(available=True).order_by('-created')
return context
def get_context_data(self, **kwargs):
context = super(CatalogoListView, self).get_context_data(**kwargs)
context['filtro'] = self.request.GET.get('filtro', 'todas')
context['orderby'] = self.request.GET.get('orderby', 'created')
context['category'] = Category.objects.get(slug="catalogo")
return context
num_pages is an integer storing the total number of pages, thus not iterable.
What you are looking for is page_range, which is a list of page numbers.
You can iterate on it from your template, just replace
{% for i in page_obj.paginator.num_pages %}
with
{% for i in page_obj.paginator.page_range %}

Slicing pagination in ListView?

I have successfully added pagination to a list_contacts view (obviously for a Contact model). And am now trying to setup the template context so that I can limit the pagination display to look like this :
< 1 ... 5 6 7 8 9 ... 42 >
I went from this answer and tried to use a "paginator" template tag, unsuccessfully. The main problem is that I'm using ListView instead of the old object_list, and these two don't set the context the same way.
Here's my view :
from django.views.generic import ListView
from app.models import Contact
list_contacts = ListView.as_view(
model=Contact,
http_method_names = ['get'],
template_name='contacts.html',
context_object_name='contacts',
paginate_by=6
)
And my template "contacts.html" :
<ul class="pagination">
{# Previous page link #}
{% if page_obj.has_previous %}
<li>
«
</li>
{% else %}
<li class="disabled">
«
</li>
{% endif %}
{# First page #}
{% if show_first %}
<li>
1
</li>
<li>...</li>
{% endif %}
{# List of pages (with current "active") #}
{% for page in page_numbers %}
{% ifequal page page_obj.number %}
<li class="active">
{{ page }}
</li>
{% else %}
<li>
{{ page }}
</li>
{% endifequal %}
{% endfor %}
{# Last page #}
{% if show_last %}
<li>...</li>
<li>{{ page_obj.pages }}</li>
{% endif %}
{# Next page link #}
{% if page_obj.has_next %}
<li>
»
</li>
{% else %}
<li class="disabled">
»
</li>
{% endif %}
</ul>
EDIT: here's my view code that ended up working
class ListContactsView(ListView):
model = Contact
http_method_names = ['get']
template_name = 'contacts.html'
context_object_name = 'contacts'
paginate_by = 6
def get_context_data(self, **kwargs):
_super = super(ListContactsView, self)
context = _super.get_context_data(**kwargs)
adjacent_pages = 2
page_number = context['page_obj'].number
num_pages = context['paginator'].num_pages
startPage = max(page_number - adjacent_pages, 1)
if startPage <= 3:
startPage = 1
endPage = page_number + adjacent_pages + 1
if endPage >= num_pages - 1:
endPage = num_pages + 1
page_numbers = [n for n in xrange(startPage, endPage) \
if n > 0 and n <= num_pages]
context.update({
'page_numbers': page_numbers,
'show_first': 1 not in page_numbers,
'show_last': num_pages not in page_numbers,
})
return context
list_contacts = ListContactsView.as_view()
There are different ways to achieve this (or even a combination of some):
Override or extend get_context_data by calling super
Write a templatetag
Write a template filter
Override or extend the PaginationMixin
An example of the first
class MyListView(ListView):
def get_context_data(self, **kwargs):
context = super(MyListView, self).get_context_data(**kwargs)
context['foo'] = 'bar'
return context

Django class based views with url issue

I'm trying to get to grips with class based views.
I have urls.py as follows:
urlpatterns = patterns('homework.views',
(r'^index/$', 'index'),
url(r'^(?P<sub_slug>\w+)/$', NavListView.as_view(), name='nav'),
url(r'^(?P<sub_slug>\w+)/(?P<class_grp_slug>\w+)/$', SubNavListView.as_view(), name='subnav'),
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
url(r'^admin/', include(admin.site.urls)),)
I have my views.py:
# Subject navigation
class NavListView(ListView):
template_name = 'templates/home.html'
def get_queryset(self):
self.subject = Subject.objects.all()
return self.subject
def get_context_data(self, **kwargs):
context = super(NavListView, self).get_context_data(**kwargs)
context['subjects'] = self.subject
return context
# Class group navigation
class SubNavListView(NavListView):
def get_queryset(self):
self.group = Group.objects.filter(subject__name__iexact=self.kwargs['sub_slug'])
return self.group
def get_context_data(self, **kwargs):
context = super(NavListView, self).get_context_data(**kwargs)
context['groups'] = self.group
return context
In my 'templates/home.html' I have:
{% extends 'templates/base.html' %}
{% load url from future %}
{% block nav-menu-items %}
<ul class="nav">
{% for sub in subjects %}
<li class="">{{ sub }}</li>
{% endfor %}
<li class="active">Add Subject</li>
</ul>
{% endblock nav-menu-items %}
{% block class_groups_nav %}
<div class="tabbable">
<ul class="nav nav-tabs">
{% for group in groups %}
<li>
<a data-toggle="tab" href="{% url 'subnav' sub_slug class_grp_slug %}">{{ group }}</a>
</li>
{% endfor %}
<li>Add</li>
</ul>
{% endblock class_groups_nav %}
I'm trying to achieve a 'nav' of subjects, then a 'subnav' below showing a tab for each class group for the subject selected in the navigation above.
I've tried different ways of doing this such as making Subject.objects.all() available as context processors. I have also attempted to subclass NavListView so I can inherit the previous context, making them available in SubNavListView.
At the moment, I'm getting a NoReverseMatch error where the url named 'nav' is not passing the sub_slug and so I can't use it in the url in the template.
Any thoughts on getting this working?
Many thanks,
Assuming your Subject model has field named slug in it, you need to update your code to
<li class="">{{ sub }}</li>
ie. pass an appropriate parameter to {%url ... %}. Change sub.slug to whatever field name you want to refer to.
If you want to, you can also do {% url 'nav' sub_slug=sub.slug %}.
You are trying to pass sub_slug, but which is not defined in the template context, and result in empty string. So nav url will not get any parameter.
I see similar problem in your other {%url ...%} tags in the template.