How to order a set of object in a view - python

My code currently lists all domains in a server using:
{% for domain in server.domain_set.all %}
I want to order the domains in the view by their url. Something like:
{% for domain in server.domain_set.all().order_by('url') %}
But I get an exception "could not parse the remainder". How can I order the list?

The "could not parse the remainder" errors is because you're including Python code in your django template. Django doesn't allow that.
You could add a method on the model:
def sorted_domains(self):
return self.domain_set.all().order_by('url')
And then call it like this:
{% for domain in server.sorted_domains %}
An alternative is to set the default sort order on your Domain model with a Meta attribute.

You can use a dictsort filter:
Takes a list of dictionaries and returns that list sorted by the key
given in the argument.
{% for domain in server.domain_set.all|dictsort:'url' %}
Also see:
Sorting related items in a Django template
order_by template filter

Related

Slicing paginator.page_range inside a template

I have page_obj in a template which was returned from a ListView view. Now, I wanted to create links to several pages before and after the current page. Therefore, I wanted to slice page_obj.paginator.page_range this way: page_obj.paginator.page_range[page_obj.number-3:page_obj.number+4]. This works in django shell but for some reason when I did it a template, there is a Template Syntax Error, Could not parse the remainder: '[page_obj.number-3:page_obj.number+4]' from 'page_obj.paginator.page_range[page_obj.number-3:page_obj.number+4]'. Is there a workaround for this case?
P.S. I know I can do it using the whole page_obj.paginator.page_range and then using if statements to check if a page is in the required range, but I thought it's a bit inefficient.
As stated in my comment Django Template Language does not include normal python syntax. The reason for this is Django aims to separate the logic and design of the website. If there is need to perform somewhat complicated logic you either need to use template tags or filters.
For your need either an inclusion tag would work or a simple filter that would take the page_range and return a sliced version of it. A template filter here would not be very useful considering we can only pass one argument to it, meaning it would not be very customizable. Let's assume that your pagination would look very similar or perhaps you would pass the template you use to the tag.
Firstly you need to create a templatetags sub-package in your app and then in that you would add files (e.g. pagination_tags.py) which would contain your tags. The layout would be something like:
your_app/
__init__.py
models.py
templatetags/
__init__.py
pagination_tags.py
views.py
Now in your file pagination_tags.py you want to write your tags. As a reference you may read the howto on Custom template tags and filters in the documentation.
Firstly we declare register which is an instance of template.Library. After which we would write our template tags / filters. We will use an inclusion_tag:
from django import template
register = template.Library()
#register.inclusion_tag('pagination_tag.html')
def show_pagination(page_obj, **kwargs):
left = kwargs.get('left', 3)
right = kwargs.get('right', 4)
pages_iter = page_obj.paginator.page_range[page_obj.number - left:page_obj.number + right]
template = kwargs.get('template', 'default_pagination_template.html')
return {**kwargs, 'page_obj': page_obj, 'pages_iter': pages_iter, 'template': template}
Now we will have a simple template named pagination_tag.html that will simply extend the template name either passed as a keyword argument or default_pagination_template.html:
{% extends template %}
Now in default_pagination_template.html or any other template we can use all the variables in the dictionary that our function show_pagination returns:
{% for page_num in pages_iter %}
Display page links here, etc.
{% endfor %}
You can modify this implementation as per your needs. I will also leave the design and implementation of default_pagination_template.html upto you.
Now in your template where you want to use this, first we will load these tags. Then we will use them:
{% load pagination_tags %}
...
{% show_pagination page_obj left=5 right=6 template="some_custom_template.html" %}

Django dynamic url parameter names

