Home » Odeon Blogs »

Liviu, Agile Bear

Django Template Tag vs Kay Framework Processors

In our previous post we discovered how to obtain in Kay templates the same behavior as Django's inclusion tag. Another question pops in. What about template tags?

What is a template tag?

"A template tag is code that displays information dynamically in the template."

In other words, it's a method that handles the data based on the context and outputs it in the desired manner. The Django docs example is pretty clear:

  1. <p>The time is {% current_time "%Y-%m-%d %I:%M %p" %}.</p>

  2. #output
  3. The time is 2010-09-03 11:13 PM

Kay framework doesn't allow you do import .py files to use their functions inside a template, because it's not transforming it in a list of nodes, like Django does.

CONTEXT_PROCESSORS

"Much like Django’s context processors Kay allows you to define a
number of functions that are run when templates are rendered that
make commonly used data available to your template
."

This is exactly what we have been searching for. Let's see how to define such a context processor and how to use it in a template for the Django example:

  1. # my_app.utils - defining the method
  2. import datetime
  3. def current_time(format_string = "%Y-%m-%d %I:%M %p")
  4. return datetim.datetime.now().strftime(format_string)

  5. # my_app.context_processors - create the processor
  6. from utils import current_time
  7. def current_time_processor(request):
  8. return {'current_time': current_time}

  9. # settings - include the processor
  10. CONTEXT_PROCESSORS = (
  11. ...
  12. 'my_app.context_processors.current_time_processor'
  13. )

  14. # template usage
  15. <p>The time is {{ current_time("%Y-%m-%d %I:%M %p") }}.</p>

  16. # output
  17. The time is 2010-09-03 11:13 PM

Applying processors for single view, not for the whole project

If the processor will only be used in a few templates, you can skip the part where we include it in the settings.CONTEXT_PROCESSORS tuple and send it when rendering the view's response:

  1. # my_app.views
  2. from kay.utils import render_to_response
  3. from context_processors import current_time_processor

  4. def current_time(request):
  5. return render_to_response('my_app/current_time.html', {}, processors=(current_time_processor,))

The only functionality that is missing is setting a variable in the context and there doesn't seem to be a solution for it.

Note: The returned value of your function will need the |safe filter applied to it to avoid HTML escaping.

Categories: GAE Kay Framework


Tagged as: context_processor django kay framework template tag

Leave a Comment

Django inclusion tag for Kay framework

Django inclusion tag helps you when you need to repeat the same HTML block for several times or keeping your design consistent site-wide.

How can you do the same in Kay framework? Is there such an option?

Well, first of all, you need to search in the right place, like the docs say:
Kay uses Jinja2 for rendering HTML templates.
So let's check the Jinja2 Template Designer Documentation. This is where the solution can be found: Macros

Macros

Macros are comparable with functions in regular programming languages. They are useful to put often used idioms into reusable functions to not repeat yourself.
This is exactly what we were searching for.

Lets take the polls results example from Django docs and transform it in a Kay/Jinja2 macro:

Django

  1. # polls.templatetags.poll_tags
  2. from django import template
  3. register = template.Library()

  4. @register.inclusion_tag('results.html')
  5. def show_results(poll):
  6. choices = poll.choice_set.all()
  7. return {'choices': choices}

  8. # templates/polls/results.html
  9. <ul>
  10. {% for choice in choices %}
  11. <li> {{ choice }} </li>
  12. {% endfor %}
  13. </ul>

  14. # templates/polls/view_poll.html
  15. {% load poll_tags %}
  16. {% show_results poll %}

Kay framework

  1. # templates/polls/macros.html
  2. {% macro show_results(poll) %}
  3. <ul>
  4. {% for choice in poll.choices_set.fetch(1000) %}
  5. <li> {{ choice }} </li>
  6. {% endfor %}
  7. </ul>
  8. {% endmacro %}

  9. # templates/polls/view_poll.html
  10. {% from "polls/macros.html" import show_results %}
  11. {{ show_results(poll) }}

