The Django community aggregator
Will Larson: Replacing Django's Template Language With Jinja2
In this example we're going to take a closer look at the flexibility of Django's loose coupling philosophy by replacing its default templating language with Jinga2. Jinga2 is a very similar template language to the one provided by Django, but provides additional functionality like more flexible if syntax in templates, the option to raise an error when an undefined object is operated upon within templates (as opposed to Django's templating language which will always fail silently), and more flexible solutions for retrieving templates.
Lets get started.
-
First we need to download and install Jinja2.
sudo easy_install jinja2
-
Then make sure it works.
import jinja2
-
Create a Django project and app.
django_admin.py startproject loose_coupling cd loose_coupling python manage.py startapp with_jinja -
Create a templates folder for your project, and one for the new app as well.
mkdir templates mkdir with_jinja/templates mkdir with_jinja/templates/with_jinja
-
Open up
loose_coupling/settings.pyand change the settings forINSTALLED_APPSandTEMPLATE_DIRS.INSTALLED_APPS = ( 'loose_coupling.with_jinja', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', ) import os ROOT_PATH = os.path.dirname(__file__) TEMPLATE_DIRS = ( os.path.join(ROOT_PATH,'templates'), os.path.join(ROOT_PATH,'with_jinja/templates'), )
-
Open up
loose_coupling/urls.pyand change its contents to this:from django.conf.urls.defaults import * urlpatterns = patterns( 'loose_coupling', (r'^$','with_jinja.views.index'), )
-
Now we just need to create our views. First, open up
with_jinja/views.pyand add these imports:import math,random from django.http import HttpResponse from django.conf import settings from jinja2 import FileSystemLoader, Environment
And then we're going to write a little convenience method to handle rendering via Jinja2.
template_dirs = getattr(settings,'TEMPLATE_DIRS') default_mimetype = gettattr(settings, 'DEFAULT_CONTENT_TYPE') env = Environment(loader=FileSystemLoader(template_dirs)) def render_to_response(filename, context={},mimetype=default_mimetype): template = env.get_template(filename) rendered = template.render(**context) return HttpResponse(rendered,mimetype=mimetype)
And next, lets create a simple custom test that our Jinja template is going to use to help render its output.
def greater_than_fifty(x): return x > 50 env.tests['gtf'] = greater_than_fifty
Now lets create the
indexview that we'll be using. -
Next, we need to create our
index.htmltemplate that theindexview is trying to render. To do that, first we are going to create abase.htmltemplate in theloose_coupling/templatesdirectory. So, open uploose_coupling/templates/base.htmland add this to it<html> <head> <title> Loosely Coupled Django </title> </head> <body> {% block content %} {% endblock %} </body> </html>
And now we need to create the
loose_coupling/with_jinja/templates/with_jinja/index.htmltemplate. It will extend our base template and be quite simple:{% extends 'base.html' %} {% block content %} <p> The number generated was {{ n }}. </p> {% if n is gtf %} <p> The number was definitely greater than fifty.</p> {% else %} <p> Unfortunately, the number was below fifty.</p> {% endif %} {% endblock %}
-
Now, save all the files and run the project.
python manage.py runserver
-
Now navigate over to http://127.0.0.1:8000 and you'll see Django development server merrily rendering our Jinja2 template.
(You can download a zip of the project we developed here.)
Thanks to Django's loose coupling philosophy, it was really quite painless to switch over to using Jinja2 instead of the default templating language. As this series continues we'll look at how this isn't just a freak occurance, but occurs throughout the Django stack.
01 Jan 1970 12:00am GMT
Will Larson: Replacing Django's ORM with SQLAlchemy
In the first example of Django's loose coupling we looked at replacing the templating system with Jinja2. In this article we're going to look at replacing the standard Django ORM with SQLAlchemy. This is a pretty big leap, and before we jump in too deeply lets take a look at how switching to SQLAlchemy is going to impact our application.
In the introduction to this series, I said that Django's implementation follows the loose coupling philosophy, but we may have to add several grains of salt to that statement as we consider the changes caused by switching from Django's ORM to SQLAlchemy. First, we're going to no longer be able to use the Django models.py files to describe our models. Instead, we'll be using SQLAlchemy's ORM for defining tables and fields (which will look pretty similar).
Following from that, we won't be able to use a simple python manage.py syncdb to create the tables for our project. We won't be able to use python manage.py loaddata or python manage.py dumpdata to create fixtures to use as initial data or scaffolding for the Django testing framework. If we're strict about only using SQLAlchemy, we won't even be able to use the contributed resources like the Django Admin, the sessions middleware, or the authentication middleware.
The take away message here is that while Django is loosely coupled, it is loosely coupled does not imply decoupling has no consequences. Changing some systems, like template rendering, have very few consequences, but others--like the Django ORM--are a far from plug and play. This is unavoidable, because perfect decoupling places additional onus upon the individual developer, and would make it slower (and more complex) to get Django projects up and running. When you first begin using Django it is difficult to gauge which subsystems will be costly to decouple, and that is why its highly recommendable to start out using the default Django stack, and then deviate responsibly as you gain more experience.
(Full disclosure: it is possible to use the Django ORM and SQLAlchemy together. This means you could still take advantage of the Django sessions middleware (and most other stuff), while using SQLAlchemy for some parts of your app. This tutorial won't build that Frankenstein, since it's only interested in exploring the loose coupling aspect, but it wouldn't be prohibitively difficult to do so. Although, it would be awkward in some regards.)
Now lets start turning silver into gold1.
Using SQLAlchemy with Django
-
First we need to download SQLAlchemy. The easiest way to do this is to use
easy_install.easy_install SQLAlchemy
But you can also grab a copy of the 0.5 release at their download page.. This tutorial is using the 0.5beta2, but as long as you don't grab a 0.4 release things should work correctly.
If you chose to manually download the files, you'll then need to install them:
tar zxvf SQLAlchemy-0.5.0beta2.tar.gz cd SQLAlchemy-0.5.0beta2 python setup.py install -
If you are using a Python before 2.5 you'll need to install pysqlite and SQLite as well.
-
Now lets make sure the install worked correctly.
>>> import sqlalchemy >>> sqlalchemy.__version__ '0.5.0beta2'
If SQLAlchemy doesn't import correctly, or if the version isn't some derivative of 0.5, then you'll need to revisit steps one and two.
-
Create a Django project and app.
django_admin.py startproject loose_coupling cd loose_coupling python manage.py startapp with_sqlalchemy -
Create a couple of template directories.
mkdir templates cd with_sqlalchemy mkdir templates templates/with_sqlalchemy -
Next we need to make a few simple changes in our
settings.pyfile. We'll be changingINSTALLED_APPS,TEMPLATE_DIRS, andMIDDLEWARE_CLASSES.MIDDLEWARE_CLASSES = ('django.middleware.common.CommonMiddleware',) INSTALLED_APPS = ('loose_coupling.with_sqlalchemy',) import os ROOT_PATH = os.path.dirname(__file__) TEMPLATE_DIRS = (os.path.join(ROOT_PATH,'templates'),)
Since we're not using the Django ORM, we won't use any of the database reliant middleware or apps.
-
Now we'll edit the
loosely_coupled/urls.pyfile.from django.conf.urls.defaults import * urlpatterns = patterns( 'loose_coupling', (r'^$', 'with_sqlalchemy.views.index'), )
Normally we'd use include a
urls.pyfile from thewith_sqlalchemyapp into the project'surls.pyfile, but we're trying to keep things simple. -
Now we're going to use the SQLAlchemy ORM to create a simple table to play around with. Because the file already exists, we're going to throw the table definition into the
models.py, so go ahead and open upwith_sqlalchemy/models.py.from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class Language(Base): ablename__ = 'languages' = Column(Integer, primary_key=True) e = Column(String) ension = Column(String) __init__(self, name, extension): self.name = name self.extension = extension __repr__(self): return u"Language(%s, %s)" % (self.name, self.extension)
We can see that SQLAlchemy can define models very similar to those we'd define for Django. We won't delve into it here, but SQLAlchemy can also get much more low level by defining database tables and then mapping them onto a Python class. You could even skip out on using ORM completely and take advantage of their interface for constructing SQL.
-
And now we'll write our
views.pyfile. First, we have some imports to begin our file with.from django.shortcuts import render_to_response import sqlalchemy, sqlalchemy.orm from models import Base, Language
Then we need to setup SQLAlchemy to make our
Languageclass available.engine = sqlalchemy.create_engine('sqlite:///loose.sqlite') Session = sqlalchemy.orm.sessionmaker(bind=engine) session = Session() Base.metadata.create_all(engine)
The
sqlite:///loose.sqliteinstruction creates an SQLite database at the relative pathloose.sqlite, i.e. within thewith_sqlalchemydirectory. SQLAlchemy makes it equally easy to specifiy an absolute path, or to create the database in memory. If we wanted to make this more Django-like, we could have specified theDATABASE_NAMEsetting insettings.py, and then loaded the value like this:from django.conf import settings engine = sqlalchemy.create_engine(settings.DATABASE_ENGINE)
Also, the
Base.metadata.create_all(engine)only really needs to be done once when SQLAlchemy first creates the table (although doing it multiple times doesn't hurt anything), but that would involve creating a counter-part to thepython manage.py syncdbfunctionality in a script, and would make the example more complex, so we're skipping that at the moment.Next, we'll create two really simple utility functions that we'll use to populate our data:
def is_empty(): urn len(session.query(Language).all()) <= 0 def populate(): _langs = [Language('Python','py'),Language('Ruby', 'rb'), Language('Common Lisp', 'lisp'),Language('Objective-C', 'm')] sion.add_all(new_langs) sion.commit()
These simple functions are going to be used to populate our database with some data. It also provides a minimal example of the SQLAlchemy querying and object creation syntaxes, which are very similar to those in the Django ORM.
Finally, we need to write the
indexfunction.def index(request): is_empty(): populate() gs = session.query(Language).all() urn render_to_response('with_sqlalchemy/index.html',{'langs':langs})
In a better designed application we wouldn't need to make sure we'd created the test data each time we call the
indexview, and then the view would look like this:def index(request): gs = session.query(Language).all() urn render_to_response('with_sqlalchemy/index.html',{'langs':langs})
Which looks almost identical to what it would have looked like writing it with the Django ORM (just a slight syntax difference for retrieving data).
-
And now we need to create the
with_sqlalchemy/index.htmltemplate that we are rendering. First we'll create a base template, located inloose_coupling/templates, and namedbase.html. It will look like this:<html> <head> <title> Loosely Coupled Django </title> </head> <body> {% block content %} {% endblock %} </body> </html>
And next we'll create the
index.htmltemplate. Open upwith_sqlalchemy/templates/with_sqlalchemy/index.html, and fill it in:{% extends 'base.html' %} {% block content %} {% for lang in langs %} <p>{{ lang }}</p> {% endfor %} {% endblock %}
The most important thing to notice about these templates is that we are passing them instances generated by the SQLAlchemy ORM, but it looks identical to how it would look if the template was dealing with the Django ORM: the template language is ignorant of the details of the Django ORM, and can be used to anything from normal Django Model instances to normal Python dicts.
-
Now everything is finished, just waiting for us to test.
python manage.py runserver
Then navigate over the http://127.0.0.1:8000/. You'll see a simple display of the four programming languages and their extension.
You can grab a copy of the project here. It just so happens to be a Git repository, but you can use it without having/using Git.
Now, this example is much less transparently useful than the previous one working with Jinja2, but it still demonstrates two important points about Django. First, Django is Python, so you can do anything in Django that you could do outside of Django. Second, Django is loosely coupled, but sometimes it is loosely coupled with consequences, and decoupling some subsystems--especially the ORM--isn't painless.
As a final point, I'd like to apologize for the disservice that is done to SQLAlchemy in this entry. SQLAlchemy is an extremely full-featured and versatile system, and the usage here makes it look like a mirror image of the Django ORM. It isn't. Not by a long shot. SQLAlchemy can abstract things similarly to the Django ORM, but makes it much easier to get dirty, and also exposes more advanced functionality like connection pools.
Okay, and a final final point: I think this tutorial speaks for itself, but I wouldn't recommend decoupling from the Django ORM without an extremely compelling reason. It is the most coupled of all the subparts of Django, and certainly not trivial to replace.
For those who feel disappointed that I've been unfair to Django by not looking at using both the Django ORM and the SQLAlchemy ORM together, have heart, the next example in this series will take a look at something along those lines (but featuring a slightly more realistic situation).
-
I felt like I was obligated to make at least one lame alchemy related joke and/or pun. Consider the itch scratched.↩
01 Jan 1970 12:00am GMT
Will Larson: An Introduction to Django's Loose Coupling
One of Django's core design philosophies is that of loose coupling. This is one of the best parts of Django, but its also something that takes some time and exposure before its value (and how to take advantage of it) really clicks. This short string of tutorials will begin by briefly examining what Django's loose coupling philosophy means and why its advantageous for those of us building with Django, and will then continue with a handful of small example projects that look at how to replace individual pieces of the Django stack.
What it Means
Loose coupling means that individual components of Django's feature stack are kept as separate as possible. For example, Django templating language makes it very pleasant to represent the contents of your models, but its equally easy to use Django's templating language to represent other kinds of Python objects as well. The Django ORM makes it easy to setup and access a database, but it possible to use SQLAlchemy instead.
This makes it easier to integrate Django applications with legacy apps, but more importantly it lets you use the tools you are familiar with, and the tools that are most suited for your specific problem. Often new developers who come to Django dislike part of the Django toolkit, and submit suggestions to make Django function more similarly to the frameworks and tools they are already familiar with. However, the Django solution is both extremely simple and extremely powerful: if you prefer other tools, use them.
Django isn't a monolithic tool that descends from the heavens to rule our project, it provides a handful of tools that are willing to cooperate with one another, but are equally willing to cooperate with tools of your own choosing. Developers coming from Ruby on Rails or other extremely opinionated frameworks may be used to following their framework's best practices to avoid fighting against a framework which feels that it knows your project better than you do, but with Django you'll be back in the driver's seat1.
Now, lets start looking at examples of loose coupling in Django by using the Jinga2 template engine to render webpages.
-
Which seems to be one of the biggest complaints against Django: it doesn't make enough decisions for you. Which, it seems, is actually a fair complaint, since for many projects you simply need to make any decision, and not having to think about it will save time and energy. However, I think you'll find as you get more comfortable with Django that while it will let you make decisions, it will rarely make you make a decision.↩
01 Jan 1970 12:00am GMT
Simon Willison: Replacing Django's Template Language With Jinja2
Replacing Django's Template Language With Jinja2. Part of Wil Larson's series on taking advantage of Django's loose coupling.
01 Jan 1970 12:00am GMT
Simon Willison: Python BoF and Django Drinkup
Python BoF and Django Drinkup (via). At OSCON? Come along to the Jax Bar tonight (Tuesday 22nd) from 7pm to 10pm to hang out with fellow Pythoneers and Djangonaughts.
01 Jan 1970 12:00am GMT
Simon Willison: ComicVine.com
ComicVine.com. Also powered by Django, Whiskey Media's comic book encyclopedia and community. 43,000 characters and 94,000 issues and counting.
01 Jan 1970 12:00am GMT
Ross Poulton : Django 1.0 Alpha Released
Django, my web development framework of choice, has reached Alpha 1.0. There's a bunch of Backwards Incompatible Changes to the API which will require changes to your projects, but the codebase should be pretty stable from here to 1.0.
The biggest changes to this from the last official release are 'Unicode everywhere', a new and more powerful admin module powered by newforms, an updated database ORM that's more efficient, and automatic escaping in templates to help reduce the risk of cross-site scripting attacks.
1.0 should be released with a party at DjangoConf 2008 on September 2nd - I look forward to it (Although I won't be at DjangoConf, unfortunately).
Great work to everybody involved!
01 Jan 1970 12:00am GMT
Peter Baumgartner: Building a Community Site with Django in 40 Hours or Less
Chronicling my attempt at ultra-rapid development of a community website with Django and Pinax.
01 Jan 1970 12:00am GMT
Paul Kenjora: Django Shares Spotlight At Refactor Phoenix
I will be giving a special talk on my biggest Django project to date, Arkayne.com. James Britt from Happy Camper Studios is organizing the event. The focus of the talk will be Arkayne as well as the technology that has made it possible, mainly Django. A blurb from the official Refactor Phoenix website [...]
01 Jan 1970 12:00am GMT
Alex Koshelev: Спринтеры в Яндексе
Нет, конечно не Майкл Джонсон и Ко, а мы - отчаянные джангисты. Прошло уже больше недели, а я вот только сейчас могу рассказать, а главное даже чуть-чуть показать как это было.
Технические итоги спринта подвел очень лаконично Ваня Сагалаев, я же больше расскажу чуть про процесс.
И так, в субботу 12 июля в одном из московских офисов Яндекса, собрались люди чтобы подправить джангу. Собирались долго, поскольку для многих оказалось неожиданностью, что привычные шатлы от метро по выходным не ходят и надо добираться пешком. Сюрпризом это стало и для некоторых сотрудников самого Яндекса, к слову, которых набралось целых 3 человека.
Принимал нас Яндекс в большой столовой, с заранее приготовленными для нас вкусностями.
Когда уже почти все собрались и немного познакомились, Ваня взял вступительное слово, рассказал немного про процесс спринтования и все неспеша полезли в джанговский трак чтобы захватить себе тикет получше. Те кто просмотрел их список заранее, сразу выбрали что-то себе. Откровенно говоря, интересных задачек было мало. Они либо слишком нудные, либо какие-то очень заумные и специфичные.
Кстати, была группа, которая занималась не доводкой NFA, а реализацией улучшенного бекэнда для Oracle, как потом оказалось, из очень серьезной конторы:) Но об этом попозже.
Так, я оказался в числе тех самых, подготовленных, и выбрал очень простой себе тикет, т.к. на что-то более серьезное не хватило бы времени. Суть его заключалась в том, чтобы избавится от ненужных проверок на "залогиненность" пользователя. Я быстро набросал патч #6991, который потом всё равно Ване пришлось подправлять, залил его в трак и уж было собрался уходит на дерби. Как вспомнил, что у меня есть телефон-фотоаппарат. И устроил маленькую фотоохоту на спринтеров, которые были погружены в процесс. Вот что получилось:
Было немного грустно, что все вот так сидят и мало кто представляет, откуда кто приехал и чем занимается. И тут я вспомнил что у меня телефон ещё и видеокамера!:) Быстро сообразив как это использовать, я попросил всех немного рассказать и себе и о своем занятии. Получился такой не плохой ролик с представлением участников:
Django-sprint at Yandex 12 July 2008 from Alex Koshelev on Vimeo.
Потом я быстро собрал свой ноутбук и отправился в дорогу. Но это уже совсем другая история. И в противовес этой - очень грустная.
В итоге, я был очень доволен что всё таки приехал на спринт и познакомился с интересными людьми. Было немного жаль, что не досидел до конца, потому что там было много интересного и пицца:)
Хочу ещё!
Теги:
django opensource web личное яндекс
01 Jan 1970 12:00am GMT