Python Properties for Django Templates

Python Properties can make method calls visible to Django templates!

I have been developing a website for my wife as she catalogs every brewery, cidery, distillery, meadery, and winery in Colorado. Using Django has been a dream – easy interactions with the database, and templates make rendering data to HTML simple.

I wanted to give users the ability to view information about a place by entering its name in the URL. This is not only nice for humans, but search engines will rank you slightly higher if you have coherent urls.

Say a user wanted to view information about a place called Fresh. Then the user could navigate to “/place/Fresh”.

This works well for most entries, but plenty of businesses have special characters and spaces in their name. Trying to navigate to “/place/Fresh Brew ‘n Stuff!”, for instance, doesn’t translate very well to a navigation request, and lookups on models can be unreliable with the added syntax.

Here’s the basis of the model that stores the name of each business in the database:

class Place(models.Model):
    name = models.CharField(max_length=125, unique=True)
    ...

And here is the url-pattern that users will be directed to:

url(r'^place/(?P<name>[\w|\W]+)$', PlaceInfoView.as_view(), name='place_profile')

Each instance of Place will need a way to generate a safe url-encoding of its name.

That’s easy. Urllib solved this a long time ago. These methods added to Place handle the encoding as well as the decoding and retrieval:

import urllib
...

def get_safe_url_name(self):
    return urllib.parse.quote(str(self.name))

@classmethod
def find_place_from_safe_url_name(cls, name: str):
    return Place.objects.get(name__iexact=urllib.parse.unquote(name))

Horray! But there’s a problem: Django templates can’t call either method. They can only access variables from a class. (Update 12/06/16: Thanks to many eager replies, I’ve learnt methods can be called from within Django templates, although I hold the position that this information wasn’t well documented. Regardless, calling “place.get_safe_url_name” from within a template makes it look like we’re accessing a variable, not calling a function. It’s because of this I still believe a property is a more correct solution.)

Admittedly, I’ve done the lazy, round-about, hacky solution of zipping each instance with its safe-encoding and iterating over each tuple in the template. That’s messy and unmaintainable though. Months later, the template and its respective view will confuse developers, at least for a short time.

This is a perfect time for a property!

Properties, among other things, allow getter and setter methods to be called as variables of an instance. Here’s what was added to the Place model:

@property
def safe_url_name(self):
    return self.get_safe_url_name()

Now in a Django template, a URL using safe encoding can be generated:

<a href="{% url 'place_profile' name=place.safe_url_name %}">

And it can be used in Python code:

url_to_redirect = reverse('place_profile',
                          kwargs={'name': place.safe_url_name})

There! Using a property, I can call a function from a template and get the relevant information I need to link pages together using a standard encoding.

Simple.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s