How access tuple item - django-templates

models.py
class MyModel(models.Model):
GENDER = (('M',"Male"),('F',"Female"))
gender = models.CharField(max_length = 1, choices = GENDER)
template.html
{% for item in mymodels %}
{{ item.GENDER[item.gender] }} #HOW TODO?
{% endfor %}

You can find some answers here
I think that best bet is to use helper function that returns proper value or extend/write new filter.

Related

Shopify - best way to incrementally build up a string in Liquid

I have an array of cities. I want to create a formatted string that can be output to the page... something like New York, Miami and Denver.
In JavaScript, I'd do something like this (rough):
const cities = ['nEW-york','MIAMI FL', 'denver'];
let out = '';
cities.forEach((city, i) => {
if(i === cities.length-1 && i !== 0) {
out += ` and ${formatCity(city)}.`;
} else {
if(i !== 0) {
out += ', ';
}
out += city;
}
});
console.log(out);
You can assume my formatCity() function knows what different kinds of input to expect and how to transform it into a nicely formatted city name (ex nEW-york --> New York).
The first way I thought to do this in Liquid was to use a for loop and capture, which just creates a mess of whitespace and newlines and whatnot.
Instead, I went with an approach similar to how i' do it in JavaScript, using the append filter.
{% assign cities = 'nEW-york,MIAMI FL,denver' | split: ',' %}
{% assign out = '' %}
{% for city in cities %}
{% if forloop.last and forloop.first == false %}
{% assign out = out | append: ' and ' | append: city | append: '.' %}
{% elsif forloop.first == false %}
{% assign out = out | append: ', ' | append: city %}
{% else %}
{% assign out = out | append: city %}
{% endif %}
{% endfor %}
{%- capture prettyCityStr -%}
{%- render 'format-cities', str: out -%}
{% endcapture %}
{{- prettyCityStr -}}
And you can assume that my format-cities snippet does the same as the formatCity() function above. You should also assume that it's not just as simple as throwing the word and in before the last city.
I have no idea if this method (used extensively on any given page... with WAY larger arrays of cities) would cause any performance issues (it seems to me that re-initializing the out variable over and over again may be problematic) or if there's just a more standard way.
I don't work in Liquid often - any input from anyone who does would be greatly appreciated.

specify variable array index for liquid

My liquid code is
{% assign _product_id = product.id %}
{% assign _product_tag = product.collections[0].title}
{% assign _product_name = product.title %}
{{_product_tag}}
{% assign pagla_array = collections[_product_tag].products %}
{{ pagla_array.first.title }}
Here last line showing nothing. if I use a static index for assigning pagla_array like {% assign pagla_array = collections['Beans'].products %} then it show value. What wrong did I make here?
This line:
{% assign _product_tag = product.collections[0].title}
Is not closed correctly. It should end with %}
In addition you should use handles for the collections, not title.
So it should become:
{% assign _product_tag = product.collections[0].handle %}
....
{% assign pagla_array = collections[_product_tag].products %}

Flask-Admin - Inline Model not working with field named something other than 'id'

I am looking at inline models and have been testing out the example here:
https://github.com/mrjoes/flask-admin/tree/master/examples/sqla-inline
I have noticed that if the primary key field of the LocationImage model/table is renamed to something other than ID, then the after_delete handler does not get triggered.
So this works
class LocationImage(db.Model):
id = db.Column(db.Integer, primary_key=True)
alt = db.Column(db.Unicode(128))
path = db.Column(db.String(64))
location_id = db.Column(db.Integer, db.ForeignKey(Location.id))
location = db.relation(Location, backref='images')
#event.listens_for(LocationImage, 'after_delete')
def _handle_image_delete(mapper, conn, target):
try:
if target.path:
os.remove(op.join(base_path, target.path))
except:
pass
But if I rename the id column like so,
image_id = db.Column(db.Integer, primary_key=True)
Then _handle_image_delete does not get called.
I cannot fathom where this field is specified and how to make it work with a PK named something other than 'id'.
Thank you
In field_list.html a hidden field is rendered for the primary key. You need to change it to output your renamed field {{ field.form.image_id }}.
{% import 'admin/model/inline_list_base.html' as base with context %}
{% macro render_field(field) %}
{% set model = field.object_data %}
{% if model and model.path %}
{{ field.form.image_id }}
<img src="{{ url_for('static', filename=model.path) }}" style="max-width: 300px;"></img>
{% else %}
{{ field }}
{% endif %}
{% endmacro %}
{{ base.render_inline_fields(field, template, render_field) }}

using Liquid variables inside of a liquid tag call

