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()

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

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

http://github.com/mzupan/django-decorators

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.