django creating template filters - django

views.py
#register.filter(name="avl")
def avl_facilities(obj):
return obj.avl_facilities()
#register.filter
def na_facilities(obj):
return obj.na_facilities()
models.py
class Model(models.Model):
#some code .....
def avl_facilities(self):
item = ['bar','bank','music','wifi','offers','credit']
avl = []
for i in item:
if getattr(self,i) == True:
avl.append(i)
return avl
def na_facilities(self):
item = ['bar','bank','music','wifi','offers','credit']
na = []
for i in item:
if getattr(self,i) == False:
na.append(i)
return na
html
<div class="facility pad10">
{% for item in data.rest|avl %}
/* data.rest is appropriate instance of model defined above*/
<span class="label label-danger mrg2 pad5 pull-left">
{{item|title}}
</span>
{% endfor %}
</div>
errro
Invalid filter: 'avl_facilities'
doubt
i am not able to understand that if the avl_facilities inside the model is returning proper iterable list , but its not working as a template filter , thanks in advance

You can't define filters in views.py. You have to put them in a new file inside a templatetags directory.
You should know though that both of these filters are totally unnecessary. It's quite possible to call model methods from the template, as long as they don't take arguments:
{% for item in data.rest.avl_facilities %}

Related

WTForms - Pass argument to form then create fields if variable exists

Depending on the arguments I pass to the form I want to return different form fields.
class TestForm(FlaskForm):
"""Test Form."""
if one:
field1 = StringField('Field 1')
if two:
field2 = StringField("Field 2")
if three:
field3 = StringField("Field 3")
submit = SubmitField("Add Service")
def __init__(self, one=None, two=None, three=None, *args, **kwargs):
super().__init__(*args, **kwargs)
self.one = one
self.two = two
self.three = three
I am not able to see the arguments when doing the if statements.
I am aware of the option to have logic in html when rendering the form, however due the nature of the project have opted to use quick_form on the html side.
Here is the html code I am using.
{% import 'bootstrap/wtf.html' as wtf %}
<h3 >Add Service</h3>
<div class="row">
<div class="col-md-4">
{{ wtf.quick_form(form) }}
</div>
</div>
Instead of creating this logic in your form class. I would recommend to create all the fields you need and then dynamically choose which to show the user using jinja2 in your html file.
Here's an example.
{% for fields in fields_list %}
{% if field == '1' %}
{{ form.field1.label(class="form-control-label") }}
{{ form.field1(class="form-control form-control-lg") }}
{% endif %}
{% if field == '2' %}
{{ form.field2.label(class="form-control-label") }}
{{ form.field2(class="form-control form-control-lg") }}
{% endif %}
{% if field == '3' %}
{{ form.field3.label(class="form-control-label") }}
{{ form.field3(class="form-control form-control-lg") }}
{% endif %}
{% endfor %}
And then when you render or redirect to your .html from your routes code, don't forget to send
the proper arguments, such as
# create your fields list, which do I want to show?
# Make it a list of integers
fields_list = [1, 2, 3]
return render_template('insert-name.html', fields_list , form=form)
If my answer helped you, please consider accepting my answer.
I am new to this site and trying to build up some reputation :)
Thank you! Happy Coding!
If someone ever comes accross the same question, here is a quick solution I came upon some times ago.
It is in part inspired from the accepted answer here.
In your forms.py or wherever you declare your TestForm class, put the class declaration inside a function that takes your parameter as an argument and returns the class object as output.
The argument will now be accessible within the class itself, allowing for any test you may want to perform.
Here's a working example based on your original question (I just added a default value to get at least one StringField in case the parameter is ommited):
def create_test_form(var='one'):
class TestForm(FlaskForm):
"""Test Form."""
if var == 'one':
field1 = StringField('Field 1')
if var == 'two':
field2 = StringField("Field 2")
if var == 'three':
field3 = StringField("Field 3")
submit = SubmitField("Add Service")
return TestForm()
Then simply create the form in your routes like so:
form = create_test_form('two')
Finally pass it to your HTML to render the form with quick_form like you did.
This example will render a form with a single StringField named "Field 2" and a "Add Service" submit button.

How to get a model by modelname in django template

