Django custom template tag using session - django-templates

I have a UpgradeView with the same possibility for saving like in the django admin. I can save, save and continue editing or save and create a new object.
Each leading to a different view:
DetailView, UpdateView and the CreateView.
After saving I want to give a message out, on every view or template its leading to.
For example "Successfully saved" or "Object could not be saved."
When writing custom template tags it's getting really hard for me, because after I created the tag, I don't know how, where and when to pass the message to the other views.
This is the UpdateView where i come from.
class TopicEditView(UpdateView):
fields = ['title','description',]
model = Topic
...
def get_success_url(self):
if self.request.POST.get('save'):
return reverse('topic_detail', kwargs={'pk':self.object.pk})
elif self.request.POST.get('save_and_continue'):
return reverse('topic_edit', kwargs={'pk':self.object.pk})
elif self.request.POST.get('save_and_create_new'):
return reverse('topic_create')
else:
return reverse('fallback_success_url')
My custom template tag is still empty, because the only examples i saw are pretty hard to understand for me.
#register.inclusion_tag('msg.html', takes_context=True)
def get_msg(context):
return None
Inside 'msg.html' i only have the string saying "Successfully saved", this did lead to nothing and i forgot why i did that.
And this is in my template (nothing):
{% load msg_handler %}
{% get_msg %}
How and where can I pass the message to these views using the session?

Try the Django messages framework instead:
http://docs.djangoproject.com/en/1.8/ref/contrib/messages

I got this faster done than I ever thought.
This is my custom template tag:
#register.simple_tag(takes_context=True)
def get_msg(context):
try:
return context.request.session.pop('msg')
except KeyError:
return ''
And this is my view, passing the message:
class TopicEditView(UpdateView):
...
def get_success_url(self):
self.request.session['msg']='Successfully saved!'
...
Nothing have changed in my template.
If there is a more elegant/useful way, i would appreciate it.
EDIT :
Thanks to Lorenzo Peña's comment, i tried using the messages framework which was really easy to use!
First i went to my views.py again and imported messages
from django.contrib import messages
and changed this line
self.request.session['msg']='Successfully saved!'
To this:
messages.add_message(self.request, messages.SUCCESS, 'Successfully saved!')
Then i made a new template called msg_loader.html containing this:
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
And changed in my other templates this
{% load msg_handler %}
{% get_msg %}
to this
{% include "msg_loader.html" %}

Related

jinja2 error 'list object' has no attribute 'items'

EDIT Upon finding the solution, I changed the title to better reflect the issue. Unstuckify's reply remains valid and relevant to the text of the question
I have a list of dictionaries that I want to loop through with Jinja2:
dict = [{'symbol': 'BTCUSDT', 'price': 59325.1234, 'Qty': 2501}, {'symbol': 'ETHUSDT', 'price': 55.12, 'Qty': 14}]
I've used the loop below (from here). I expected the outer 'for' statement to pick up the first item in the list (which would be a dict) and the inner 'for' loop to iterate through the k,v in the dict.
{% for dict_item in dict %}
{% for key, value in dict_item.items() %}
<h1>Key: {{key}}</h1>
<h2>Value: {{value}}</h2>
{% endfor %}
{% endfor %}
However, Jinja gives me the following error - which suggests Jinja doesn't recognise the elements in the list as dicts:
jinja2.exceptions.UndefinedError: 'list object' has no attribute 'items'
Even heavily simplifed I keep getting the same error:
{% for symbol in dict %}
<h2>{{ symbol }}</h2>
{% endfor %}
The Jinja docs aren't detailed enough on this. I've also tried this and looked at this approach without success. Passing just a dict (not a list of dicts) works well.
Can anybody explain why I'm getting this error and suggest better code? Getting the same error message with different approaches leads me to think there's some fundamental error in my thinking, but I can't figure it out.
What does your code look like on the back end?
I have this in python:
#app.route('/test1')
def test1():
dict = [{'symbol': 'BTCUSDT', 'price': 59325.1234, 'Qty': 2501}, {'symbol': 'ETHUSDT', 'price': 55.12, 'Qty': 14}]
return render_template('test1.html', dict=dict)
This in my .html file:
<!doctype html>
<title>Jinja Test</title>
{% for dict_item in dict %}
{% for key, value in dict_item.items() %}
<h1>Key: {{ key }}</h1>
<h2>Value: {{ value }}</h2>
{% endfor %}
{% endfor %}
My rendered output looks how I would expect it to look with the formatting:
Key: symbol
Value: BTCUSDT
Key: price
Value: 59325.1234
etc.
The issue wasn't with the code, but rather environmental - I changed the title of the question to better reflect the issue.
It turns out that there was most probably an issue with the Flask install, as a re-install (largely) solved the issue - the code above (both in the question and the answer) works as expected.
In addition, during debugging I noticed that Jinja2 in my environment doesn't like commented text in the HTML () and behaves in a non-deterministic way when this is present. Equal code will sometimes throw an error, sometimes it won't and the Jinja2 error message will point to commented out code.
Python 3.9.6, Flask 2.0.1, Jinja2 3.0.1, Chrome 93.0.4577.63, PyCharm 2021.1 on Win10 Home 20H2 19042.1165

