I have added a metafield to a collection within my Shopify store. I have a namespace, key, and value. I am looking for a liquid code snippet to check if the collection has a specific metafield key, and then if so, to output the assigned value.
I have tried the following with no success:
{% if relationship = collection.metafields.parent %}
<span>{{ relationship.parent[value] }}</span>
{% endif %}
Does anyone have any idea of how I could implement this functionality?
Thanks.
I guess if you wanted to check for the existence of a metafield, or more specifically the existence of a value within a metafield, you could do:
{% if collection.metafields.parent['metafield_name'] %}
<span>{{ collection.metafields.parent['metafield_name'] }}</span>
{% endif %}
This is taking the value of whatever is contained in the metafield and checking if it returns a truthy or falsey type value. If the value is truthy (if there's text in the metafield) it will then output that to the screen.
Related
I'd like to list a series of items separated by commas.
But if an item after the first doesnt exist, I don't want the comma or item to appear.
So I've implemented this:
<em>{{object.item1|default_if_none:""}}</em>
{% if object.item2 %}
<em>, {{object.item2|default_if_none:""}},</em>
{% endif %}
<em>{{object.item3|default_if_none:""}}</em>
If object.item2 exists, it'll put a whitespace after item1--before the comma.
When it displays, it'll look something like:
"I_am_item_one , I_am_item_two, Item_three"
What's the best way to fix this?
edit: is it possible to for-loop through an object's properties?
{% for property in object.property %} or similar..
I am not aware of any way to loop over objects and their properties in a template. You could look into either passing the data into the template as a dictionary/list or creating a model method (as done in this answer: Django templates: loop through and print all available properties of an object?)
Once your data is iterable, you could use the following code to decide when to add a comma. I am not entirely clear if you want a comma between each item or only after the first item, so here are two options.
This will only add a comma on the first entry
{% for item in item_list %}
<em>{{ item }}</em>{% if forloop.first %}, {% endif %}
{% endfor}
This will add a comma after each item except the last one
{% for item in item_list %}
<em>{{ item }}</em>{% if not forloop.last%}, {% endif %}
{% endfor}
It is probably fairly clear, but you can use forloop.first to detect when you are on your first item and forloop.last to detect when you are on the last item of a loop.
I am experimenting with using Grav to create my next website. One of the things I would be able to do is to build a unordered list using data provided in Grav frontmatter from the Grav page that uses the template. Here is how I am trying to do this
---
sectionone:
listitems: "['Benefit 1','Benefit 2','Benefit 3']"
---
and then in the template somehow do the following
{% block featurelist %}
<ul>
{% set items = {{page.header.sectionone.consumers.benefits|json_decode}} %}
{% for item in {{}} %}
<li>{{item}}</li>
{% endfor %}
</ul>
{% endblock %}
However, Twig does not like this and reports back an error along the lines of
Twig_Error_Syntax
A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "punctuation" of value "{".
with the offending line being my {% set items = ... } statement. I am clearly doing something wrong here but I am a Twig newbie so I fail to see what that might be.
{% block featurelist %}
<ul>
{% set items = page.header.sectionone.consumers.benefits|json_decode %}
{% for item in items %}
<li>{{item}}</li>
{% endfor %}
</ul>
{% endblock %}
I figured this out eventually. Grav documentation is by and large very good. However, the documentation on page headers/frontmatter appears to be somewhat incomplete. There is no full description of the entire syntax that is understood by the frontmatter processor. In order to define an array in front matter all you need to do is the following
---
sectionone:
benefits:
- 'Benefit 1'
- 'Benefit 2'
- ...
---
In essence the standard markdown syntax for an unordered list. Grav's twig processor appears to convert this to a PHP array - no parsing required!
I am generating some Django template code on the fly, in order to display rows in tables that are not stored in
a Django database and do not have models. I know the database and I can introspect them if needed, but I don't want
to write code by hand.
For example, field PSOPRDEFN.OPRCLASS stores an optional reference to a particular row where PSCLASSDEFN.OPRID=PSOPRDEFN.OPRCLASS, essentially a foreign key relationship. If there is no relationship PSOPRDEFN.OPRCLASS has one ' ' (space character) in it.
I also have a page for a given PSCLASSDEFN row, where the url is:
url(r'^(?i)permissions/(?P<CLASSID>[A-Z0-9_&]{1,50})/$',
'pssecurity.views.psclassdefn_detail',
name="psclassdefn_detail"),
Note that the ?P CLASSID regular expression does not allow for blanks which corresponds to gets stored in the PSCLASSDEFN table - I figure it's safer to limit what the user can put in the url request.
Back to my generated template: I want to hyperlink to the relation, if it exists. I feed my home-grown template generator a json "directive" indicating what I want put into the template (thanks for the inspiration, django-tables2):
....
{
"colname": "LANGUAGE_CD"
},
{
"urlname": "security:psclassdefn_detail",
"colname": "OPRCLASS",
"kwargs": [
{
"colname": "dbr",
"accessor": "dbr"
},
{
"colname": "CLASSID",
"accessor": "inst.OPRCLASS"
}
]
},
...
Some fairly trivial code generation then results in:
<div class="row">
<div class="col-xs-6 fieldlabel" title="LANGUAGE_CD" >Language Code</div>
<div class="col-xs-6 fieldvalue text-left _fv_LANGUAGE_CD">{{inst.LANGUAGE_CD}}</div>
</div>
<div class="row">
<div class="col-xs-6 fieldlabel" title="OPRCLASS" >Primary Permission List</div>
<div class="col-xs-6 fieldvalue _fv_OPRCLASS">
{% if inst.OPRCLASS|slugify %}
{{inst.OPRCLASS}}
{% endif %}
</div>
</div>
My problem is that started getting random Template url resolution errors when displaying some of the PSOPRDEFN data. I eventually tracked it down to the blank OPRCLASS fields in some rows.
In order to avoid this I first added
{% if inst.OPRCLASS %}
<a ...></a>
{% endif %}
That didn't work because the field is not empty, it is blank (and therefore doesn't match the CLASSID regex). So, this is where I read the filter docs again and found that slugify strips out blanks and non-alpha.
{% if inst.OPRCLASS | slugify %}
<a ...></a>
{% endif %}
Works, as a workaround. The problem is that CLASSID only stores alphanum, but that's not always true for other fields. I wouldn't mind introspecting the table column definition at template generation runtime to see what to do, but I need to find an appropriate way to disable url reversal, for only some rows.
Questions. Is there a better filter, such as a |strip? I suppose I could always build my own filter.
Even better, is there a tag to selectively catch NoReverseMatch' exceptions at template generation time?
{% try NoReverseMatch %}
{{inst.OPRCLASS}}
{% endtry %}
The reason I was so verbose in my description is because this is not something that can be worked around using Models. And neither can I custom-tune the template by hand. I find Django works quite well without models in most cases, but url reversing in templates can be quite brittle when a few rows of data do not match expectations. Hardening it would be very beneficial.
You can assign the result of the url tag to a variable.
{% url 'path.to.view' arg arg2 as the_url %}
{% if the_url %}
link
{% else %}
No link
{% endif %}
This syntax does not raise an exception if reversing the view fails.
I've these variables:
#Int
user.id
#Float (e.g. X.YYYY)
profile.rating
I need these formatted like so (the ` is delimiter):
`profile.rating`
I've tried numerous ways to format them, but none worked. For example, concatenating them: ""|add:profile.rating|add:"" gave me nothing (literally, nothing).
I suspect that this is because add: is numbers-first, but converting the numbers into string with either slugify or stringformat:"" gave me, again, nothing.
How do I do this?
Do note: I need to do this with filters since the result will be passed as a parameter to an include.
Update:
Basically, I'm building a sort of modular include. It include looks like this:
<section>
...
{% if custom_section %}
<section id="{{ custom_section_id }}">
{{ custom_section }}
</section>
{% endif %}
...
</section>
which means that I can't just directly include the values in a parameter, I need the markup that will go inside the nested section.
I managed to solve this issue with this piece of markup:
{% with "<a href="\"/user/" as link_start %}
{% with profile.rating|stringformat:".1f" as rating %}
{% with user.id|slugify as id %}
{% with link_start|add:id|add:"/\">" as link %}
{% with link|add:rating|add:"/5</a>" as data %}
{% include "XX" with custom_data:data|safe %}
{% endwith %} a couple of times
Key here is the |stringformat:".1f" and the user.id|slugify since without them, djangos worthless templating language defaults on the belief all values are numerical, and thus crap comes out.
Of note is the |safe as well, as without it the language escapes the value.
Do note: I need to do this with filters since the result will be
passed as a parameter to an include.
You can pass them directly to include, as it will take context correctly.
{% include user.id %}
Running into a strange problem with Django's template, and being n00b I don't even know how to debug a template...
PROBLEM: variables of a type list somehow stopped being a list when passed into the template.
In my view, I have a bunch of variables passed to the template that are a dictionary of lists. Here's the code,
VIEW
project_image_design = {}
for p in projects:
project_image_design[p.id] = []
images = UploadedImage.objects.filter(project=p, image_type=UploadedImage.DESIGN)
for i in images:
project_image_design[p.id].append(i)
Here's the context. I have projects, each contains images. I created a dictionary, where the keys are the project id, and the value is a list of images associated with that project.
However, when I use this in the template, things go wrong,
TEMPLATE
{% for p in projects %}
<div class="row">
{% for list in project_image_design|get_item:p.id %}
{% for i in list %}
<div class="col-md-2"><img src = "{% get_static_prefix %}media/{{ i.filename }}"></div>
{% endfor %}
{% endfor %}
</div>
{% endfor %}
So in the template, I'm iterating through projects, then using the project's id (p.id) to get the dictionary value, which is a list of images, and then iterating through that. The fancy get_item tag is just a way to access dictionary values through keys that not straight-forward variables (see: Django template how to look up a dictionary value with a variable).
Anyway, I get this error:
TypeError at /designer/my_projects/
'UploadedImage' object is not iterable
The error occurs on this line: {% for i in list %}, which is the line where I'm iterating through the list of images I retrieved using my project's id.
What's going on here????
I double-checked via pdb in the view, it all checks out. The variable being passed is indeed a dictionary of lists, I put a type on all the individual dictionary elements and they're all lists (like: type(project_image_design[1]) would return <class 'list'>).
Also, at the moment, all the lists are of length 1. I'm wondering maybe the template sort of deflates lists that are size 1? That'd seem like a pretty weird thing to do, probably not the reason.
Any help would be appreciated.
Also, how do I debug templates the way I can debug Python code? Like stepping through and stuff? Is that even possible?
It hasn't stopped being a list. But you have two nested for loops: you iterate through the items you get from the dictionary - confusingly calling each item list - and then attempt to iterate again through items in that "list". But the inner loop makes no sense: you should be doing simply:
{% for i in project_image_design|get_item:p.id %}
<div class="col-md-2"><img src = "{% get_static_prefix %}media/{{ i.filename }}"></div>
{% endfor %}
I'd also point out that your view logic is over-complicated. It could be reduced to simply this:
for p in projects:
project_image_design[p.id] = UploadedImage.objects.filter(project=p, image_type=UploadedImage.DESIGN)
And in fact it could be simplified even further: you don't need the dictionary, or the get_item tag, at all. Instead, provide a method on Project called something like design_images which just returns the images of that type:
def design_images(self):
return self.uploadedimage_set.filter(image_type=UploadedImage.DESIGN)
and removing the dictionary logic from the view altogether, and now your template can just be:
{% for p in projects %}
<div class="row">
{% for i in p.design_images %}
<div class="col-md-2"><img src = "{% get_static_prefix %}media/{{ i.filename }}"></div>
{% endfor %}
</div>
{% endfor %}