Keeping items per page static while paginating - django

I posed a question and got a quick response (thanks, Bob!) on how to do pagination in Django:
Change value for paginate_by on the fly
However, as outlined below, I'm having an issue getting the items per page to remain set.
I modified my ProdListView.html template to show buttons with page numbers, but, while it works, I'm getting an odd behavior. I have a for loop that I use to put out the page numbers as buttons:
{% for i in DataPaginated.paginator.page_range %}
{% if DataPaginated.number == i %}
<button class="w3-button w3-amber">{{ i }} </button>
{% else %}
{{ i }}
{% endif %}
{% endfor %}
If the pagination is set to the default of ten this works properly and shows three pages (there are 24 items). If I change the pagination to 20, it looks correct at first and shows two pages. However, if I click on the second page, it changes my pagination back to 10, shows three pages again, and places me on the 2nd of the three pages.
In the interest of space, you can find the code I'm using in the views.py file and template file, besides the fragment above, in Bob White's answer here:
Change value for paginate_by on the fly
I would like whichever items per page is picked in the form to stay set while the user pages through the output until/unless they choose to change the items per page again. Is there something I can do to keep my pagination (items per page) locked at the setting chosen with the form?
Thanks--
Al

Looking at the code in the views, you have a line: paginate_by = request.GET.get('paginate_by', 10) or 10, but I don't see in your template that you're sending paginate_by query param?
This will basically reset your pagination to 10 if you don't explicitly provide it.

Related

In Django, is there a better alternative to using "include" 100+ times in the same document?

I made a Django site that lets users search a database and then display results. Each result is an article and for each article you see the title, author, publication date, etc. This information for each result appears in a box I created with CSS to make the result look nice. Since I may alter the look of the box in the future, I want to use it as a template and then "include" it in my results page. But because there could be over 100 results and each result will display the box, I would need to "include" the box over 100 times with code like the following:
{% for article in article_results %}
{% include 'result_box.html' %}
{% endfor %}
Is this ok? Is there a better way to do this?
Yes. See this documentation:
https://docs.djangoproject.com/en/1.11/ref/templates/builtins/#cycle
You can loop through each element passed into your template and generate some HTML depending on it's characteristics.

How to get the value of an individual radio button in a Django template?

Is there a way for a template to examine the value of an individual radio button?
I know how to get other attributes:
{% for button in myform.radio_select %}
{{button.tag}}
{{button.choice_label}}
{{button.id_for_label}}
{% endfor %}
But how do I get the value? I tried the obvious {{button.value}} and the less obvious {{button.tag.value}} but they did not work.
The answer to this question says that as of Django 1.11, the value is stored in {{button.data.value}}.
Previous versions of Django used {{button.choice_value}}.

django-cms redirect top level menu to first child

I would like to redirect the top menu items in a page I'm writing to point at their children if they have any. E.g. this site has a top level About, with several CMS pages underneath. When a user clicks on about, I want to show the first child page under the top-level about menu.
I can do it like the below, but it feels wrong. I know I can do a static redirect, but I don't want to do that, as the first child might change (it's a CMS system after all) and I don't want the people entering data into the CMS to have to manage a redirect. So I prefer the below to a configured redirect.
This is a pretty common requirement, is there a standard way of doing this? I think I might be able to do it with a navigation modifier, so I might try that route next.
<div style="display:none;">{% show_menu 0 1 1 %}</div>
<div id="topmenu">
<ul>
{% for child in children %}
<li>
{% if child.children %}
<a href="{{child.children.0.get_absolute_url}}">
{% else %}
<a href="{{child.get_absolute_url}}">
{% endif %}
{{ child.get_menu_title }}
</a>
</li>
{% endfor %}
</ul>
</div>
About the above, I need to have the show_menu in there otherwise the data isn't there for my custom menu. Is there any way for this not to be needed?
The thing I dislike the most about the above, is that the /about/ page is still active. It's not the worst thing in the world if someone browses there, they'll get an empty page, but I'd rather that not happen. I think if I wrote a navigation extension the same disadvantage would be there. I guess I need a programmatic redirect to redirect to the first child if that page is hit. Thoughts? I can probably do it in template logic, but that seems crazy.
2018: updated version, that works in cms 3.4+ and new middleware style compatible available here. Once you added the /firstchild, you'll need to edit/publish the redirecting page in the cms pages's change list, as, you guess it, will redirect.
solution with middleware and custom redirect field value of "/firstchild", to trigger redirects.
plus: you won't have any blank pages
plus: it's flexible, as it allows pages that will not be redirected to subpages
plus: no action needed when the first child's url changes
minor drawback: there is always a redirect ( 301, though, not that bad ;)
minor drawback: configuration is a bit nerdy, as you will have to enter "/firstchild" into the "redirect" field. definitely not end-user friendly, but works. the cms itself doesnt provide a better solution at all (not even in 2018).
from django.shortcuts import redirect
# redirect to first subpage if the "redirect" field is exactly "_subpage"
class RedirectSubpageMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
if request.current_page:
page = request.current_page
if "slug" in view_kwargs and page.get_redirect() == "/firstchild":
subpages = request.current_page.children.all()
if subpages.count():
# can redirect! must make uri!
return redirect(subpages[0].get_absolute_url(), permanent=True)
return None
You might consider setting the CMS_REDIRECTS = True in the settings.py file.
Then you can go into the /about/ page in the admin and add the redirect to /about/child-page
Note: I haven't actually tried this, I just happened to stumble across this information this morning.