First obvious difference, in Kay, the code doesn't stay in a separate .py file, but in an .html one which needs to be imported directly in the template where it is being used.

Unfortunately, if you need to handle the arguments in some special way, you are limited by normal template syntax only and this might not be the right solution.

Special macro + call

Jinja also comes with something extra by using macro + call together.  This is like calling a macro inside another macro and it is
useful as replacement for loops. 

  1. {% macro dump_users(users) %}
  2. <ul>
  3. {% for user in users %}
  4. <li><p>{{ user.username|e }}</p>{{ caller(user) }}</li>
  5. {% endfor %}
  6. </ul>
  7. {% endmacro %}

  8. {% call(user) dump_users(list_of_user) %}
  9. <dl>
  10. <dl>Realname</dl>
  11. <dd>{{ user.realname|e }}</dd>
  12. <dl>Description</dl>
  13. <dd>{{ user.description }}</dd>
  14. </dl>
  15. {% endcall %}

Categories: GAE Kay Framework


Tagged as: django inclusion tag kay framework macro

Leave a Comment

Calling instance methods with arguments inside Django templates

What to do in those strange situations where you need to use an instance's method, which needs at least one argument, in the template? You can't always generate the values from the views.py and send them in variables, imagine a for loop where you need to get it.

The easy way to do it is using Django filters.

Three solutions. Pick the one that fits your needs.

There are three solution presented in this article:
1. calling one method only which requires one argument;
2. calling one method only which requires more string/int arguments;
3. calling any method which requires one or more arguments.

The no argument case is a simple {{ instance.method }} call, so there is no point discussing it.

To illustrate our examples, we'll use the following model and call the template_call() method:

  1. from django.db import models

  2. class SomeObject(models.Model):
  3. some_field = models.TextField()

  4. def template_call(self, *args):
  5. """ this is the method that will be called """
  6. return ", ".join(args)

Calling one method only which requires one argument

The simplest case. We only need to call one method which requires only one argument. A normal filter is enough since we can hardcode the method's name in it and send the attribute as a parameter:

  1. #template_filters.py
  2. from django import template
  3. register = template.Library()

  4. @register.filter
  5. def test_simple_template_call(instance, args):
  6. """
  7. allows only one argument for one exact method
  8. """
  9. return instance.test_template_call(args)

  10. # template usage
  11. {{ instance|test_simple_template_call:"value1" }}

  12. # output
  13. value1

Calling one method only which requires more string/int arguments

This approach is similar to the one before, the difference is that the arguments will be passed as key=value pairs separated by '&'. To resolve them, we will use the same class Django uses to parse query strings out of URL's, since it is the same structure.

  1. #template_filters.py

  2. from django.http import QueryDict
  3. @register.filter
  4. def test_advanced_template_call(instance, args):
  5. """
  6. allows more arguments like a query dict:
  7. var_name=value&var_name2=value
  8. """
  9. qd = QueryDict(args)
  10. return instance.test_template_call(*qd.values())

  11. # template usage
  12. {{ instance|test_advanced_template_call:"var_1=value1&var_2=value2" }}

  13. # output (note the values order)
  14. value2, value1

Calling any method which requires one or more arguments

This is the complex one. You can send any number of arguments and specify which method to call. For this to happen we need not one, but two filters: one to set the arguments in the instance's attributes and one to name the method. The order the filters are called is essential: first the arguments and then the method.

  1. #template_filters.py

  2. @register.filter
  3. def template_args(instance, arg):
  4. """
  5. stores the arguments in a separate instance attribute
  6. """
  7. if not hasattr(instance, "_TemplateArgs"):
  8. setattr(instance, "_TemplateArgs", [])
  9. instance._TemplateArgs.append(arg)
  10. return instance

  11. @register.filter
  12. def template_method(instance, method):
  13. """
  14. retrieves the arguments if any and calls the method
  15. """
  16. method = getattr(instance, method)
  17. if hasattr(instance, "_TemplateArgs"):
  18. to_return = method(*instance._TemplateArgs)
  19. delattr(instance, '_TemplateArgs')
  20. return to_return
  21. return method()

  22. # template usage
  23. {{ instance|template_args:"value1"|template_args:"value2"|template_args:"value3"|template_method:"test_template_call" }}

  24. # output
  25. value1, value2, value3

