Django: How to pass an url as parameter of include

In Django web applications I usually have a single template file for navigation or bread crumb that I include from other template files, it is easy and straight forward to pass parameter to the included template file, some thing like:

{% include "nav.html" with title="title" object=my_object %}

But it would be a little tricker to send a URL as a parameter, i.e. you can’t write it:

{# WRONG DOESN'T WORK #}
{% include "nav.html" with title="title" link={% url 'book_edit'%} %}

But luckily Django have a decent and elegant solution, you can use “url” template function with “as” parameter, which will not display but will store it in a variable that you can use later in the include function:

{% url 'some-url-name' arg arg2 as my_link %}
{% include "nav.html" with title="title" link=my_link %}

In fact you don’t need to send my_link as it going to be visible on the included file anyway, so this will work as expected:

{% url 'some-url-name' arg arg2 as my_link %}
{% include "nav.html" with title="title" %}

Django CRUD Parent/Child Edition

I’ve written before about implementing CRUD operations on a single table in Django, and the post was very popular on my blog and I hope many people learned something from it.

The next logical step is to write a CRUD application with Parent/Child relationship, the application is bit more verbose but it follow the same simple logic used in the first post.

In fact I didn’t write a single application, I wrote five stand alone applications that shows different ways to implement CRUD operations:

  • books_simple: Single table CRUD operations.
  • books_pc_formset: Parent/Child CRUD operation using Django formsets, which means editing the children in the sample form as the parent.
  • books_pc_formset2: similar to previous app but uses a foreign key in the children.
  • books_pc_multiview: Parent/Child CRUD operation using multiple one view for the parent and another seperate view for the children.
  • books_pc_multiview2: similar to previous app but uses a foreign key in the children.

You can find the code here:
https://github.com/rayed/django-crud-parent-child

Hope you find it useful, and please let me know if it needs any improvements.

Django Image and File Field Caveats

Everytime I work with Image or File fields in Django I forget some tiny detail that waste 10-20 minutes until I remember what was I missing, I always say I will remeber it next time but I never do! so I made a list of common errors I keep doing while working with Image/File fields:

For complete working project: https://github.com/rayed/dj-imagefield-example

Setting MEDIA_URL and MEDIA_ROOT

Make sure you set proper values for MEDIA_URL and MEDIA_ROOT in your settings.py, e.g. I use the following structure:

my_new_site/
    apps/
       apps/
          settings.py
       gallery/
       blog/
    www/
       media/
       static/
    requirements.txt


I would put the following settings (for both MEDIA and STATIC files):

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, '..','www','static')
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, '..','www','media')

Make sure uploaded files are accessible in development server

Serving uploaded files (media) is the job of the web server and not Django, but to ease the development process I usually make Django serve it in debug mode.

This is done by adding the following at the end of your main urls.py:

from django.conf import settings
from django.conf.urls.static import static
if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

HTML forms changes

Make sure you have enctype=”multipart/form-data” in your form tag, i.e.:

<form method="post" enctype="multipart/form-data">


Instead of:

<form method="post">

Don’t forget request.FILES

I always forget to include “request.FILES” in my ModelForms, and I always get “This field is required” error message!

def author_create(request, template_name='author/form.html'):
    form = ImageForm(request.POST or None, request.FILES or None)
    if form.is_valid():
        form.save()
        return redirect('author:home')
    return render(request, template_name, {'form':form})    

ImageField requires Imaging library

If you want to use Django built in ImageField you have to install Pillow Imaging library! FileField doesn’t needed it though.

View the image

Just a reminder that you can access the image URL in your template like so:

<img src="{{ author.image.url }}"  />

Django memory leak with gunicorn

tl;dr add “–max-requests” to Gunicorn to easily solve memory leak problems.

If you have a long running job that leaks few bytes of memory it will eventually will consume all of your memory with time.

Of course you need to find out where is the memory leak and fix it, but sometimes you can’t because it on a code that you use and not your own code.

Apache webserver solve this problem by using “MaxRequestsPerChild” directive, which tells Apache worker process to die after serving a specified number of requests (e.g. 1000), which will free all the memory the process acquired during operation.

I had a similar problem with Django under Gunicorn, my Gunicorn workers memory keep growing and growing, to solve it I used Gunicorn option “–max-requests”, which works the same as Apache’s “MaxRequestsPerChild”:

gunicorn apps.wsgi:application -b 127.0.0.1:8080 --workers 8 --max-requests 1000

