Django: How do I pass a for variable into a querystring? - html

I'm attempting to fix some pagination issues in a Django app. Using the Previous and Next buttons work fine, since I'm using existing variables like previous_page_number, but I'm unsure how to get the Page 1, Page 2, Page N buttons to work. Here's the Previous Page code, which works just fine:
{% if table.page and table.paginator.num_pages > 1 %}
{% block pagination %}
<nav class="pagination is-centered" role="navigation" aria-label="pagination">
{% block pagination.previous %}
{% if table.page.has_previous %}
Previous
{% else %}
<a href="#" class="pagination-previous" disabled>Previous</a>
{% endif %}
{% endblock pagination.previous %}
And here's my current Pagination Range code, which doesn't. Specifically, the else segment is what I'm unable to get working.
{% block pagination.range %}
{% if table.page.has_previous or table.page.has_next %}
<ul class="pagination-list">
{% for p in table.page|table_page_range:table.paginator %}
{% if table.page.number == p %}
<li>
<a class="pagination-link is-current is-dark" aria-label="Page {{ table.page.number }}" aria-current="page">{{ table.page.number }}</a>
</li>
{% else %}
<li>
{{ p }}
</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
{% endblock pagination.range %}
Currently, the pagination doesn't work, and the URL that gets passed ends at page= instead of page=p, whatever p is at the time. I'm uncertain how to pass p in as a readable variable, to cause it to appear in the URL. I've tried using the same format as other parts of the code not in the querystring.
{% else %}
<li>
{{ p }}
</li>
{% endif %}
This causes an error that prevents the page loading at all, as Django can't reconcile the {{ part.

Display
and you can get from the views.py like that;
page = request.GET.get("page", ""),
if it is page is none you can give them default value like this
page = request.GET.get("page", "1")

Related

Django, display certain hyperlinks based on user group

{% extends 'base.html' %}
{% block content %}
<p>Welcome to home page.</p>
<p>{% user.groups.all() %}</p>
{% endblock %}
At the moment I'm trying to figure out how I could even get all the user's groups to show on the page. This results in an error.... Invalid block tag on line 5: 'user.groups.all()', expected 'endblock'. Did you forget to register or load this tag?
I have tried to do if statements, but it seems break as soon as it meets one condition. For example if user is a part of test1 and test2 groups, I'd like for it to display test1 and test2, but it only displays test1.
{% extends 'base.html' %}
{% block content %}
<p>Welcome to home page.</p>
{% if user.groups.all.0.name == "test1" %}
<p>test1</p>
{% if user.groups.all.0.name == "test2" %}
<p>test2</p>
{% endif %}
{% endblock %}
In the first code, you should have used {{ }} instead. In order to access groups, do this:
{{ user.groups.all }}
and to check for a specific group:
{% if desired_group in user.groups.all %}
some html..
{% endif %}
if you want to output specific html for each group:
{% for group in user.groups.all %}
{% if group == desired_group %}
some html..
{% endif %}
..some more conditions..
{% endfor %}

Twig set reusable piece of html