With all this filters, you can choose the one that best fits your needs without over doing it.

Category: Django


Tagged as: django template tag

Leave a Comment

Resubmit form with errors and FileField or ImageField

urlize HTML safe

The form validation process is very useful and does not require too much code to be written. But what if you need to save certain fields when the form is not valid?

Let's consider the following models.py, forms.py and situation:

  1. # models.py
  2. from django.db import models

  3. class Avatar(modesl.Model):
  4. original_image = models.ImageField()

  5. class Character(models.Model):
  6. name = models.TextField()
  7. avatar = models.ForeignKey(Avatar, blank=True, null=True)

  1. # forms.py
  2. from django import forms
  3. from models import Character

  4. class CharacterForm(forms.ModelForm):
  5. name = forms.CharField(required=True, min_length = 3)
  6. avatar = forms.ImageField(required=True)

  7. class Meta:
  8. model = Character
  9. fields = ['name', 'avatar']

  10. def clean_avatar(self):
  11. """
  12. we need to return a Avatar object to match the model field type
  13. """
  14. img = self.cleaned_data.get('avatar')
  15. if not img:
  16. raise forms.ValidationError('You must select a character image')
  17. avatar = Avatar(original_image = img)
  18. avatar.save()
  19. return avatar

What will happen to the avatar field if the user inputs a name that is already in use? Its value will get deleted and you risk the user not noticing it, generating another form error page.

What can we do about it?

We don't have access to the session inside the clean method to set avatar.id in it, nor to form.cleaned_data in views.py.

We must use what we can: altering the data used to populate form fields values inside the avatar clean method. So let's add a separate hidden field that will store our avatar.id and we can now make the avatar as not required since it can be empty.

  1. # forms.py
  2. class CharacterForm(forms.ModelForm):
  3. name = forms.CharField(required=True, min_length = 3)
  4. avatar_id = forms.CharField(required=False, widget=forms.HiddenInput)
  5. avatar = forms.ImageField(required=False)

Very important: the order of the Meta fields. We want the avatar_id to be in front of avatar:

  1. # forms.py
  2. class Meta:
  3. model = Character
  4. fields = ['name', 'avatar_id', 'avatar']

This way the avatar_id field is evaluated before we modify its value and we don't risk getting our custom value getting overwritten with POST data.

The avatar_id value is set in the clean_avatar method, because that will still be the only place where we create the avatar.

  1. # forms.py
  2. def clean_avatar(self):
  3. img = self.cleaned_data.get('avatar')
  4. avatar_id = self.cleaned_data.get('avatar_id')
  5. # avatar or avatar_id must exist
  6. if not (img or avatar_id):
  7. raise forms.ValidationError('You must select a character image')
  8. if not img and avatar_id:
  9. try:
  10. avatar = Avatar.objects.get(id=avatar_id)
  11. return avatar
  12. except:
  13. raise forms.ValidationError('You must select a character image')

  14. avatar = Avatar(original_image = img)
  15. avatar.save()

  16. if not self.is_valid() and avatar:
  17. self.data['avatar_id'] = avatar.id

  18. return avata

Of course there are other ways, like adding a JavaScript to check all fields value before submitting the form, but this handles cases where the user disabled or the browser does not support JS.

Category: Django

Leave a Comment

Django urlize HTML safe

urlize HTML safe

The default django urlize filter is not HTML safe as the docs say.

Note that if urlize is applied to text that already contains HTML markup,
things won't work as expected. Apply this filter only to plain text.

The easy way to solve this problem is to use an HTML parser and make sure the filter is applied only to plain text.

BeautifulSoup functions