shopify I need to check if current page is a collection page and not a single product page

I am trying to check if my current page is a collection page not a single product page in some collection.
I mean for example if someone goes to collection page of shoes then I can check using collection.handle == 'Shoes' but if I select a product from that page then it will still give me true But I want my condition to be true on if it is collection page.
Thanks for your Help!
Use this simple way with template:
{% if template contains 'collection' %}
Do something
{% endif %}
As Shopify evolves you can now use this:
{% if template.name == 'collection' %}
Do something
{% endif %}
A safer alternative to avoid rare cases where templates are badly named would be to use request.page_type - see the Shopify docs
{% if request.page_type == 'collection' %}
Do something
{% endif %}
I would use:
{% if template == 'collection' %}
Do something
{% endif %}
Because if you use contains and not == (equal to) you may run risk of it changing down the line if you ever created a new template which contained the word collection?
You can try this in condition.
{%if request.page_type == "collection" %}
--- True if visit collection page or sub collection page. ---
{% endif %}
Thanks.

{% if %} tag for showing strikethrough

Let's have a DetailView with a model Person. Let's suppose year_of_birth = None for this Person instance.
Can Django template language organize something like this?
{% with "---" as strikethrough %}
<p>Year of birth: {% object.year_of_birth or strikethrough %}
{% endwith %}
I've experimented with curly brackets. Anyway I get something like this:
Exception Type: TemplateSyntaxError
Exception Value:
Invalid block tag on line 9: '{strikethrough}', expected 'endwith'. Did you forget to register or load this tag?
Well, is the idea of using 'or' in such case viable or I must use {% if %} tag?
You can use the default
or default_if_none template tag:
...
<p>Year of birth: {% object.year_of_birth|default:strikethrough %}</p>
...
Also take a look at firstof for logic with more than two options.

show item only on collection pages

on shopify, i want a line of code of code from theme.liquid to show only on collection pages regardless the template used (i have a few custom collection templates). how do i write the code to do this?
currently i'm using.
{% if template == 'collection.home' %}
<div class="filter_btn">
loremipsum
</div>
{% endif %}
{% if template == 'collection.featured' %}
<div class="filter_btn">
loremipsum
</div>
{% endif %}
similar duplicate of code above for other collection template
i will be adding new collections with new custom templates in the future, but that means i have to duplicate more of the same to cover new collection templates..
how can i have a line of code that covers all collection templates / collection pages
Use the contains operator instead:
{% if template contains 'collection' %}
I am likely a collection template
{% endif %}
See more about contains here:
http://docs.shopify.com/themes/liquid-documentation/basics/operators#contains

Shopify Add Custom field to Collection Page in Admin

Hello Shopify Developers.
I'm a newbie on Shopify. I want to build a menu just like http://www.nastygal.com/. It shows menu items and featured products in menu area.
Would you give me a suggestion how to make a menu like this?
I'm not sure this is the best idea, though I think that I can create special collections to assign menus. I want to add a custom field in collection page to assign category to special menu. I noticed that I may use meta-fields for this but not sure.
How to add meta-fields to description fields in collection admin page?
How to get values from meta-fields in front page?
I'm open for suggestions, please teach me.
Best regards, Lorant.
I'd say there was a couple of steps to making this work, but it shouldn't be hard.
Create a collection that contains the products in it that you would like to show in the menu.
In your navigation link list create a link that points to collection you want to show in the menu. Call it something like show-products.
The menu liquid would look something like this:
{% assign linklist = linklists.main-menu %}
{% for link in linklist %}
{% if link.title == 'show-products' and link.type == 'collection_link' %}
{% assign menu_collection = link.object %}
{% for menu_product in menu_collection.products %}
{{ menu_product.featured_image | product_img_url: 'compact' | img_tag: menu_product.title }}
{{ menu_product.title }}
{% endfor %}
{% else %}
{% include 'my-normal-menu-link' with link %}
{% endif %}
{% endfor %}
Note: this code is untested, but I don't see why it wouldn't work.