Django template conditional statement - django

I have the conditional statement below
{% if juice.slug != "slug-one" or "slug-two" %}
rendering things if the page slug isn't slug-one or slug-two
{% endif %}
For some reason this conditional statement only works when it isn't "slug-one", instead of when it isn't "slug-one" or "slug-two" .

Short answer: use if juice.slug != "slug-one" and juice.slug != "slug-two".
The statement juice.slug != "slug-one" or "slug-two" is always True. Python evaluates the truthiness of expressions, and a non-empty string has truthiness True.
You are looking for a condition:
{% if juice.slug != "slug-one" and juice.slug != "slug-two" %}
rendering things if the page slug isn't slug-one or slug-two
{% endif %}
So you have to repreat the juice.slug != part, and the operator in between is and, not or. If we use or, then the statement is still always True, since:
slug | slug != "slug-one" | slug != "slug-two" | with and | with or
--------------------------------------------------------------------------
"slug-one" | False | True | False | True
"slug-two" | True | False | False | True
other | True | True | True | True
So if you use or, each time at least one of the two statements is True, since if the string is equal to "slug-one", then of course it is not equal to "slug-two" and vice versa.

Related

Optimize code of a function for a search filter in django with variable numbers of keywords - too much code, i'm a beginner

Hello great community,
i'm learning django/python development, i'm training myself with development of a web app for asset inventory.
i've made a search filter, to give result of (for example) assets belonging to a specific user, or belonging to a specific department, or belonging to a specific brand, model or category (computers, desks, ecc..) there are many fields that mostly are foreign tables, main table is "Cespiti" that mean Asset in italian
now (after a lot) i've done with multiple keyword search (for example) somebody type in the search box the department and the category and obtain the relative results (for example all desks in a specific department, or all computer of a specific model in a specific department).
i've made it in a "if" check form that split the keyword in single words, count it and apply progressive filtering on the results of the previous keys in sequence.
but i'm not satisfact of my code, i think it's too much "hardcoded" and instead of creating an IF condition for each number of keyword (from 1 to 3) i wish like to code something that is not so dependent in the number of keyword, but is free.
Here's the code of the view, i hope someone can give me the right direction.
def SearchResults(request):
query = request.GET.get('q')
chiave =query.split()
lunghezza = int((len(chiave)))
if lunghezza == 1:
object_list = Cespiti.objects.filter(
Q(proprietario__cognome__icontains=chiave[0]) |
Q(proprietario__nome__icontains=chiave[0]) |
Q(categoria__nome__icontains=chiave[0]) |
Q(marca__nome__icontains=chiave[0]) |
Q(modello__nome__icontains=chiave[0]) |
Q(reparto__nome__icontains=chiave[0]) |
Q(matricola__icontains=chiave[0])
).distinct
elif lunghezza == 2:
object_list = Cespiti.objects.filter(
Q(proprietario__cognome__icontains=chiave[0]) |
Q(proprietario__nome__icontains=chiave[0]) |
Q(categoria__nome__icontains=chiave[0]) |
Q(marca__nome__icontains=chiave[0]) |
Q(modello__nome__icontains=chiave[0]) |
Q(reparto__nome__icontains=chiave[0]) |
Q(matricola__icontains=chiave[0])
).filter(Q(proprietario__cognome__icontains=chiave[1]) |
Q(proprietario__nome__icontains=chiave[1]) |
Q(categoria__nome__icontains=chiave[1]) |
Q(marca__nome__icontains=chiave[1]) |
Q(modello__nome__icontains=chiave[1]) |
Q(reparto__nome__icontains=chiave[1]) |
Q(matricola__icontains=chiave[1])
).distinct
elif lunghezza == 3:
object_list = Cespiti.objects.filter(
Q(proprietario__cognome__icontains=chiave[0]) |
Q(proprietario__nome__icontains=chiave[0]) |
Q(categoria__nome__icontains=chiave[0]) |
Q(marca__nome__icontains=chiave[0]) |
Q(modello__nome__icontains=chiave[0]) |
Q(reparto__nome__icontains=chiave[0]) |
Q(matricola__icontains=chiave[0])
).filter(Q(proprietario__cognome__icontains=chiave[1]) |
Q(proprietario__nome__icontains=chiave[1]) |
Q(categoria__nome__icontains=chiave[1]) |
Q(marca__nome__icontains=chiave[1]) |
Q(modello__nome__icontains=chiave[1]) |
Q(reparto__nome__icontains=chiave[1]) |
Q(matricola__icontains=chiave[1])
).filter(Q(proprietario__cognome__icontains=chiave[2]) |
Q(proprietario__nome__icontains=chiave[2]) |
Q(categoria__nome__icontains=chiave[2]) |
Q(marca__nome__icontains=chiave[2]) |
Q(modello__nome__icontains=chiave[2]) |
Q(reparto__nome__icontains=chiave[2]) |
Q(matricola__icontains=chiave[2])).distinct
context = {
'object_list': object_list, 'query' : query,
}
return render(request, 'search_results.html', context=context)
One way you could do it would be to separate the step of building the Q objects from the view method. That way it could be performed in a loop:
def generate_search_query_params(word):
return (
Q(proprietario__cognome__icontains=word) |
Q(proprietario__nome__icontains=word) |
Q(categoria__nome__icontains=word) |
Q(marca__nome__icontains=word) |
Q(modello__nome__icontains=word) |
Q(reparto__nome__icontains=word) |
Q(matricola__icontains=word)
)
def SearchResults(request):
query = request.GET.get('q')
queryset = Cespiti.objects.all()
for word in query.split():
queryset = queryset.filter(
generate_search_query_params(word)
)
object_list = queryset.distinct()
context = {
'object_list': object_list, 'query' : query,
}
return render(request, 'search_results.html', context=context)
thanks, i really appreciate your suggestion,
i reach to insert in a loop, now number of keywords is unlimited and it's not hardcode ( thanks a lot), i've thinked about Abdul idea and Damon solution, i wish to avoid the initial ".object.all() so i've arranged in this way: the first "level" is fixed, so i can avoid the .all() and all sublevels of filtering are looped, what do you think about?
def SearchResults(request):
query = request.GET.get('q')
chiave =query.split()
lunghezza = int((len(chiave)))
object_list = Cespiti.objects.filter(
Q(proprietario__cognome__icontains=chiave[0]) |
Q(proprietario__nome__icontains=chiave[0]) |
Q(categoria__nome__icontains=chiave[0]) |
Q(marca__nome__icontains=chiave[0]) |
Q(modello__nome__icontains=chiave[0]) |
Q(reparto__nome__icontains=chiave[0]) |
Q(matricola__icontains=chiave[0])
)
for I in range(1,lunghezza):
print(I)
object_list = object_list.filter(
Q(proprietario__cognome__icontains=chiave[I]) |
Q(proprietario__nome__icontains=chiave[I]) |
Q(categoria__nome__icontains=chiave[I]) |
Q(marca__nome__icontains=chiave[I]) |
Q(modello__nome__icontains=chiave[I]) |
Q(reparto__nome__icontains=chiave[I]) |
Q(matricola__icontains=chiave[I])
)
context = {
'object_list': object_list, 'query' : query,
}
return render(request, 'search_results.html', context=context)