Django Themes (or where to put base.html?)

The Wrong Way

I used to create a new directory to hold common templates like “base.html”, and add it TEMPLATES_DIR in the settings.py file:

TEMPLATE_DIRS = (os.path.join(BASE_DIR, 'templates') ,)

But in most cases the “base.html” would need to use CSS, JS, and image files to be functional, so I changed the url routing to access them (from the DEBUG mode only), something like:

$ vi apps/urls.py
:
# Serve Static Files 
from django.conf import settings
from django.conf.urls.static import static
if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
    urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
    urlpatterns += patterns('django.views.static',
        url(r'^(?P<path>(js|css|img)/.*)$', 'serve', {'document_root':  settings.BASE_DIR+'/../www'}),
        )

This setup isn’t ideal for many reasons:

  • I had to modify settings.py and urls.py with complicated settings.
  • Theme design span multiple directories, and it isn’t self contained.
  • Switching the design is complicated, and include many changes.

Django Simple Themes

Nowadays I create a new Django application e.g. “my_theme” to hold my “base.html” template and all needed static files (CSS, JS, Images, etc …).

./manage.py startapp my_theme

Then add it to INSTALLED_APPS:

INSTALLED_APPS = (
    # django core apps ...
    'my_theme',
    # other apps ...
)

The directory structure for my new app looks like this:

my_theme/
    templates/
        base.html
    static/
        my_theme/
            css/
            js/
            img/

and from my “base.html” (or any other template) I could access the static file using the static tag:

{% load staticfiles %}
<img src="{% static "my_theme/img/logo.png" %}" />

I don’t even need to change the “urls.py” file to access the static file, since the development server (i.e. ./manage.py runserver) already knows how to find them.

But for production I have to define:

STATIC_ROOT = os.path.join(BASE_DIR, '../www/static')

and run:

./manage.py collectstatic --noinput

New Theme

By having all theme files inside an application I can start new theme by copying “my_theme” to something like “new_theme” and replace it in the INSTALLED_APPS in the settings.py.

What about uploaded files?

To access uploaded file from development server you need to define both MEDIA_URL and MEDIA_ROOT and change your “urls.py”:

$ vi apps/urls.py
:
from django.conf import settings
from django.conf.urls.static import static
if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Source: Serving files uploaded by a user during development

Sample Theme

You can download my sample theme from:
https://github.com/rayed/django_theme

Translation in Django

Settings

In your project “settings.py” setup the following values:

LOCALE_PATHS = (
    os.path.join(BASE_DIR, 'locale'),
    )
LANGUAGE_CODE = 'ar'

Here we defined the location of our translation files, by default Django will look for it under application directories under “locale” directory, but here we define it for the whole project.

The LANGUAGE_CODE line define a fixed translation to Arabia “ar”.

Source Code

For Python source code Django uses “ugettext” function aliased as “_” (underscore) to translate text strings:

from django.utils.translation import ugettext as _
from django.http import HttpResponse
from django.shortcuts import render

def page1(request):
    output = _("Welcome to my site.")
    return HttpResponse(output)

def page2(request, template_name='index.html'):
    return render(request, template_name)

Template

Inside Django templates, Django uses trans template tag with the text to translate, don’t forget to load the tag using “{% load i18n %}”

{% load i18n %}

<h1>{% trans "Hello World" %}</h1>

Commands

After preparing the code we the following steps:

  • Collect translation string using “django-admin.py makemessages” command.
  • Edit the translation file “django.po”
  • Compile the translation to “django.mo”.

$ cd project_home

# Make the "locale" directory to store translation data
$ mkdir locale

# scan the project for translation strings
$ django-admin.py makemessages -l ar

# Edit the translation file and add your translation
$ vi locale/ar/LC_MESSAGES/django.po
:
msgid "Hello World"
msgstr "مرحبا يا عالم"
:

# compile django.po to django.mo
$ django-admin.py compilemessages

That’s it, you should be able to see your applications translated!

Django returning JSON for AJAX requests

In your views.py you can have a page that return JSON data for AJAX request like this:

import json
from django.http import HttpResponse

def ajax(request):
    data = {}
    data['something'] = 'useful'
    return HttpResponse(json.dumps(data), content_type = "application/json")

This would work fine if you fill the data your self, but if you are getting the data from a model try the following:

from django.core import serializers
def tasks_json(request):
    tasks = Task.objects.all()
    data = serializers.serialize("json", tasks)
    return HttpResponse(data, content_type='application/json')