This may be something that most people know, but I had a hard time tracking this down for some reason.
I have a couple of custom template tags that do things like get twitter feeds or my github commits. Some of them are simple inclusion tags and don't need this, however, I do have one tag that gets some data and spits it out formatted. Now, I know this may not be the best possible solution to the problem and I will eventually go back and fix this code to do something else, but for now, I wanted to make sure that the data that was being formatted inside of the template tag was being escaped so that I could avoid any XSS issues that could potentially arise.
So the easiest thing to do is to import the escape function.
from django import template
from django.utils.html import escape
register = template.Library()
@register.simple_tag
def super_tag():
data = get_some_data('whatever')
return '<p>%s</p>' % escape(data)
That's it. Now if data has anything in it, like say a <script> tag with some javascript in there, you won't have to worry about any XSS. Simple and probably well known, but I'm putting this out there anyway, if not for others, for myself so I'll remember!
Here's another small and easy Django tip, but one that I had a hard time finding an answer to when I first got started... I came from a Ruby on Rails and Groovy/Grails background before starting with Django. One thing both of those frameworks have is the concept of environments. Depending on what you're doing, you can have one of 3 environments going:
- Development
- Testing
- Production
Typically the development environment was used when running the built-in server such as when you run ./manage.py runserver in Django. The testing environment was when running tests, of course. Similar to Django's ./manage.py test. Then the production environment was when running it on a real webserver such as Apache. You could set the environment if you felt like it as well, so you could run in development mode while under Apache. Each environment had its settings and different databases.
With a lot of my recent Django apps, I have noticed that I am duplicating code with the settings file. The typical way to run in different modes is to set the DJANGO_SETTINGS_MODULE environment variable before running. What I have noticed is common is to create 2 or 3 different settings.py files based on the different environments you want to set up. This is fine and all, but there are times when you may use similar settings between each file and all you really want to do is change what type of database you want to use depending on each environment.
For instance, when I'm doing development, I just want to use sqlite3. When running on Apache, I'd like it to use MySQL. However, everything else stays the same in my settings files because I don't really do any hardcoding of file paths or anything like that. If at any point I needed to change a setting that would be related to both development and production modes, I wouldn't want to change two different settings files to make those changes.
My solution to this problem was to set an environment variable and check it in the settings.py file. I called this environment variable DJANGO_RUN_ENV. I set this inside of my .wsgi file that is run when using Apache. If that environment variable was not set, I would default to the development mode. You could set this up in many different modes if needed, but I was only interested in production and development mode since when testing, the default is to use the in memory database. Here is what everything looks like...
Here is the top portion of my settings.py file:
import os
import django
RUN_ENV = 'DJANGO_RUN_ENV'
DEBUG = True
if os.genenv(RUN_ENV, '') == 'production':
# We don't want debug in production
DEBUG = False
SITE_ROOT = os.path.dirname(os.path.realpath(__file__))
if os.getenv(RUN_ENV, '') == 'production':
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'OPTIONS': {
'read_default_file': os.path.join(SITE_ROOT, 'mysql.cnf')
}
}
}
else:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'mydb.db',
}
}
# Further settings.py stuff
In my .wsgi file that is created to run Django under Apache (or any production webserver), I added these lines:
import os, sys
path = os.path.dirname(os.path.realpath(__file__))
if path not in sys.path:
sys.path.append(path)
os.environ['DJANGO_RUN_ENV'] = 'production'
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()
This can get a little messy if you let it. If you intend on changing a lot of stuff in the settings file, it may make more sense to have different settings files for each environment and then set the DJANGO_SETTINGS_MODULE environment variable appropriately. However, if at times you're like me and you just need to change one or two things in your settings file and having duplicated code in multiple settings files seems like a hassle, this may be a good option as well.
Feel free to comment...