Unable to check variable greater than condition in Ansible jinja2 template

My jinja2 template yields correct value for the variable
{{ vars[fruit | join("")] | default('ERR') }}
The variable fruit has value 83.6 and it gets printed by Ansible's template module.
I wish to write an if condition in jinja2 template where I want to check if the value of the variable
fruit is more than 70
{% if ( vars[fruit | join("")] | int ) > 70 %}
MORE THAN 70
{% endif %}
However, the 'if' the condition fails when I expect it to succeed.
I also tried the following:
{% if ( vars[fruit | join("")] | int > 70 ) %}
I also tried
{% if vars[fruit | join("")] | int > 70 %}
But, none of them worked. Can you please let me know what needs to be done to meet the if condition?
The int filter does not accept a string with a dot. You should convert it to a float instead by rounding it down with the round filter:
{% if ( vars[fruit | join("")] | round(method='floor')) > 70 %}

Wrapping long text sections in Jinja2

I have the definition of a variable, it's name and an associated comment in a YAML file and am trying to use Jinja2 to create an appropriate target file; in this case a proprietary config file
...
- comment: >
This is a comment which will almost certainly end up longer than standard eighty characters or at least on the occasion on which it does.
name: my_useful_variable
value: /a/long/example/path/to/my/file.txt
I would like this text to be rendered as follows:
# This is a comment which will almost certainly end up
# longer than standard eighty characters or at least on
# the occasion on which it does.
my_useful_variable = "/a/long/example/path/to/my/file.txt"
Does Jinja2 have any way of wrapping text so that the long comment line is limited in length and split over however many lines is necessary?
So far I have:
# {{item.comment}}
{{item.name}} = "{{item.value}}"
But this of course does not deal with the length of the comment.
Solution
Following on from the answer provided by #blhsing below, I came up with the following macro, which works fine for basic variables and simple lists (i.e. not dictionaries or more complex hierarchical data structures:
{% macro set_params(param_list, ind=4, wid=80) -%}
{% for item in param_list %}
{% if item.comment is defined %}{{item.comment|wordwrap(wid - ind - 2)|replace('', ' ' * ind +'# ', 1)|replace('\n', '\n' + ' ' * ind + '# ')}}
{% endif %}
{% if item.value is iterable and item.value is not string %}{{item.name|indent(ind, True)}} = [ {% for item_2 in item.value %}{{item_2}}{{ ", " if not loop.last else " " }}{% endfor %}{% else %}{{item.name|indent(ind, True)}} = {{item.value}}{% endif %}
{% endfor %}
{%- endmacro %}
To use this, simply pass a list of items similar to the spec given at the top together with the indentation and the page width.
A bit of explanation:
Line 3, If comment is defined then it is word wrapped to the correct length bearing in mind the width and the indent. The first replace deals with indenting the first line and the second indents subsequent lines. All prefixed with '# '
Line 5, depending on whether the variable is simple or iterable, it is rendered in the form name = value or name = [ value1, value2, value3 ]
Of course, it is not fool-proof but it meets my basic requirements.
You can prepend the given string with a newline character, then use the wordwrap filter to wrap the text into multiple lines first, and use the replace filter to replace newline characters with newline plus '# ':
{{ ('\n' ~ item.comment) | wordwrap(78) | replace('\n', '\n# ') }}
The above assumes you want each line to be no more than 80 characters. Change 78 to your desired line width minus 2 to leave room for '# '.
If you're doing this in Ansible, another option is to use the Ansible comment filter:
{{ item.comment | wordwrap(78) | comment }}
or, for more detailed control
{{ item.comment | wordwrap(78) | comment(decoration="# ", prefix="", postfix="") }}
Docs: https://docs.ansible.com/ansible/latest/user_guide/playbooks_filters.html#adding-comments-to-files

Django Template Save operation result into a variable

I have a listing counter "2370" and the page only shows "12" item so I want to calculate the pages number, I did the solution below
{% widthratio listing.total_count 12 1 %}
so, how I can save the result into a variable?
{% set total_pages = widthratio listing.total_count 12 1 %}
this one didn't work
If would choose Rohans comment, but if you would write your own templatetags, use an assignment_tag (https://docs.djangoproject.com/en/1.7/howto/custom-template-tags/#assignment-tags, exists since Django 1.4).
#register.assignment_tag
def page_numbers(total_items, items_per_page):
if not total_items:
return 0
# int divisions round down (1 / 12 = 0), so just add 1 to cover this
return total_items / items_per_page + 1
Within the template you should use;
{% page_numbers listing.total_count 12 as page_nrs %}

Split before decimal in django

i have a variable which fetches the data from database
{{i.rewardpoints}}
and the values it returns such as 1.799 or 12 the db has multiple values which contains decimals and without decimals
but i need to show the values without decimals
how can i do this
To round to nearest integer:
{{ i.rewardpoints|floatformat:"0" }}
To get the integer part:
{{ i.rewardpoints|stringformat:"d" }}
The floatformat filter documentation
The stringformat filter documentation
In [19]: tpl = Template('{{ x|stringformat:"d" }} {{ x|floatformat:"0" }}')
In [20]: tpl.render(Context({'x': 1.1}))
Out[20]: u'1 1'
In [21]: tpl.render(Context({'x': 1.9}))
Out[21]: u'1 2'