Posts tagged with django:

Expanding template variables in Django Flatpages

Recently I had to make use of Django's Flatpages framework for a project.  Normally the flatpages framework is fine for a couple pages out of the box but I didn't like the idea of hard coding my media url in the flatpages when it was dynamic everywhere else.  I also wanted to be able to use other tags and filters in the flatpage and have Django render them as usual. 

I didn't want to re-write the flatpages framework (although it's a fairly easy task in Django), rather I wanted to just add one feature.  So how did I do it?  I created a string filter that takes the page content and re-renders it at a template level.  Here's the code:

Starting with the custom filter itself:

from django import template
from django.template import Template, Context
from django.template.defaultfilters import stringfilter
from django.conf import settings
register = template.Library()

@register.filter
@stringfilter
def parse_blocks(value):
    """ use the django template loader and response object to spit 
    out rendered content
    """
    t = Template(value)
    c = Context({ 'MEDIA_URL': settings.MEDIA_URL })
    return t.render(c)

Not the greatest name for the filter I know, but eventually I want it to recognize template blocks.  You can call it what you like. 

Next up the template itself:

{% extends 'base.html' %}
{% load parse_blocks %}

{% block title %}- {{ flatpage.title }}{% endblock %}

{% block content %}
{{ flatpage.content|parse_blocks }}
{% endblock %}

Again pretty self explanatory.  Pass the content as a string to the filter and let the filter do it's job.

A sample flatpage:

<div class="content">
    <img src="{{ MEDIA_URL }}images/image.png" /></div>

You really have to love the ability of Django to make these kind of hacks work without much effort.

SSL/Nginx/Django

I wanted to create a client portal of sorts to allow my clients to login and check billing status/history etc.  Since I pre-dominantly program websites in Django that part was a no brainer. But what was troubling me was dealing with the SSL and non-SSL parts.  I had only ever created "secure sites" in the past where everything was behind a login and run over SSL.  It was the flipping back and forth between secure and non that had me a little puzzled on how to implement.

I did some reading and eventually settled (for now) on using nginx (which I use as my main webserver) to look at the paths and redirect as needed:

if ($uri ~ (/bromin|/accounts|/invoices|/sf)) {
    rewrite (.*) https://nomad.ca$1 permanent;
}

So I have one of these blocks in the standard port 80 definition and another in the secure socket definition (with the operator switched to !~ and the rewrite to http).

The only reason I don't like this implementation is that the links in the HTML are not updated. So a link may claim it will take you to a secure page but might redirect to a non-secure page if the SSL isn't needed.  This violates a bit of what I would consider safe coding practice as a prudent user would notice the difference and it may raise suspicion.  However since I haven't even purchased a signed certificate this will do for now.

Disqus this amongst yourselves...

So I just finished adding Disqus as my comment manager.  I did this for a number of reasons but mainly comments are just a pain in the @ss to administer.  So Disqus can deal with it and I can better spend my time developing apps.  The installation was a piece of cake and the added functionality for the comments on this blog are phenomenal.

I still have a bit of work to integrate the look a little better but it's Saturday and sunny.  :)

Merging Querysets in Django

I had a need in one of my projects to do two different queries that result in similarly structured data. Instead of passing two variables into the template and then having to worry about the logic on the template side I wanted to merge them.

Thankfully Python provides a solution:

from itertools import chain

list = Model.objects.filter(foo=foo)
list2 = Model2.objects.filter(foo=foo)

final_var = list(chain(list,list2))

final_var is now a list containing both sets of results.

Django’s upload_to

Went looking for info on how to dynamically assign paths to uploaded content in a Django app. Found this post and tried it out. Either I have an issue with my attempt or that site is referencing old material.

In that post they claim that the upload_to callable will take 3 arguments. However in reality (or at least with my code, django 1.0+) it only takes two:

def get_path(instance, filename):
    return settings.MEDIA_ROOT, 'something', \
           instance.slug, filename

That works however I found that you ended up having to do a little more work to sanitize the filename that Django would have done for you. I guess if you customize the path then you should know how to customize the filename. Just seems like it violates DRY a little in that I shouldn't have to mess with the filename if I don't need to (Django should sanitize it like normal unless I tell it not to).  Just my opinion.

UPDATE: I had to get this working so I went back to it a couple weeks ago and realized that I could just use os to sanitize the filename. However I'm still sure there is a better way.

def get_path(instance, filename):
    return os.path.join(settings.MEDIA_ROOT, 'something', \
           instance.slug, filename)