Show page items count in django pagination - django

I'm trying to figure out how to show something like "Showing 1-10 of 52" using django pagination in my templates.
I accomplished the pagination itself but nothing comes to my mind about this requirement.
Any ideas?

As the documentation shows, Paginator objects have a count attribute which shows the total number of objects across all pages (in the queryset or list they're paginating) and Page objects have start_index() and end_index() methods.
So, assuming you pass the page to the template context:
Showing {{ page.start_index }}-{{ page.end_index }} of {{ page.paginator.count }}

You'll need to do something moderately more complex behind the scenes. And please note that while I am a python dev, i've been using werkzeug and jinja2 for a long time now and so my django syntax is a little rusty. Also this was dry-coded (as in I just typed it here in the browser) and should be tested to make sure that it works as intended.
Generally I'll create a pagination object, and pass it in a query object that isn't filtered by pages, you can also tell it how many per page and what page you're on.
So something vaguely similar to:
Paginator(query, objects_per_page, current_page_number)
And then pass the resulting paginator object into the template.
Inside the paginator's init you'd want to do something similar to:
def __init__(self, query, objects_per_page, current_page_number):
self.total = query.count()
self.per_page = objects_per_page
self.current_page_number = current_page_number
self.lower_limit = objects_per_page * current_page_number
self.upper_limit = objects_per_page * (current_page_number + 1)
if self.upper_limit > self.total:
self.upper_limit = self.total
self.objects = query[self.lower_limit - 1:self.upper_limit - 1]
Then in the template you'd do something like
Showing {{paginator.lower_limit}}-{{paginator.upper_limit}} of {{paginator.total}}
And later when you are ready to iterate over the objects you could just iterate over paginator.objects.
I hope that gives you a general idea of how you can cleanly do this.

The django Paginator object has a num_pages property. In your view, you can just pass the correct data to your template and show it there.
So as an (rough) example:
view
current_page = ## input param
items_per_page = 10
paginator = Paginator(result, items_per_page)
...
return render_to_response('template.html',
{
'num_pages': paginator.num_pages,
'start_count': items_per_page * current_page + 1,
'end_count': (items_per_page * current_page + 1) + len(paginator(current_page).object_list)
'items_per_page': items_per_page
}
template
showing {{start_count} - {{end_count}} of {{num_pages}}
(I wrote this code without the benefit of a compiler, so test it)

paginator = Paginator(query, settings.SEARCH_DATA_PER_PAGE)
For number of Pages :
num_of_pages = paginator.num_pages
For total number of items across all pages :
num_of_objects = paginator.count
You can calculate number of items per page from this data.

totalPages = math.ceil(float(totalRecords)/recordsPerPage)
or
paginatorInstance.num_pages # this is there by default
Wasn't that hard, was it?

Related

How to use Django iterator in this case

I want to show 20 reviews per page on my website.
Unfortunately whole reviews count is about 260,000, It takes 3~4sec to show at page 10,000~
I used to use queryset like this:
reviews = Review.objects.all()[(page_num-1)*20:20*page_num]
.
.
.
obj['reviews'] = reviews
Now I think using iterator(chunk_size=) is better than upper one for efficiency.
But I dont know how to use iterator() properly.
I definitely need your helps
Please let me know...
Django pagination:
https://docs.djangoproject.com/en/3.1/topics/pagination/
p = Paginator(objects, 20)
>>> p.count
20*N
>>> p.num_pages
N
page1 = p.page(1)
page2 = p.page(2)
...
pageN = p.page(n)

Wagtail: Filter Page model by a field on the child

I have two models, ParentPage and ChildPage. I want to find the set of ParentPages where a field is_completed is True on the ChildPage.
Normally in Django, I could do something like
ParentPage.objects.filter(child_page__is_completed=True)
However, I don't think there is a join here for the Wagtail/Treebeard hierarchy.
I also thought you might be able to filter ChildPages by multiple ParentPages, e.g. ChildPage.objects.children_of([parent_ids]), but I can't see a way to do that either.
Is there a simpler way?
The Page table has path and url_path columns. If you find all children and strip the last part of the path or url_path, you can use that result to query the parent pages.
Path:
child_paths = ChildPage.objects.filter(is_completed=True).values_list("path", flat=True)
parent_paths = set([cp[:-4] for cp in child_paths])
pages = Page.objects.filter(path__in=parent_paths)
Url path:
child_urls = ChildPage.objects.filter(is_completed=True).values_list("url_path", flat=True)
parent_urls = set([url.rsplit('/', 1)[0] for url in child_urls])
pages = Page.objects.filter(url_path__in=parent_urls)
Disclaimer: untested code.

How to add URL strings for a Django app to a database and randomly select?

I built a small survey application which randomly assigns participants to one of 9 paths by randomly selecting the last part of the URL and appending it onto a button link. I then use e.g. {% if 'surveyone' in request.path %} etc. in my template tags to display the right content to the user.
This works fine on my local machine but I have since discovered Multi-threaded processing and using constant lists such as the below SURVEY_URLS has caused issues when the project is uploaded to the server. I want to instead store this in my database.
Question(s)
How do I add/store the below SURVEY_URLS list of strings in my MySQL database?
How do I remove particular URL strings from the same database? (as below)
The below is my existing code which has all the functionality I need and works fine on my machine. I just need to make it work correctly when dealing with multiple concurrent users.
Thanks
views.py
SURVEY_URLS = ['/surveyone/', '/surveytwo/', '/surveythree/', '/surveyfour/', '/surveyfive/', '/surveysix/', '/surveyseven/', '/surveyeight/', '/surveynine/']
def begin(request):
total_path_counter = TotalCounter.objects.get_or_create(total_max_counter__gte=0)[0]
if total_path_counter.total_max_counter < 100:
survey_url = random.choice(SURVEY_URLS)
if survey_url == '/surveyone/':
survey_path_counter = SurveyCounter.objects.get_or_create(survey_wizard_type = 'survey_wizard_one')[0]
if survey_path_counter.survey_wizard_count > 9:
SURVEY_URLS.remove('/surveyone/')
elif survey_url == '/surveytwo/':
survey_path_counter = SurveyCounter.objects.get_or_create(survey_wizard_type = 'survey_wizard_two')[0]
if survey_path_counter.survey_wizard_count > 9:
SURVEY_URLS.remove('/surveytwo/')
....
....
elif survey_url == '/surveynine/':
survey_path_counter = SurveyCounter.objects.get_or_create(survey_wizard_type = 'survey_wizard_nine')[0]
if survey_path_counter.survey_wizard_count > 9:
SURVEY_URLS.remove('/surveynine/')
return render(request, 'Begin.html', {'survey_url': survey_url})
else:
return render(request, 'surveyfull.html')
Begin.html
<a class="btn btn-success" href="/experiment{{survey_url}}">Begin Instruction Tasks</a>
Well, I suppose you can make a new model something like this:
class Survey(model.Model):
name = models.Charfield(max_length=50) # surveyone goes here
...
than in the view you can pull the random like this:
survey_count = Survey.objects.all().count()
random_survey_index = random.randint(0, survey_count - 1)
get the instance name by random_survey_index and append it to your url
Not sure how effective this approach is, but should work.

Django No Reverse Match with Arguments

From my template:
July
URL Pattern:
url(r'^ocal/$', views.calendar, name = "othermonth"),
View:
def calendar(request, year, month):
my_year = int(year)
my_month = int(month)
my_calendar_from_month = datetime(my_year, my_month, 1)
my_calendar_to_month = datetime(my_year, my_month, monthrange(my_year, my_month)[1])
my_tickets = Event.objects.filter(on_sale__gte=my_calendar_from_month).filter(on_sale__lte=my_calendar_to_month)
my_previous_year = my_year
my_previous_month = my_month - 1
if my_previous_month == 0:
my_previous_year = my_year - 1
my_previous_month = 12
my_next_year = my_year
my_next_month = my_month + 1
if my_next_month == 13:
my_next_year = my_year + 1
my_next_month = 1
my_year_after_this = my_year + 1
my_year_before_this = my_year - 1
cal = TicketCalendar(my_tickets).formatmonth(year, month)
return render_to_response('calendar.html', {'events_list': my_tickets,
'calendar': mark_safe(cal),
'month': my_month,
'month_name': named_month(my_month),
'year': my_year,
'previous_month': my_previous_month,
'previous_month_name': named_month(my_previous_month),
'previous_year': my_previous_year,
'next_month': my_next_month,
'next_month_name': named_month(my_next_month),
'next_year': my_next_year,
'year_before_this': my_year_before_this,
'year_after_this': my_year_after_this,
}, context_instance=RequestContext(request))
Error:
Reverse for 'othermonth' with arguments '(2013, 7)' and keyword arguments '{}' not found.
I've searched through stackoverflow and the django documentation but I can't seem to figure out why I'm getting this NoReverseMatch error. I'm sure its a very simple oversight on my part because I'm staring at code from a previous project which is almost identical to this and that works fine. Any help would be appreciated, thanks.
UPDATE: I tried removing the parameters that I was trying to send with the URL and that fixed the NoReverseMatch however the view that is called requires those arguments so the link fails.
How do you plan for those arguments to be embedded in your URL? There's nothing capturing them, and no way for the reverse lookup to construct it. You need a URL pattern that accepts these parameters. Something like:
url(r'^ocal/(?P<year>\d{4})/(?P<month>(0|1)?\d)/$', views.calendar, name = "othermonth_bymonth"),
Using keyword rather than positional arguments here is optional, but I think it makes things easier - and allows setting default values that can trigger behavior like showing a calendar for the current day when nothing's specified.
Also, your mytickets queryset can also be constructed thus:
mytickets = Event.objects.filter(on_sale__year=year, onsale__month=month)
Which I think is a little cleaner to read.
Actually, upon a closer look - Django's built in date based views can do a lot of this for you. If you haven't already looked into them, I recommend doing so:
https://docs.djangoproject.com/en/dev/ref/class-based-views/generic-date-based/
For this particular view, all you'd need to do is subclass MonthArchiveView to create your TicketCalendar instance and add it to your context.
Edit: OK, you're still getting problems. This is how I would go about solving this:
views.py
class TicketMonthArchiveView(MonthArchiveView):
allow_empty = True # show months even in which no tickets exist
allow_future = True # show future months
model = Event
def get_context_data(self, **kwargs):
base_context = super(TicketMonthArchiveView, self).get_context_data(**kwargs)
my_tickets = kwargs['object_list']
base_context['calendar'] = mark_safe(TicketCalendar(my_tickets).formatmonth(self.get_year(), self.get_month()))
# the above could be a little off because I don't know exactly how your formatmonth method works
return base_context
urls.py
from somewhere.views import TicketMonthArchiveView
# other patterns, plus:
url(r'^ocal/(?P<year>\d{4})/(?P<month>(0|1)?\d)/$', TicketMonthArchiveView.as_view(), name='calendar_by_month'),
template event_archive_month.html
{{ next_month|date:'%f' }}
Obviously there's a lot more you could do here with the year and day views, but this should demonstrate the general concepts.
Large context volumes caused this behavior for me as well, despite having the correct syntax.
I had this, on Django 1.5:
urls.py
url(r'(?P<CLASSID>[A-Z0-9_]+)/$', 'psclassdefn.views.psclassdefn_detail', name="psclassdefn_detail"),
template.html
{% for inst in li_inst %}
<li><a href={% url 'psclassdefn.views.psclassdefn_detail' CLASSID=inst.CLASSID %}></a></li>
{% endfor %}
I kept on getting NoReverseMatch even though the syntax seemed OK and I could reverse into a no-argument url.
Now, li_inst was a huge list of about 1000 rows fetched from the db and passed to the context variable. Just to test, I trimmed the list by removing all but 10 or so rows. And it worked, without any syntax changes.
Maybe the templating system intentionally throttles the context size? I can filter the list if needed, just didn't expect this error to come from it.

Optimizing database queries in Django

I have a bit of code that is causing my page to load pretty slow (49 queries in 128 ms). This is the landing page for my site -- so it needs to load snappily.
The following is my views.py that creates a feed of latest updates on the site and is causing the slowest load times from what I can see in the Debug toolbar:
def product_feed(request):
""" Return all site activity from friends, etc. """
latestparts = Part.objects.all().prefetch_related('uniparts').order_by('-added')
latestdesigns = Design.objects.all().order_by('-added')
latest = list(latestparts) + list(latestdesigns)
latestupdates = sorted (latest, key = lambda x: x.added, reverse = True)
latestupdates = latestupdates [0:8]
# only get the unique avatars that we need to put on the page so we're not pinging for images for each update
uniqueusers = User.objects.filter(id__in = Part.objects.values_list('adder', flat=True))
return render_to_response("homepage.html", {
"uniqueusers": uniqueusers,
"latestupdates": latestupdates
}, context_instance=RequestContext(request))
The query that causes the most time seem to be:
latest = list(latestparts) + list(latestdesigns) (25ms)
There is another one that's 17ms (sitewide annoucements) and 25ms (adding tagged items on each product feed item) respectively that I am also investigating.
Does anyone see any ways in which I can optimize the loading of my activity feed?
You never need more than 8 items, so limit your queries. And don't forget to make sure that added in both models is indexed.
latestparts = Part.objects.all().prefetch_related('uniparts').order_by('-added')[:8]
latestdesigns = Design.objects.all().order_by('-added')[:8]
For bonus marks, eliminate the magic number.
After making those queries a bit faster, you might want to check out memcache to store the most common query results.
Moreover, I believe adder is ForeignKey to User model.
Part.objects.distinct().values_list('adder', flat=True)
Above line is QuerySet with unique addre values. I believe you ment exactly that.
It saves you performing a subuery.