I have model LandingSnippet that contains attribute ...model = CharField()..., and it is related to context keyword (for example cars in context below)
I have next code in my view
def GeneratedLanding(request):
snippets = LandingSnippet.objects.all().filter(view_on=True).order_by('order')
context = {
'snippets':snippets,
...
'cars':Car.objects.all(), # this is cars
...
return render(request,'qlanding/generateindex.html',{'context':context})
how i can get querySet cars that is in context by keyword cars as a string
for example
{{context}}
prints
{'snippets': <QuerySet [<LandingSnippet: Snippet1Title>, <LandingSnippet: 2 - about - Лучшая служба развозки детей>]>, 'services': <QuerySet []>, 'cars': <QuerySet []>, 'faqs': <QuerySet []>}
and
{{snippet.model}}
prints
cars
QUESTION:
How can i get the {{ context.cars }} ? I think something like context[snippet.model] where snippet.model='cars'
i want push it inside another template when include
{% if snippet.module %}
{% with "qlanding/snippets/module/"|add:snippet.module|add:".html" as path %}
{% include path with model=context[snippet.model] %} # But this is incorect while rendering
{% endwith %}
{% endif %}
you can write a simple template tag like this:
first in your app directory create a directory named templatetags this directory must contains an empty file named __init__.py
create a file with any name in this directory. for example load_from_context
write these code on this file
from django import template
register = template.Library()
#register.tag(name="GetFromContext")
def get_from_context(parser, token):
bits = token.split_contents()
node_list = parser.parse(('endGetFromContext',))
variable = bits[1]
return GetFromContextNode(node_list, variable)
class GetFromContextNode(template.Node):
def __init__(self, node_list, variable):
self.node_list = node_list
self.variable = variable
def render(self, context):
variable_value = template.Variable(self.variable).resolve(context)
with context.push():
context['model'] = context.get(variable_value)
return self.node_list.render(context)
then in your template you can use it like this
{% load load_from_context %}
{# any code in your template #}
{% GetFromContext snippet.model %}
{% include path %}
{% endGetFromContext %}
#vorujack , I get the same error still. but based on your solution I got next.
from Django import template
register = template.Library()
#register.simple_tag
def get_model_from_context(context,model_name):
return context[model_name]
and how I used in view
{% get_model_from_context context=context model_name=snippet.model as model %}
{% include "qlanding/snippets/module/"|add:snippet.module|add:".html" with model=model %}
many thanks for #vorujack

Django: context of modified query list

I have a database with blog style documents, i.e., author's name, publication date, body text, etc.
I have built a django framework to output entries from the database as a result of a search term. That part is ok. The problem is that I want to show sections of the body text with the matched search terms highlighted (equivalent to a google search result). This means that I can't create a template tag with the body_text attribute only, because that text is not highlighted. I already did a function that receives as input the query and the body text and outputs the same text with the found search terms in bold.
My problem now is how do I pass this result to the html template?
Using the tutorial from Django documentation suppose you have the following views.py:
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
context = {'latest_question_list': latest_question_list}
return render(request, 'polls/index.html', context)
and the correspondent template:
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li>{{ question.question_text }}</li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
Now suppose you have the function in views.py:
def signal_tokens(text,query_q):
...
return new_text
What should be the best way to replace the {{ question.question_text } with the output from signal_tokens? My solution was to replicate the context variable with a list of dictionaries, where each dictionary is a copy of each entry, except for the 'question_text' key, where I used signal_tokens result:
def index(request):
query_q = 'test'
latest_question_list = Question.objects.order_by('-pub_date')[:5]
new_context = []
for entry in latest_question_list:
temp_d = {}
temp_d['id'] = entry.id
temp_d['question_text'] = signal_tokens(entry.question_text,query_q)
new_context.append(temp_d)
context = {'latest_question_list': new_context}
return render(request, 'polls/index.html', context)
But the problem is that I need to copy all entries. Is there a more elegant way to solve this problem?
This is an ideal use case for a template filter. Move your highlight code to a file in the templatetags directory, register it as a filter, then you can call it from the template:
{{ question.question_text|highlight:query_q }}
Obviously you will need to pass query_q to the template context as well.

How to target a specific object with Django templates

I have a class called Features in my models.py. In my html, I am displaying a list on the right that excludes two of these Features, one is the active feature that has been selected, the other is the most recently added since they are the main content of my page. The remaining Features in the list are displayed by date and do show what I am expecting.
Now, I want to single out the first, second and third Features (title only) in THAT list so I can place them in their own separate divs - because each has unique css styling. There are probably numerous ways of doing this, but I can't seem to figure any of them out.
This is a link to my project to give a better idea of what I want (basically trying to get the content in those colored boxes on the right.)
I'm just learning Django (and Python really), so thanks for your patience and help!
HTML
{% for f in past_features %}
{% if f.title != selected_feature.title %}
{% if f.title != latest_feature.title %}
<h1>{{ f.title }}</h1>
{% endif %}
{% endif %}
{% endfor %}
VIEWS
def feature_detail(request, pk):
selected_feature = get_object_or_404(Feature, pk=pk)
latest_feature = Feature.objects.order_by('-id')[0]
past_features = Feature.objects.order_by('-pub_date')
test = Feature.objects.last()
context = {'selected_feature': selected_feature,
'latest_feature': latest_feature,
'past_features': past_features,
'test': test}
return render(request, 'gp/feature_detail.html', context)
MODELS
class Feature(models.Model):
title = models.CharField(db_index=True, max_length=100, default='')
content = models.TextField(default='')
pub_date = models.DateTimeField(db_index=True, default=datetime.now, blank=True)
def __str__(self):
return self.title
def __iter__(self):
return [
self.id,
self.title ]
You can either store the first three Features in separate variables in your context or add checks to your template loop like {% if forloop.first %} or {% if forloop.counter == 2 %}.
If all you want is to not have the
selected_feature
latest_feature
these two records out of the past_features queryset, then you can use exclude on the past_features query and pass the id's of the selected_features and latest_feature objects.
The views.py would look like:
def feature_detail(request, pk):
selected_feature = get_object_or_404(Feature, pk=pk)
latest_feature = Feature.objects.order_by('-id')[0]
# Collect all the id's present in the latest_feature
excluded_ids = [record.pk for record in latest_feature]
excluded_ids.append(selected_feature.pk)
#This would only return the objects excluding the id present in the list
past_features = Feature.objects.order_by('-pub_date').exclude(id__in=excluded_ids)
test = Feature.objects.last()
context = {'selected_feature': selected_feature,
'latest_feature': latest_feature,
'past_features': past_features,
'test': test}
return render(request, 'gp/feature_detail.html', context)
Django provides a rich ORM and well documented, go through the Queryset options for further information.
For access to a specific object in Django templates see following example:
For access to first object you can use {{ students.0 }}
For access to second object you can use {{ students.1 }}
For access to a specific field for example firstname in object 4 you can use {{ students.3.firstname }}
For access to image field in second object you can use {{ students.1.photo.url }}
For access to id in first object you can use {{ students.0.id }}

Django-Template : Get Variables in a Tag block !

I need to retrieve an optional number saved in DB , to a custom template tag i made . which to retrieve , a variable ( a photo ID ) included in this Gallery . within the gallery loop .
{% get_latest_photo {{photo.id}} %}
How to accomplish that ?!
P.s : I know that can be done with inclusion tag , but in the present time how to make it fix this one !
Edit the template html file :
{% for album in albumslist %}
{% get_latest_photo photo.id %}
{% for photo in recent_photos %}
<img src='{% thumbnail photo.image 200x80 crop,upscale %}' alt='{{ photo.title }}' />
{% endfor %}
{{ album.title }}
{% endfor %}
templatetag
from django.template import Library, Node
from akari.main.models import *
from django.db.models import get_model
register = Library()
class LatestPhotoNode(Node):
def __init__(self, num):
self.num = num
def render(self, context):
photo = Photo.objects.filter(akar=self.num)[:1]
context['recent_photos'] = photo
return ''
def get_latest_photo(parser, token):
bits = token.contents.split()
return LatestPhotoNode(bits[1])
get_latest_photo = register.tag(get_latest_photo)
P.s Its working very well when i replace album.id (in {% get_latest_photo photo.id %} ) with a number which acts as an album id and retrieve the photo from .
Regards
H. M.
You don't put the brackets around variables when you use them in template tags.
{% get_latest_photo photo.id %}
To evaluate correctly the num variable I think you should modify your LatestPhotoNode class like this:
class LatestPhotoNode(Node):
def __init__(self, num):
self.num = template.Variable(num)
def render(self, context):
num = self.variable.resolve(self.num)
photo = Photo.objects.filter(akar=num)[:1]
context['recent_photos'] = photo
return ''
Are you sure your template tag is written properly? For example, you need to use Variable.resolve to properly get the values of variables: Passing Template Variables to the Tag
I had the same problem problem and after reading the docs, I solved it using this
class LatestPhotoNode(Node):
def __init__(self, num):
self.num = template.Variable(num)
def render(self, context):
num = self.num.resolve(context)
photo = Photo.objects.filter(akar=num)[:1]
context['recent_photos'] = photo
return ''
If you are trying to render multiple variables, using json.dumps is very useful.