I made a custom link tag in Liquid and I am trying to be able to pass liquid variables into the call for that tag like so
{{ assign id = 'something' }} // this value is actual dynamic while looping through data
{% link_to article: id, text: 'Click Me!' %} // my custom tag
However this results in the article parameter being passed in as 'id' instead of 'something' as per the assign statement above it.
Does anyone know how to pass variables into tag calls?
I've recently solved this very simply with Jekyll 0.11.2 and Liquid 2.3.0 by passing the name of the variable as the tag parameter.
{% assign v = 'art' %}
{% link_to_article v %}
You can also pass the name of the control var while in a loop, like article above.
In Liquid::Tag.initialize, #markup is the second parameter, the string following the tag name. The assigned variables are available in the top level of the context.
def render(context)
"/#{context[#markup.strip]}/"
end
This obviously only allows one param to be passed. A more complex solution would parse params like x: 2, y: 3.
This solved the case for me context[#markup.strip].
My problem was that i wanted to be able to pass a variable to my custom Liquid tag like this: {% get_menu main_menu navigation.html settings.theme.id %}
In order to do this i first split the variable string into different varaibles on every space character.
class GetMenu < Liquid::Tag
include ApplicationHelper
def initialize(tag_name, variables, tokens)
#variables = variables.split(" ")
#menu_object = #variables[0]
#file_name = #variables[1]
#theme_id = #variables[2]
super
end
def render(context)
# This is where i use context[#theme_id.strip] to get the variable of "settings.theme.id"
content = CodeFile.find_by(hierarchy: 'snippet', name: #file_name.to_s, theme_id: context[#theme_id.strip])
#menu ||= Menu.find_by_slug(#menu_object)
context.merge('menu' => #menu)
Liquid::Template.parse(content.code).render(context)
end
end
Liquid::Template.register_tag('get_menu', GetMenu)
*This is just a more rich example that the answer above by Jonathan Julian
Doesn't look like this is possible, my solution was to just pass the variable name in to the tag and grab it out of the context the tag is being rendered in. Like so:
{% for article in category.articles %}
{% link_to variable: article, text: title %}
{% endfor %}
in my tag code (condensed):
def render(context)
uri = "article/#{context[#options[:variable]]['id']}"
"<a href='#{uri}'>#{build_link_text context}</a>"
end
It would be great to have a tag that can be called with literals and variables like
{% assign v = 'art' %}
{% link_to_article v %}
or
{% link_to_article 'art' %}
or
{% link_to_article "art" %}
and also of course
{% link_to_article include.article %}
In order to so I propose a helper function
def get_value(context, expression)
if (expression[0]=='"' and expression[-1]=='"') or (expression[0]=="'" and expression[-1]=="'")
# it is a literal
return expression[1..-2]
else
# it is a variable
lookup_path = expression.split('.')
result = context
puts lookup_path
lookup_path.each do |variable|
result = result[variable] if result
end
return result
end
end
And in the render just call the helper function to get the value of the literal or variable.
def render(context)
v = get_value(context, #markup.strip)
end
FYI, the initialiser would look like this:
def initialize(tag_name, markup, tokens)
#markup = markup
super
end
This does not strictly answer the question, but it may help others who are new to Liquid (like myself) and try something like this. Instead of implementing a custom tag, consider implementing a custom filter instead. Variables are resolved before they are passed into filters.
Ruby code:
module MyFilters
def link_to_article(input, text)
"<a href='https://example.org/article/#{input}'>#{text}</a>"
end
end
Liquid::Template.register_filter(MyFilters)
Liquid template:
{% assign id = 'something' %}
{{ id | link_to_article: 'Click Me!' }}
Output:
<a href='https://example.org/article/something'>Click Me!</a>
You can also use variables as parameters. So the following would have the same output:
{% assign id = 'something' %}
{% assign text = 'Click Me!' %}
{{ id | link_to_article: text }}
And filters can have zero or more (comma-separated) parameters:
{{ 'input' | filter_with_zero_parameters }}
{{ 'input' | filter_with_two_parameters: 'parameter 1', 'parameter 2' }}

Django template tag for Model query result

I wonna simple tag, for showing table of any model arrays like:
{% table city_list %}
Do anybody see such things?
You can try django-tables app, which allows you to do the following, given model Book:
# Define
class BookTable(tables.ModelTable):
id = tables.Column(sortable=False, visible=False)
book_name = tables.Column(name='title')
author = tables.Column(data='author__name')
class Meta:
model = Book
# In your views
initial_queryset = Book.objects.all()
books = BookTable(initial_queryset)
return render_to_response('table.html', {'table': books})
# In your template table.html
<table>
<!-- Table header -->
<tr>
{% for column in table.columns %}
<th>{{ column }}</th>
{% endfor %}
</tr>
<!-- Table rows -->
{% for row in table.rows %}
<tr>
{% for value in row %}
<td>{{ value }}</td>
{% endfor %}
</tr>
{% endfor %}
</table>
I think the above is much more elegant and self explanatory than just doing
{% table book_list %}
Try generic vieww e.g. http://www.djangobook.com/en/2.0/chapter11/
I've made a fork of django-tables which makes this extremely easy. Here's a simple example:
In models.py:
from django.db import models
class City(models.Model):
name = models.CharField(max_length=200)
state = models.CharField(max_length=200)
country = models.CharField(max_length=200)
In tables.py:
import django_tables as tables
from .models import City
class CityTable(tables.Table):
class Meta:
model = City
In views.py:
from django.shortcuts import render_to_response
from django.template import RequestContext
from .models import City
from .tables import CityTable
def city_list(request):
queryset = City.objects.all()
table = CityTable(queryset)
return render_to_response("city_list.html", {"city_table": table},
context_instance=RequestContext(request))
In city_list.html:
{% load django_tables %}
{% render_table city_table %}