Basically trying to do
{% url dynamic_url_name dynamic_parameter_name=dynamic_parameter_value %}
Tried the simplest approach of
{{entry}}
{% include 'mainsite/children/title_template.html' with
the_title=title_text
is_title_page=True
entries_of="title"
belongs_to="profile"
belongs_to_url_arg="user"
belongs_to_url_arg_value="author"
%}
But unfortunately this resulted in utter failure of
From this I can tell that parameters can't be context variables, so what I can try to do next is to simply unpack a dictionary as I would do inside python with something like
{% url **{dynamic_parameter_name:dynamic_parameter_value} %}
But I have no idea if it is possible inside django templates, and if possible how?
My urls look like
re_path(r'^title/(?P<title_text>[a-zA-Z0-9-]+)/$', TitlePage.as_view(), name='title')
re_path(r'^user/(?P<user>[a-zA-Z0-9-]+)/$', ProfilePage.as_view() ,name='profile')
And I the url is choosen either by a context variable or in an include tag, hence it is a variable.
{% url url_variable xxx=value %}
Now, url_variable is already a part of django, url tag accepts variable as it's first argument. But the xxx is not always the same, rather it changes according to url_variable, in this particular case; if url_variable is title, I want xxx to be title_text and if it is profile I want it to be user.
The parameter name is held in belongs_to, so if this was a regular python function, I could've simply done
url(url_variable, **{belongs_to: value})
and it would've unpacked it with the correct parameter name. So I need some kind of equivalency of this in template processor
I think you're overcomplicating things. You haven't shown your views themselves, but I can't see why they couldn't all take a commonly-named parameter - say, param - that does the specific work. So the URLs could be:
re_path(r'^title/(?P<param>[a-zA-Z0-9-]+)/$', TitlePage.as_view(), name='title')
re_path(r'^user/(?P<param>[a-zA-Z0-9-]+)/$', ProfilePage.as_view() ,name='profile')
and now you can do
{% url dynamic_url_name param=dynamic_parameter_value %}
**kwargs are not specifically supported as a parameter for the {% url %} tag, but there are a few workwarounds.
If you have a path in urlpatterns defined as:
path('myurl/<str:some>/<str:thing>', name='myurl')
Then you could have a filetoinclude.html:
My Url
And then in your main.html:
{% include 'filetoinclude.html' with urlname='myurl' some="aaa" thing="bbb" %}
And when you render you will have something like:
My Url
But obviously the issue is that maybe you want to address specific parameters of the URL when you reverse it. For this reason you could create a templatetag like:
from django.urls import reverse
from django import template
register = template.Library()
#register.simple_tag
def dynamic_url(urlname, **kwargs):
return reverse(urlname, kwargs=kwargs)
and then in your filetoinclude.html you could do:
{% load url_extended %}
{% dynamic_url urlname some=some thing=thing %}
which will yield the same URL as before.

Django performance with render_to_response

I have Django app, which have following statements:
response = render_to_response('template.html', {'s':s, 'r':r}, context_instance=RequestContext(request))
The typical statement at template.html is:
<tbody>{%for m in r.m_set.all %}<tr>
<td>{{m.id}}</td>
<td>{{m.Name}}</td>
<td>{{m.MaterialLot.id}}</td>
<td>{{m.MaterialSublot.id}}</td>
<td>{{m.Description|truncatechars:20}}</td>
<td>{{m.StorageLocation.id}} - {{m.StorageLocation.Name}}</td>
<td>{{m.Quantity|floatformat:"-3"}}</td>
<td>{{m.QuantityUnitOfMeasure.id}}</td>
<td>{{m.Status.id}}</td></tr> {%endfor%}</tbody>
There are about 10 thousands records. the page response time take about 3 minutes(ThinkServer, Linux, Apache, mod_wsgi, Python3.4, Django 1.9.4, MySQL), is this normal?
Thanks!
Every call to {{ m.XXX.id }} leads to separate SQL-query for the XXX instance. To avoid this you should use the select_related() method of the queryset.
Add the following method to the class of the r variable:
def get_all_m(self):
return self.m_set.all().select_related()
and then use it in the template:
{% for m in r.get_all_m %}
To only access the id of the related models, add the _id suffix to the field name. This data is already available because that's the value of the foreign key, so you don't need extra queries or joins. However, you are accessing the Name property of the StorageLocation field, so you need a join to prevent doing a query in every iteration.
Your call for the data becomes:
r.m_set.select_related('StorageLocation').all()
Your template becomes:
<tbody>
{%for m in r.m_set.all %}
<tr>
<td>{{m.id}}</td>
<td>{{m.Name}}</td>
<td>{{m.MaterialLot_id}}</td>
<td>{{m.MaterialSublot_id}}</td>
<td>{{m.Description|truncatechars:20}}</td>
<td>{{m.StorageLocation.id}} - {{m.StorageLocation.Name}}</td>
<td>{{m.Quantity|floatformat:"-3"}}</td>
<td>{{m.QuantityUnitOfMeasure_id}}</td>
<td>{{m.Status_id}}</td>
</tr>
{%endfor%}
</tbody>
Just remember that if you need access to a field other than id on a related model, add the field name to the select_related() call.