To get the HTML tags from a text, .findAll() is the way to go, but if we need the plain text, .contents is what we need. Given a simple example to see what is the difference:

  1. from BeautifulSoup import BeautifulSoup
  2. html = '<p>foo <a href="http://od-eon.com">http://od-eon.com</a> bar</p> foobar'
  3. soup = BeautifulSoup(html)
  4. soup.findAll()
  5. >>> [<p>foo <a href="http://od-eon.com">http://od-eon.com</a> bar</p>, <a href="http://od-eon.com">http://od-eon.com</a>]
  6. soup.contents
  7. >>> [<p>foo <a href="http://od-eon.com">http://od-eon.com</a> bar</p>, u' foobar']

The flow is clear to obtain a safe urlize from this simple example. We need to apply the urlize function on the .contents of each tag that the text contains:
Step 1. iterate over .contents and if its not a tag, do the replacement
Step 2. iterate over the tags, create a new BeautifulSoup instance from each tag
Step 3. if the new BeautifulSoup.findAll() returns more tags, for each of them repeat the process; if it doesn't, apply the urlize function and replace the tag in the main soup object

There is only one minor check that we should add in this case, what kind of tag are we converting now, because the http://od-eon.com inside the anchor tag it shouldn't convert to a new link, it already is one. So at Step 2. we need to check if tag.name is one of our accepted tags.

Here's how the code would look like:

  1. from django.template.defaultfilters import stringfilter
  2. from django.template import Library
  3. from django.utils.html import urlize
  4. register = Library()
  5. def html_urlize(value, autoescape=None):
  6. """Converts URLs in plain text into clickable links."""
  7. from BeautifulSoup import BeautifulSoup
  8. ignored_tags = ['a', 'code', 'pre']
  9. soup = BeautifulSoup(value)
  10. tags = soup.findAll(True)
  11. text_all = soup.contents
  12. for text in text_all:
  13. if text not in tags:
  14. parsed_text = urlize(text, nofollow=True, autoescape=autoescape)
  15. text.replaceWith(parsed_text)
  16. for tag in tags:
  17. if not tag.name in ignored_tags:
  18. soup_text = BeautifulSoup(str(tag))
  19. if len(soup_text.findAll()) > 1:
  20. for child_tag in tag.contents:
  21. child_tag.replaceWith(html_urlize(str(child_tag)))
  22. elif len(soup_text.findAll()) > 0:
  23. text_list = soup_text.findAll(text=True)
  24. for text in text_list:
  25. parsed_text = urlize(text, nofollow=True, autoescape=autoescape)
  26. text.replaceWith(parsed_text)
  27. try:
  28. tag.replaceWith(str(soup_text))
  29. except:
  30. pass
  31. return mark_safe(str(soup))
  32. html_urlize.is_safe = True
  33. html_urlize.needs_autoescape = True
  34. html_urlize = stringfilter(html_urlize)
  35. register.filter(html_urlize)

Category: Django

Leave a Comment

Django Admin - Sort by ForeignKey field

Django Docs says that in Admin you can only display the __unicode__ representation of a ForeignKey field in list_display and it also allows you to define a custom function if you want to customize the display.

Let's take a simple Blog, Entry as example:

  1. # models.py
  2. class Blog(models.Model):
  3. name = models.CharField(_('Blog Name'), max_length=200)
  4. desc = models.TextField()

  5. class Entry(models.Model):
  6. blog = models.ForeignKey(Blog)
  7. title = models.CharField(max_length=200)
  8. post = models.TextField()

The target is to have the blog name in the Entry admin list view and also having it sortable.

Of course if will not work if you add it in list_display like 'blog__name'.

  1. # amin.py
  2. class EntryAdmin(admin.ModelAdmin):
  3. list_display = ('title',)

Half is done, you can now see it in admin.

Adding the Blog name in Entry admin

Easy way to do this is to define a function in the Entry model, allowing us to call it in the admin, just like any other field.

  1. # models.py
  2. class Blog(models.Model):
  3. name = models.CharField(_('Blog Name'), max_length=200)
  4. desc = models.TextField()

  5. class Entry(models.Model):
  6. blog = models.ForeignKey(Blog)
  7. title = models.CharField(max_length=200)
  8. post = models.TextField()

  9. def blog_name(self):
  10. return self.blog.name

Now we can add it in the admin:

  1. # amin.py
  2. class EntryAdmin(admin.ModelAdmin):
  3. list_display = ('title', 'blog_name')

Making it sortable by

Being a function in the model, it misses an attribute used by the admin admin_oderd_field to order.

  1. # models.py
  2. class Blog(models.Model):
  3. name = models.CharField(_('Blog Name'), max_length=200)
  4. desc = models.TextField()

  5. class Entry(models.Model):
  6. blog = models.ForeignKey(Blog)
  7. title = models.CharField(max_length=200)
  8. post = models.TextField()

  9. def blog_name(self):
  10. return self.blog.name
  11. blog_name.admin_order_field = 'blog__name'

Now you can actually order the results in entry admin list display by blog name.

Category: Django

5 Comments

Facebook Like button

Facebook launched social plugins you can add to your site with just a few lines of HTML code. You can customize how much details should it display and the layout style from their interface.

This is how the code generated looks like:

  1. <iframe src="http://www.facebook.com/plugins/like.php?href=http%3A%2F%2Fdevelopers.facebook.com%2F&amp;layout=standard&amp;show_faces=true&amp;width=450&amp;action=like&amp;colorscheme=light" scrolling="no" frameborder="0" allowTransparency="true" style="border:none; overflow:hidden; width:450px; height:px"></iframe>

It is enough to get you started adding the plugin to your page.

Iframe Properties

All the details you select in that form are found in the src attribute of the iframe tag and you can change them directly from here, even the URL to like by changing:

  1. href=http%3A%2F%2Fdevelopers.facebook.com%2F

to your URL:

  1. href={{ hostname|iriencode }}

Separate Like button for each page

Creating a separate like button for each page will result in changing the same src attribute to something like:

  1. href={{ hostname|iriencode }}{{ request.path_info|iriencode }}

Complete iframe code:

  1. <iframe src="http://www.facebook.com/plugins/like.php?href={{ hostname|iriencode }}{{ request.path_info|iriencode }}&amp;layout=standard&amp;show_faces=true&amp;width=450&amp;action=like&amp;colorscheme=light" scrolling="no" frameborder="0" allowTransparency="true" style="border:none; overflow:hidden; width:450px; height:px"></iframe>

How would this reflect on the Facebook profile?

The plugin will use the page's title tag to print it in the feed list like:

  1. Liviu likes Article Title on Title Tag.

Customizing the FB feed

What if you want to use a different title for the page? It's easy, Facebook also provides custom meta tags for this:

  1. * og:title - The title of your page; if not specified, the title element will be used.
  2. * og:site_name - The name of your web site, e.g., "CNN" or "IMDb".
  3. * og:image - The URL of the best picture for this page. The image must be at least 50px by 50px and have a maximum aspect ratio of 3:1.<meta property="og:title" content="Article Title"/>
  4. <meta property="og:site_name" content="Your Site"/>
  5. <meta property="og:image" content="http://path/to/image"/>

Adding the og:site_name meta tag also changes the feed to:

  1. Liviu likes Article Title on Your Site.

The "Your Site" link is the domain where that page is hosted. Also, adding the og:site_name tag will affect direct link shares on FB site.

Using Facebook's Javascript SDK

If you already added Facebook's javascript SDK, you can use the XFBML tag

  1. <fb:like href="{{ hostname|iriencode }}{{ request.path_info|iriencode }}" layout="standard" show_faces="true" width="300" action="like"></fb:like>

Category: Django

2 Comments

Scope of variables in template blocks

The Django documentation gives explicit details on how you can set a variable in the context using a custom template tag.

  1. class CurrentTimeNode3(template.Node):
  2. def __init__(self, format_string, var_name):
  3. self.format_string = format_string
  4. self.var_name = var_name
  5. def render(self, context):
  6. context[self.var_name] = datetime.datetime.now().strftime(self.format_string)
  7. return ''

Where can I use that variable?

Of course you can use in the template, but the scope depends on the templates structure. Let's take as example a template like this:

  1. {% extends 'site_base.html' %}

  2. {% load my_custom_tags %}

  3. {% block head %}
  4. {% get_current_time "%Y-%M-%d %I:%M %p" as my_current_time %}
  5. The current time is {{ my_current_time }}.
  6. {% endblock %}

This will have the expected output, in the head block of the site_base.html template will display the time.

The exception raises when you have multiple blocks where you want to use that variable:

  1. {% extends 'site_base.html' %}

  2. {% load my_custom_tags %}

  3. {% block head %}
  4. {% get_current_time "%Y-%M-%d %I:%M %p" as my_current_time %}
  5. The current time is {{ my_current_time }}.
  6. {% endblock %}

  7. {% block content %}
  8. The current time is {{ my_current_time }}.
  9. {% endblock %}

This usage will not display the time in the content block or if you set a list that you will iterate over, it will raise Caught an exception while rendering: Failed lookup for key [list] in u'[{\'block\': ...

Why is it a problem to call the template tag in both blocks?

Imagine you are doing some queries in that custom template tag, calling it in multiple blocks, increases your queries/page stat.

Why can't I use the variable in any block, since it is set in the context?

{% block %} pushes a new dictionary on the Context stack and pops it off when it reaches the matching {% endblock %}. This means any context value created while in the block has essentially gone out of scope on block exit.

Any solution?

A minor adjustment to set the variable in the main context

  1. def render(self, context):
  2. context.dicts[0][self.var_name] = datetime.datetime.now().strftime(self.format_string)
  3. return ''

Category: Django

4 Comments

Django Admin, FileField and __unicode__()

The FileField or ImageField is used in many situations (for storing avatars, albums etc). Using it's details in the admin can result in high loading times if you add them in the model's __unicode__().

Simple Example

Let's say you use for storing the avatar of your users (UserProfile has a FK to Avatar):

  1. class Avatar(IKImage):
  2. the_image = models.ImageField(upload_to='/your/path')

In the admin you need to be able to differentiate between Avatars so you need a __unicode__() method.
Ideally you need to show something related to the file and what else if not the url:

  1. def __unicode__(self, *args, **kwargs):
  2. return u'%s' % self.original_image.url

For a few records, you will not notice any problem, but as your project grows, the loading time of admin Profile edit page will increase.

Why does loading time increase?

When getting the url of the file, django will acces the storage, which response time will always be slow. So make sure you use something that exists in the database.

The fast way

Use database fields to keep things smooth. The field containing the filename in the database is name. You are not used to it but this is a good place to take advantage of it.

  1. def __unicode__(self, *args, **kwargs):
  2. return u'%s' % self.original_image.name

Leave a Comment

Firebug on other browsers

Firefox has this great tool which allows you to check what's going on inside a page. From css to js, DOM elements and Net traffic, plus a console.

It's easy to read and interact with it on all tabs. My only problem was I didn't had that on my other browsers (IE, Safari, Opera).

Internet Explorer's Dev Tools are really patchy, you cant see an updated version of he DOM on an ajax action or when you load some content. For Opera, it took me around 30 mins to activate it first time, now i don't even remember where I need to go to activate it.

Firebug Lite

I always wanted to use something like Firebug on other browsers. A small link on the Firebug page solved my problems: Firebug Lite. Now you can use the same interface on IE, Safari and Opera with a simple drag and drop on the bookmarks bar.

Keep in mind it's a Lite version

Some of the functionalities you are used to from Firebug might not be available on Firebug Lite version (eg. edit attributes) but it's easier to work on one environment than switching between different ones.

Leave a Comment
Page generated in: 0.48s