I'm using Pagination in my Django project and it all works properly, but I don't know how to expand my reach beyond +/- one page of the one I'm on. Currently, my html looks as such:
<div class="pagination pagination-centered">
<ul>
{% if title.has_previous %}
<li>«</li>
<li>{{ title.previous_page_number }}</li>
{% endif %}
<li class="active">{{ title.number }}</li>
{% if title.has_next %}
<li>{{ title.next_page_number }}</li>
<li>»</li>
{% endif %}
</ul>
</div>
This results in something that looks like [<<, 1, 2, 3, >>] if you are on page two. I'd like to expand the reach by maybe one or two steps or so. So if I'm on page 3, I can reach all the way from 1-5.
{# Create A Button Group that contains possible pages to navigate to #}
{% for page in paginator.page_range %}
{% if page < toolbar_max and page > toolbar_min and page != results.number %}
<button formaction="{% url 'volume:page' page=page %}">{{ page }}</button>
{% elif page == results.number %}
<button type="submit" formaction="{% url 'volumes:page' page=page %}">{{ page }}</button>
{% endif %}
{% endfor %}
Basically, you keep the minimum and maximum number page you want (toolbar_min and toolbar_max and pass that to your page.
# Variables to be passed for page menu
toolbar_max = results.number + 4
toolbar_min = results.number -4
So it will display 4 on each side of the number you are currently on. The 'results' is a query of mine that displays the current page I am on. I wanted that page to be highlighted with a different css class, and for it to know that it should be counting off of variable.
Related
I am trying to switch my head to start using MVC.
I have a Base HTML template, with an include NavBar code:
{% include "navBar.html" %}
So far, so good. Now, I want to send information to it (to the navBar.html template) regarding the Menu buttons. I have some simple buttons, and others with drop-button behavior.
My buttons are objects, they have information about name, href, position, etc, type (simple-button or drop-button)
So, I created a nested list in this way:
outer_list = []
for a in UserModule.objects.filter(user=user_id, is_active=True):
inner_list = []
inner_list.append(UserModule(a))
for b in Submodule.objects.filter(module=a.module, is_active=True):
inner_list.append(Submodule(b))
outer_list.append(inner_list)
return {'outer_list': outer_list}
So, my first element in every inner_list is the head of the possible drop-button or a simple button depending on his type attribute.
The list at the end is like this:
[0] Button-Simple-A
[1] Button-Drop-A => [sub-button-1, sub-button-2, sub-button-3]
[2] Button-Simple-B
and so on.
When I pass this outer_list to the template, the thing I have to do to arrange the buttons into the menu are crazy things. It has no sense to use MVC if I am going to write a lot of code in the template.
Until now, I am doing this over the template, and It is missing the first element identification, to recognize the button type, etc...
{% with isFirst=true %}
{% for inner_list in outer_list %}
{% for object in inner_list %}
{% if isFirst == true %}
<li><a href=#>Drop_parent</a></li>
{% else %}
<li><a href=#>Drop-Child</a></li>
{% endif %}
{% endfor %}
<li><a href=#>Static Button</a></li>
{% endfor %}
{% endwith %}
It is not finished at all, but I believe I am taking the wrong way.
Good advice will be thankful.
Thanks
Finally,
I got something that works. I would like to know if this is acceptable or there is another better way.
On the view side:
for a in UserModule.objects.filter(user=user_id, is_active=True):
x = Module.objects.get(id=a.module_id, is_active=True)
if x.type == 'drop-parent':
list.append(x)
for b in Submodule.objects.filter(module=a.module_id, is_active=True):
list.append(b)
list.append('drop_end')
return {'menu_list': list}
On the template site:
{% for object in menu_list %}
{% if object.type == 'drop-parent' %}
<li class="dropdown">
{{object.name}}
<div class="dropdown-content">
{% endif %}
{% if object.type == 'drop-child' %}
<a href=#>{{ object.name }}</a>
{% endif %}
{% if object == 'drop_end' %}
</div>
</li>
{% endif %}
{% endfor %}
Another question: Why adding the class identifier inside the list, I can not see the attributes in the template? I mean list.append(Module(x)) is not seen as an object Module inside template; otherwise, if I avoid adding the class I can access the attributes.
Thanks, I appreciate it
I seem to know where the issue is located since I can get around it, but for getting around it I have to sacrifice a function I really want to keep.
Here is the relevant code in the non-working state:
{% if sections %}
{% for item in sections %}
<a class="sections" href="{% url 'sections:generate' item.section.slug %}">{{ item.section.title }}</a>
{% for subsection in item.subsections %}
<p>{{ subsection.title }}</p>
{% endfor %}
{% endfor %}
{% else %}
<p>Error retrieving sections or no sections found</p>
{% endif %}
The problem part above is in the link tag. Let me explain by showing the related view.py:
def index(request):
sections = Section.objects.all()
context = {
'sections': [],
}
for section in sections:
context.get("sections").append(
{
'section': section,
'subsections': get_subsections(section),
}
)
return render(request=request, template_name='index.html', context=context)
So, 'sections' is an iterable list of items, containing for every items a dictionary with two entries. One, 'section' and one 'subsection'. There are multiple subsections for every section, this is what I really want to accomplish.
Ordinarily, when not bothering with subsections and simply iterating over a list of sections works fine. The template code for that would look something like:
{% for section in sections %}
{{ section.title }}
{% endfor %}
NOTE! The code above works just fine! But as soon as I add 'sections' as a list of dictionaries and have to reference the slug by item.section.slug the pages stop rendering.
Please advise.
Try using tuples:
View:
context['sections'] = [(section, tuple(get_subsections(section))) for section in sections]
Template:
{% for section, subsections in sections %}
<a class="sections" href="{% url 'sections:generate' section.slug %}">{{ section.title }}</a>
{% for subsection in subsections %}
<p>{{ subsection.title }}</p>
{% endfor %}
{% endfor %}
This question already has answers here:
ForLoop Iterations as List Index in Django Template
(3 answers)
Closed 8 years ago.
I have the following problem: A user is taking a course on my website. A course consists of several chapters and each chapter consists of several steps. If the user is logged in and completes a step, the backend saves the progress. When the user comes back to the site, I want him to continue where he left off. To do this I have to add the css class "active" to the corresponding navigation step and the content tab of each chapter. My starting point was to test with an if statement if the user had already finished the step. That worked great, but adds .active to all the steps that have not been finished yet. I only want to add it to the first step that hasn't been finished yet. I have actually solved this problem but it is a rather brute hack and I had to include a custom template tag that allows me to set a variable in the template:
{% load custom_tags %}
{% for chapter in chapters %}
<div>
<ul class="unstyled wizard clearfix">
{% set active_wizard_steps = 0 %}
{% for step in chapter.steps.all %}
<li class="{% if not step.read and active_wizard_steps == 0 %}active{% set active_wizard_steps = 1 %}{% endif %}{% if step.read %} finished{% endif %}"></li>
{% endfor %}
</ul>
</div>
{% endfor %}
I know that this can also be done by passing a list or dict to the template with all the active steps and then testing in the template if a step is in that list. But that would have meant rewriting a lot of view code so I decided to do it completely in the template.
So my question is: what would be the best practice way to solve this problem? I'm asking just out of curiousity and because the problem was actually really fun to figure out. Thanks a lot!
Add a method to Step:
class Step(models.Model):
# ...
def is_active(self):
# do your sauce
And in the template:
class="{% if step.is_active %}active{% endif %}"
We already have convenient helpers like get_absolute_url in models, why not add this one ?
If you don't like it, you could do a template filter and use it as such:
class="{% if step|is_active %}active{% endif %}"
Update
Here's another method, make a template filter as such:
#register.filter
def set_active_steps(steps):
active_wizard_steps = 0
for step in steps:
if not step.read and active_wizard_steps == 0:
step.is_active = True
return steps
Then, in your template:
{% for step in chapter.steps.all|set_active_steps %}
... {% if step.is_active %}...{% endif %}
{% endfor %}
I know you already solved your issue, I'm posting another answer for the sake of completion.
If you need inside a {% for %}{% endfor %} to set a class by matching the current wizard step against the current item in the loop, you can use the following code:
{% for step in wizard.steps.all %}
<li class="{% if wizard.steps.step1 == forloop.counter %}active{% elif wizard.steps.step1 > forloop.counter %} complete{% endif %}">
<span class="step">{{ forloop.counter }}</span>
</li>
{% endfor %}
I have the below dictionary with lists of objects.
args = {'Qtag': [
[<Question: Question object>, <Tags: Tags object>],
[<Question: Question object>, <Tags: Tags object>]
]
}
inside my views.py
args=dict(Qtag=Questag)
t=loader.get.template('main.html')
c=Context(args)
and inside my main.html, I am trying to iterate the below way
{%if Qtag %}
{% for item_list in Qtag %}
{% for item in item_list %}
</h4> <b> Question</b>:{{item.qid}} {{item.title}} </h4><br/>
</h4> {{item.question}} </h4>
<button name="tag" type="submit" value="tagname"> {{item.tagname}} </button>
{% endfor %}
{% endfor %}
{% endif %}
When I tried the above way in my main.html, I am getting the things displayed 4 times in the screen. Here, the first list consisting of list of Question object and list of tag object corresponds to a single question and similarly for the other one, so in the screen I only want the two question and its corresponding tags to be displayed. I am not able to iterate properly to get what I need. Any thought on how to achieve this!!!
Thanks.
I'm not sure what args=dict(Qtag=Questag) means in your code (since I don't know what Questag is). I don't know whether your tags are iterable either (I'm assuming so). Your HTML looks malformed too (e.g. I don't see an opening h4).
This is what I'd do to get something similar to what you're looking for. You can work on this.
{% if Qtag %}
{% for question, tags in Qtag %}
{# First put in the question header #}
<h4>Question : {{question.qid}} {{question.title}} </h4>
{# Then the question body #}
<p>
{{question.question}}
</p>
{# Now a tag list #}
<ul>
{% for tag in tags %}
<li>
<button name = "tag" type="submit" value="{{tag.name}}">
{{tag.name}}
</button>
</li>
{% endfor %}
</ul>
{% endfor %}
{% endif %}
Your original solution is iterating over the list quadratically (you're iterating over the list once for every outer iteration) and that's why you're seeing things being printed 4 times.
Django templates offer the builtin tag cycle for alternating between several values at different points in a template (or for loop in a template) but this tag does not reset when it is accessed in a scope outside of the cycles definition. I.e., if you have two or more lists in your template, the rows of all of which you'd like to use some css definitions odd and even, the first row of a list will pick up where the last left off, not with a fresh iteration from the choices (odd and even)
E.g., in the following code, if the first blog has an odd number of entries, then the first entry in a second blog will start as even, when I want it to start at odd.
{% for blog in blogs %}
{% for entry in blog.entries %}
<div class="{% cycle 'odd' 'even' %}" id="{{entry.id}}">
{{entry.text}}
</div>
{% endfor %}
{% endfor %}
I've tried obviating this by patching with the resetcycle tag offered here:
Django ticket: Cycle tag should reset after it steps out of scope
to no avail. (The code didn't work for me.)
I've also tried moving my inner loop into a custom tag, but this also did not work, perhaps because the compile/render cycle moves the loop back into the outer loop? (Regardless of why, it didn't work for me.)
How can I accomplish this simple task!? I'd prefer not to create a data structure in my view with this information pre-compiled; that seems unnecessary. Thanks in advance.
The easiest workaround (until the resetcycle patch gets fixed up and applied) is to use the built-in "divisibleby" filter with forloop.counter:
{% for entry in blog.entries %}
<div class="{% if forloop.counter|divisibleby:2 %}even{% else %}odd{% endif %}" id="{{ entry.id }}">
{{ entry.text }}
</div>
{% endfor %}
A little more verbose, but not hard to understand and it works great.
https://docs.djangoproject.com/en/1.8/ref/templates/builtins/#cycle
{% for o in some_list %}
<tr class="{% cycle 'row1' 'row2' %}">
...
</tr>
{% endfor %}
Give up and use Jinja2 Template System
I gave up on django template language, it's very restricted in what you can do with it.
Jinja2 uses the same syntax that the django template uses, but adds many enhancements over it.
EDIT/NOTE ( I know it sounds like a big switch for just a minor issue, but in reality I bet you always find yourself fighting the default template system in django, so it really is worthwhile and I believe it will make you more productive in the long run. )
You can read this article written by its author, although it's technical, he mentions the problem of the {% cycle %} tag in django.
Jinja doesn't have a cycle tag, it has a cycle method on the loop:
{% for user in users %}
<li class="{{ loop.cycle('odd', 'even') }}">{{ user }}</li>
{% endfor %}
A major advantage of Jinja2 is that it allows you to use logic for the presentation, so if you have a list of pictures, you can put them in a table, because you can start a new row inside a table every N elements, see, you can do for example:
{% if loop.index is divisibleby(5) %}
</tr>
{% if not loop.last %}
<tr>
{% endif %}
{% endif %}
you can also use mathematical expressions:
{% if x > 10 %}
and you can access your python functions directly (but some setup is required to specify which functions should be exposed for the template)
{% for item in normal_python_function_that_returns_a_query_or_a_list() %}
even set variables ..
{% set variable_name = function_that_returns_an_object_or_something() %}
You can use tagged cycle and resetcycle (new in Django 1.11) calls (from https://docs.djangoproject.com/en/1.11/ref/templates/builtins/#std:templatetag-resetcycle ):
{% for blog in blogs %}
{% cycle 'odd' 'even' as rowcolors silent %}
{% resetcycle rowcolors %}
{% for entry in blog.entries %}
{% cycle rowcolors %}
<div class="{{ rowcolors }}" id="{{entry.id}}">
{{ entry.text }}
</div>
{% endfor %}
{% endfor %}
I end up doing so, with the forloop.counter0 - It works great!
{% for product in products %}
{% if forloop.counter0|divisibleby:4 %}<div class="clear"></div>{% endif %}
<div class="product {% if forloop.counter0|divisibleby:4 %}col{% else %}col20{% endif %}">
Lorem Ipsum is simply dummy text
</div>
{% endfor %}
The easiest answer might be: "give up and use jQuery." If that's acceptable it's probably easier than fighting with Django's templates over something so simple.
There's a way to do it server-side with an iterator that doesn't keep a simultaneous copy of all the entries:
import itertools
return render_to_response('template.html',
{
"flattened_entries": itertools.chain(*(blog.entries for blog in blogs)),
})