Fun with Django ManyToMany Fields
This is just another reason I find Django so easy.
For example.. Say you are making a monitoring application. Now you have a bunch of servers and you have certain groups of servers. A server can belong in more then one group also. So that is pretty easy and all with Django.
Now in your template you have a few pages. One page that displays the group information like all the servers in the group and another that displays the server information and will list all the groups that server is in. So below is my basic model layout
class Group(models.Model):
parent = models.ForeignKey("Group", null=True, blank=True)
user = models.ForeignKey(User)
name = models.CharField(max_length=100)
about = models.TextField(blank=True, null=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
def __unicode__(self):
return self.name
class Server(models.Model):
hostname = models.CharField(max_length=300, db_index=True)
about = models.TextField(blank=True, null=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
groups = models.ManyToManyField(Group)
def __unicode__(self):
return self.hostname
So to get all the groups my server is a member of is pretty easy.
s = Server.objects.get(id=1)
s.groups.all()
Now to get all the servers in a group is also easy
g = Group.objects.get(id=1)
g.server_set.all()
Get Django-NonRel working with VirtualEnv
MongoDB is getting a lot of press recently. I stumbled across a pretty interesting project called Django-nonrel. This is an attempt to build non-relational databases right into mongodb. Before you could always use a 3rd party ORM or just use pymongo right in Django but then you were missing out on all the nice apps the community has created. So the goal of this project is to make it so you can keep on using those. Now as of writing this joins are not working but from what I know they are working on it.
So lets start off by creating a virtualenv environment. I will call my project photoblog
virtualenv photoblog
Now lets activate our virtualenv
source ./photoblog/bin/activate
Now we can install Django-nonrel
wget http://bitbucket.org/wkornewald/django-nonrel/get/tip.zip
unzip tip.zip
cd django-nonrel
python setup.py build
python setup.py install
Now lets install pymongo
pip install pymongo
Now lets install the djangotoolbox
wget http://bitbucket.org/wkornewald/djangotoolbox/get/tip.zip
unzip tip.zip
cd djangotoolbox
python setup.py build
python setup.py install
Now we have to download and install the django-mongo-engine
wget --no-check-certificate https://github.com/aparo/django-mongodb-engine/zipball/master
unzip aparo-django-mongodb-engine*
cd aparo-django-mongodb-engine*
python setup.py build
python setup.py install
So now lets create the Django project
cd ~/photoblog
django-admin.py startproject photoblog
cd photoblog
Now the only thing you really have to change to get a basic site up and running is the following file
settings.py
So edit that and find the block that looks like
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
'NAME': '', # Or path to database file if using sqlite3.
'USER': '', # Not used with sqlite3.
'PASSWORD': '', # Not used with sqlite3.
'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
'PORT': '', # Set to empty string for default. Not used with sqlite3.
}
}
And you want it to look something like
DATABASES = {
'default': {
'ENGINE': 'django_mongodb_engine.mongodb', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
'NAME': 'photoblog', # Or path to database file if using sqlite3.
'USER': '', # Not used with sqlite3.
'PASSWORD': '', # Not used with sqlite3.
'HOST': 'localhost', # Set to empty string for localhost. Not used with sqlite3.
'PORT': '27017', # Set to empty string for default. Not used with sqlite3.
}
}
Then you want to add the following to your INSTALLED_APPS
djangotoolbox
Now you can sync the db to set it all up!
cd ~/photoblog/photoblog
chmod +x manage.py
./manage.py syncdb
You are all set!
WSGI Holygrail run multiple versions of Python on same webserver
One issue hosting providers have is the issue with clients needing to run different version of python. One client might need python 2.4 and others might need 2.5. So in comes uWSGI
So this blog post will go over how to install uWSGI on RedHat 5 and making a uWSGI server for both python 2.4 and 2.5 at the same time for different hosts.
So the first thing you want to do is download the source
wget http://projects.unbit.it/downloads/uwsgi-0.9.6.tar.gz
tar zxfv uwsgi-0.9.6.tar.gz
cd uwsgi-0.9.6
Now make the 2.4 version
make -f Makefile.Py24
mv uwsgi uwsgi24
Now make the 2.5 version
make -f Makefile.Py25
mv uwsgi uwsgi25
Now you have two binaries compiled to run 2.4 and 2.5 for python
So now you run the binaries
./uwsgi -s 127.0.0.1:3031 --pythonpath /tmp/multi/ -w django_wsgi
This will run a uWSGI server for a django app that is in /tmp/multi with a django_wsgi.py file.
You can then run
./uwsgi25 -s 127.0.0.1:3032 --pythonpath /tmp/multi/ -w django_wsgi
At the same time and they play nicely.
My django_wsgi.py file looks like
import os
import sys
sys.path.append('/tmp/multi')
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()
Django fix for ‘User’ object has no attribute ‘backend
I was trying to auto login a user if I had their user object and was getting this error when I just called something like
login(request, user)
Well this is due to Django wanting to make sure you auth the user first. If you want to bypass this you can just use the following before login
user.backend = 'django.contrib.auth.backends.ModelBackend'
This might not be the best solution but it works
Reason #4 on why Django is great
Middleware!
I can’t tell you how great middleware is in Django. It makes life so easy! Here is a good example. Django doesn’t handle ajax errors very good. It prints them to the client. Sure I can use firebug but I mainly use chrome now and don’t like to touch firefox anymore. So I wrote a bit of middleware to only print out errors in all ajax requests if the app was in DEBUG mode.
from django.conf import settings
import traceback
class AjaxErrorMiddleware:
def process_exception(self, request, exception):
if request.META.has_key('HTTP_X_REQUESTED_WITH') and settings.DEBUG:
print traceback.format_exc()
I have added this to my Github project also for all my middleware. You just load it by adding it to your middleware section in your settings.py
Reason #3 on why Django is great
Making custom model fields are a great thing to save a lot of time. If you need a lot of custom validation around your modelforms and don’t want to do a lot of copy and pasting then create a custom model field.
For example, I have the need for a user to input a lot of hostnames and domains and got sick of doing custom validation in each model form I created. So I created a model field for it.
So here it is
class HostnameField(models.CharField):
def clean(self, value):
value = super(HostnameField, self).clean(value)
import re
regex = re.compile("^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6}$")
r = regex.search(string)
if len(r.groups()) == 0:
raise models.ValidationError("You need to enter a valid hostname/domain")
return value
The regex might not be the best, but it seems to cover all the use cases I tried on it.
Django: Custom Decorator with an Argument
Sometimes you want to be able to create a decorator for functions in Django but have the ability to pass in an argument into the decorator. I came across this when I wanted to switch from the require_login decorator to check if a user was in a certain group to see the function.
So the first thing I did was create a new directory in my project root called decorators and put a blank __init__.py file there. My I cated an auth.py file with the following contents.
from functools import wraps
from django.contrib.auth.models import Group
from django.http import Http404
def group_required(groups=[]):
def decorator(func):
def inner_decorator(request,*args, **kwargs):
for group in groups:
try:
if Group.objects.get(name=group) in request.user.groups.all():
return func(request, *args, **kwargs)
except:
pass
raise Http404()
return wraps(func)(inner_decorator)
return decorator
So we can use it like this in our project
from decorators.auth import group_required
@group_required(["admins"])
def show_index(request):
....
I decided to make it a list being passed in. The main reason was to make it was to pass in multiple groups that you might want to allow to be allowed in.
My version doesn’t do much error checking.. if you want a more updated version of this function checkout my githib project I’ve started to put all my custom decorators up and online
Reason #2 on why Django is great
The decorators that Django has are a great time saver. There are a lot of methods in your views where you only want logged in users to see. So you can use a decorator before the function to check if they are logged in or not.
from django.contrib.auth.decorators import login_required
@login_required
def my_view(request):
....
So learn to use these time savers in your project.
Reason #1 on why Django is great
All the crazy helper function they have for models to display information in templates. Say I have a model that looks like this with a choice field (it will get rendered as a select input in html.
SEX_CHOICE = (
(1, 'Male'),
(2, 'Female'),
)
class Animal(models.Model):
name = models.CharField(max_length=50)
sex = models.SmallIntegerField(max_length=1, default=0, choices=SEX_CHOICE)
Now in my template I can do the following to see if an animal object is a Male or a Female
{{animal.get_sex_display}}
There is no need to create a method for that in the model. Django does it for you. Saving you time and effort.