Best way to slice a Django queryset without hitting the database more than once

I'm running a query to get the 5 latest News items. In my template, I want to display the first item in one location, then the remaining 4 further down the page.
In my template, I do something like this:
{% for n in news|slice:":1" %}
{{ n.headline }}
{% endfor %}
... more HTML ...
{% for n in news|slice:"1:" %}
{{ n.headline }}
{% endfor %}
When I look in the Debug Toolbar, this results in two queries to the database: one with LIMIT 1 and another with LIMIT 4 OFFSET 1, but otherwise the same. I appreciate this is Django's way of intelligently only requesting the stuff you actually use, but in this case it seems a little excessive. What's the best way to do this kind of thing?
Convert to a sequence in the view, then slice the sequence.
var = list(somequery[:5])
You just need to force the queryset to evaluate itself before the slice. This could be done as simply as calling len() on it in your view before passing it off to the context.
The Django docs have a complete list of everything that causes a queryset to evaluate. Just do something from that list and you're good.

Django template: Ordering dictionary items for display

I am making a website that displays a user's chosen youtube videos. A user can enter a comment for each video.
I want to display (in this order):
User comment
video title
I have already made the view and have created the following list of dictionary items. Each one represents one video. I send this to my html page:
[
{"my_own_object": vid_obj1, "youtube_obj": obj1}
{"my_own_object": vid_obj2, "youtube_obj": obj2}
]
"youtube_obj" is the object supplied by youtube, which contains the url, title, rating, etc. "my_own_object" contains the user's comments as well as other information.
I iterate over the list and get one dictionary/video. That's fine. Then I need to display the video's information:
{% for key,value in list.items %}
{% if key = "my_own_object" %}
<div>
<p>{{value.user_comment}}</p>
</div>
{% endif %}
{% if key = "youtube_obj" %}
<div>
<p> {{value.media.title.text}}</p>
</div>
{% endif %}
{% endfor %}
This works, except that, because I cannot determine the dictionary order, I might end up with:
Video title
User comment
I thought I could get around this by assigning variables (and then printing the values in the proper order), and am still reeling from the fact that I cannot assign variables!
So, how can I get around this? Can I pluck the key/value that I need instead of iterating over the dictionary items - I tried looking for ways to do this, but no luck. Any other ideas? (I need to pass both video objects as I may need more information than comment and title, later.)
You can use dictionary keys directly:
{% for item in list %} {# PS: don't use list as a variable name #}
<p>{{item.my_own_object.user_comment}}</p>
<p>{{item.youtube_obj.media.title.text}}</p>
{% endfor %}
Just iterate twice. Once for the videos, and once again for the comments. Or, split them into their own dictionaries that are passed through to the template. That's probably a better option, as you avoid iterating twice over the dict. For very small dicts this will be no problem. For larger ones, it can be a problem.