Can't pass object of model in arguments of django custom template filter

I created a custom filter in django template, but django has some constraints in passing no. of arguments, it allows only one or two arguments for custom filter functions Read more. And, i want to pass two arguments to my custom filter one is string and second one is object of model class. But can't able to successfully accomplish this.
index.html
{% load has_permission_filter %}
{% for u in users %}
<span class={{ user|has_location_perm:('user.view_user', u)}}>View</span>
{% endfor %}
In above template user is current logged user and users is a list of instances of User model class.
has_permission_filter.py
def has_location_perm(user, args):
perm_str, obj = args[0], args[1]
// business logic
if user.has_perm(perm_str) and business_logic_check(perm_str, obj):
return 'allow'
else:
return 'not-allow'
So, I want a some sort of solution which helps me in passing object in filter function from django template.
You can't use multiple arguments with filters. You can with a simple tag though.

Django - Paginating in Template using multiple GET parameters

I am using a Django Paginator and I want to have multiple available get parameters, such as:
page=1
sort_by=price
However, in my template tags I have:
Showing items sorted by {{ SORT_PARAM }}.
Showing {{ ITEMS_PER_PAGE }} items per page.
{% if has_prev %}
Previous |
{% endif %}
However, this does not preserve the other GET variables. What I mean is, if I'm viewing
page/?page=1&sort_by=price
and I click the link in the template fragment above, I will go to
page=2
instead of
page=2&sort_by=price
What I mean is, the a href does not preserve the other GET parameters.
One solution is I could type all the possible GET parameters in the a href, such as
Previous
but this will become less scalable the more arguments I want to add to my browsing. I'm guessing there should be an automated way to obtain all GET parameters, and then pass those and one more?
This one http://djangosnippets.org/snippets/1592/ looks cleaner
You can create a 'parameter-string'. Let's supose that in your code you have:
my_view( request, page, options):
sort_choices = {P:'price',N:'name', ...}
n_item_choices = {'S':5, 'L':50, 'XL':100)
ascending_descending_choices = {'A':'', 'D':'-'}
...
then you can concatenat options as:
options='P-S-D' #order by price, 5 items per page, descending order
encode opions as:
Previous
then, in urls.py capture options and in view:
my_view( request, page, options):
... #choides ....
try:
optionsArray = options.split('-')
sort_by = sort_choices[ optionsArray[0] ]
n_ites_page = n_item_choices[ optionsArray[1] ]
asc_or_desc = ascending_descending_choices[ optionsArray[2] ]
...
except:
somebody is playing ....
with this method you are free to add more paginations options without modify urls.py, all you need is to append options at the end of string options . This has advantages but also some dangers: I hope you can identify risks.
With Django's Pagination - preserving the GET params is simple.
First copy the GET params to a variable (in view):
GET_params = request.GET.copy()
and send it to the template in via context dictionary:
return render_to_response(template,
{'request': request, 'contact': contact, 'GET_params':GET_params}, context_instance=RequestContext(request))
Second thing you need to do is use it specify it in the url calls (href) in the template - an example (extending the basic pagination html to handle extra param condition):
{% if contacts.has_next %}
{% if GET_params %}
next
{% else %}
next
{% endif %}
{% endif %}
Source - Posted same answer.

Categories