I'm creating some templates with Twig and have an issue.
I'm trying to load a piece of html that is used several times troughout a webshop. So my idea is to create a reusable piece of code that I can load everytime when needed.
The problem I'm facing is that when I do a for loop and then include that piece of code I get an empty return. With other words my code doesn't recognize the data that need to be loaded for each product in the for loop. It returns empty info boxes.
To clarify:
I have a main template index.html which calls a snippet to include some products (don't look at rain extension!!):
{% if featured %}
{% include 'snippets/products.rain' with {'products': featured, 'type': 'grid'} %}
{% endif %}
My products.rain snippet looks like this:
{% if type %}
{% if type == 'grid' %}
{% for product in products %} {# Products in this case = feautured products #}
<li class="item clearfix">.... etc etc .... </li>
{% endfor %}
{% elseif type == 'other-layout' %}
<div class="item">.... etc etc .... </div>
{% endif %}
{% endif %}
In the for loop there's html that's for 95% the same as in each for loop. I want to place that code inside a block that can be included in the for loops.
So what I did was:
{% set product_html %}
.... a lot of html ....
<a href="{{ product.url | url }}" title="{{ product.fulltitle }}">
<img src="{{ product.image }}" width="100" height="100" alt="{{ product.fulltitle }}"/>
</a>
{% endset %}
And then included in the for loop, like so:
{% if type %}
{% if type == 'grid' %}
{% for product in products %} {# Products in this case = feautured products #}
<li class="item clearfix">{{ product_html | raw }}</li>
{% endfor %}
{% elseif type == 'other-layout' %}
<div class="item">{{ product_html | raw }}</div>
{% endif %}
{% endif %}
However this returns the html that is set however with empty product.image and empty product.fulltitle.
I tried the same with set block, but that has the same result.
Is there anything I'm doing wrong....??
When you are using {% set %}, content inside your variable is not dynamic, it will only use data in your current context, see it live.
You can achieve your goal using 2 ways: using include or using macros.
As your piece of code for a product is small and not reused somewhere else, I suggest you to use macros:
{% macro product_html(product) %}
Current product is: {{ product }}
{% endmacro %}
{% import _self as macros %}
{% for product in products %}
{{ macros.product_html(product) }}
{% endfor %}
See it live

In Jekyll How Do I Work With a Specific Post?

I have a sidebar in my site that I want to be able to highlight a specific post.
How do I work with a specific post?
I tried this:
{% assign workingPost = site.posts.2014-09-08-We-Convert-All-Dollars-to-Bitcoin %}
<div>{{workingPost.title}}</div>
But the title is blank. The title for that post is We Convert All Dollars to Bitcoin and that was what I expected to get.
You can do this for all pages with :
<ul class="nav navbar-nav">
{% for p in site.pages %}
<li{% if p.url == page.url %} class="active"{% endif %}>
{{ p.title }}
</li>
{% endfor %}
</ul>
Note: in your answer, you for loop may collides with page variable if you do this in post or page page.
That's why I use {% for p in site.pages %} instead of {% for page in site.pages %}
In this blog post a commentator is looping over the collection and comparing titles. Seems lame, but works for now.
{% for page in site.pages %}
{% if page.title == 'About' %}
{{ page.content }}
{% endif %}
{% endfor %}

Same for with different behavior in Django

I have a little question, and it's about for loop in Django template:
I want to do a for and in each iteration do something different for example
{% for object in objects %}
##### html code to put a div with object information in the left and one in the right ######
{% endfor %}
To print all my objects but one in the left and another one in the right.
Can anyone help me?
You want the cycle tag.
{% for object in objects %}
<li class="{% cycle "your-left-class" "your-right-class" %}">
{{object}}
</li>
{% endfor %}
{% for object in objects %}
<div class="
{% if forloop.counter % 2 == 0 %}
pull-left
{% else %}
pull-right
{% endif %}
">
{{object}}
</div>
{% endfor %}
Check the for loop in templates.

Sorted navigation menu with Jekyll and Liquid

I'm constructing a static site (no blog) with Jekyll/Liquid. I want it to have an auto-generated navigation menu that lists all existing pages and highlight the current page. The items should be added to the menu in a particular order. Therefore, I define a weight property in the pages' YAML:
---
layout : default
title : Some title
weight : 5
---
The navigation menu is constructed as follows:
<ul>
{% for p in site.pages | sort:weight %}
<li>
<a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
{{ p.title }}
</a>
</li>
{% endfor %}
</ul>
This creates links to all existing pages, but they're unsorted, the sort filter seems to be ignored. Obviously, I'm doing something wrong, but I can't figure out what.
Since Jekyll 2.2.0 you can sort an array of objects by any object property. You can now do :
{% assign pages = site.pages | sort:"weight" %}
<ul>
{% for p in pages %}
<li>
<a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
{{ p.title }}
</a>
</li>
{% endfor %}
</ul>
And save a lot of build time compared to #kikito solution.
edit:
You MUST assign your sorting property as an integer weight: 10 and not as a string weight: "10".
Assigning sorting properties as string will ends up in a a string sort like "1, 10, 11, 2, 20, ..."
Your only option seems to be using a double loop.
<ul>
{% for weight in (1..10) %}
{% for p in site.pages %}
{% if p.weight == weight %}
<li>
<a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
{{ p.title }}
</a>
</li>
{% endif %}
{% endfor %}
{% endfor %}
</ul>
Ugly as it is, it should work. If you also have pages without a weight, you will have to include an additional internal loop just doing {% unless p.weight %} before/after the current internal one.
Below solution works on Github (doesn't require a plugin):
{% assign sorted_pages = site.pages | sort:"name" %}
{% for node in sorted_pages %}
<li>{{node.title}}</li>
{% endfor %}
Above snippet sorts pages by file name (name attribute on Page object is derived from file name). I renamed files to match my desired order: 00-index.md, 01-about.md – and presto! Pages are ordered.
One gotcha is that those number prefixes end up in the URLs, which looks awkward for most pages and is a real problem in with 00-index.html. Permalilnks to the rescue:
---
layout: default
title: News
permalink: "index.html"
---
P.S. I wanted to be clever and add custom attributes just for sorting. Unfortunately custom attributes are not accessible as methods on Page class and thus can't be used for sorting:
{% assign sorted_pages = site.pages | sort:"weight" %} #bummer
I've written a simple Jekyll plugin to solve this issue:
Copy sorted_for.rb from https://gist.github.com/3765912 to _plugins subdirectory of your Jekyll project:
module Jekyll
class SortedForTag < Liquid::For
def render(context)
sorted_collection = context[#collection_name].dup
sorted_collection.sort_by! { |i| i.to_liquid[#attributes['sort_by']] }
sorted_collection_name = "#{#collection_name}_sorted".sub('.', '_')
context[sorted_collection_name] = sorted_collection
#collection_name = sorted_collection_name
super
end
def end_tag
'endsorted_for'
end
end
end
Liquid::Template.register_tag('sorted_for', Jekyll::SortedForTag)
Use tag sorted_for instead of for with sort_by:property parameter to sort by given property. You can also add reversed just like the original for.
Don't forget to use different end tag endsorted_for.
In your case the usage look like this:
<ul>
{% sorted_for p in site.pages sort_by:weight %}
<li>
<a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
{{ p.title }}
</a>
</li>
{% endsorted_for %}
</ul>
The simplest solution would be to prefix the filename of your pages with an index like this:
00-home.html
01-services.html
02-page3.html
Pages are be ordered by filename. However, now you'll have ugly urls.
In your yaml front matter sections you can override the generated url by setting the permalink variable.
For instance:
---
layout: default
permalink: index.html
---
Easy solution:
Assign a sorted array of site.pages first then run a for loop on the array.
Your code will look like:
{% assign links = site.pages | sort: 'weight' %}
{% for p in links %}
<li>
<a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">
{{ p.title }}
</a>
</li>
{% endfor %}
This works in my navbar _include which is simply:
<section id="navbar">
<nav>
{% assign tabs = site.pages | sort: 'weight' %}
{% for p in tabs %}
<span class="navitem">{{ p.title }}</span>
{% endfor %}
</nav>
</section>
I've solved this using a generator. The generator iterates over pages, getting the navigation data, sorting it and pushing it back to the site config. From there Liquid can retrieve the data and display it. It also takes care of hiding and showing items.
Consider this page fragment:
---
navigation:
title: Page name
weight: 100
show: true
---
content.
The navigation is rendered with this Liquid fragment:
{% for p in site.navigation %}
<li>
<a {% if p.url == page.url %}class="active"{% endif %} href="{{ p.url }}">{{ p.navigation.title }}</a>
</li>
{% endfor %}
Put the following code in a file in your _plugins folder:
module Jekyll
class SiteNavigation < Jekyll::Generator
safe true
priority :lowest
def generate(site)
# First remove all invisible items (default: nil = show in nav)
sorted = []
site.pages.each do |page|
sorted << page if page.data["navigation"]["show"] != false
end
# Then sort em according to weight
sorted = sorted.sort{ |a,b| a.data["navigation"]["weight"] <=> b.data["navigation"]["weight"] }
# Debug info.
puts "Sorted resulting navigation: (use site.config['sorted_navigation']) "
sorted.each do |p|
puts p.inspect
end
# Access this in Liquid using: site.navigation
site.config["navigation"] = sorted
end
end
end
I've spent quite a while figuring this out since I'm quite new to Jekyll and Ruby, so it would be great if anyone can improve on this.
I can get the code below works on with Jekyll/Liquid match to your requirement with category:
creates links to all existing pages,
sorted by weight (works as well on sorting per category),
highlight the current page.
On top of them it shows also number of post. All is done without any plug-in.
<ul class="topics">
{% capture tags %}
{% for tag in site.categories %}
{{ tag[0] }}
{% endfor %}
{% endcapture %}
{% assign sortedtags = tags | split:' ' | sort %}
{% for tag in sortedtags %}
<li class="topic-header"><b>{{ tag }} ({{ site.categories[tag] | size }} topics)</b>
<ul class='subnavlist'>
{% assign posts = site.categories[tag] | sort:"weight" %}
{% for post in posts %}
<li class='recipe {% if post.url == page.url %}active{% endif %}'>
{{ post.title }}
</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
Check it on action on our networking page. You may click a post to highlight the navigation, as well a given link to bring you to the source page where their weight is assigned.
If you're trying to sort by weight and by tag and limit the number to 10, here's code to do it:
{% assign counter = '0' %}
{% assign pages = site.pages | sort: "weight" %}
{% for page in pages %}
{% for tag in page.tags %}
{% if tag == "Getting Started" and counter < '9' %}
{% capture counter %}{{ counter | plus:'1' }}{% endcapture %}
<li>{{page.title}}</li>
{% endif %}
{% endfor %}
{% endfor %}
The solution above by #kikito also worked for me. I just added a few lines to remove pages without weight from the navigation and to get rid of white space:
<nav>
<ul>
{% for weight in (1..5) %}
{% unless p.weight %}
{% for p in site.pages %}
{% if p.weight == weight %}
{% if p.url == page.url %}
<li>{{ p.title }}</li>
{% else %}
<li>{{ p.title }}</li>
{% endif %}
{% endif %}
{% endfor %}
{% endunless %}
{% endfor %}
</ul>
</nav>