Archive for May, 2009

django-formfieldset

Thursday, May 28th, 2009

django-formfieldset is a Django application that allows you define and render your forms with fieldsets. Just like in admin. To enable fieldset rendering you need to add FieldsetMixin as a parent class to your form and define a fieldsets attribute:

from django import forms
from formfieldset.forms import FieldsetMixin


class MyForm(forms.Form, FieldsetMixin):
    # Fields etc...

    fieldsets = ((u'Fieldset Title',
                  {'fields': ('foo', 'bar', 'baz'),
                   'description': u'This is the description for fieldset.'}),
                 (None,
                  {'fields': ('some_field',),
                   'description': u'This fieldset has no title.'}),
                 (u'Fieldset With No Description',
                  {'fields': ('some_other_field',)}))

Then you can render your form with fieldset enabled methods:

<form method="POST" action=""><table>{{ form.as_fieldset_table }}</table></form>

It is far from complete1, but feel free to download and play with it.


1: Not released yet.

Bookmark and Share

Serving Static Media In Django Development Server

Monday, May 25th, 2009

There is a misconception about how static files (a.k.a media files) are handled in Django. Actually it is quite clearly documented here and here. Nevertheless a question about this comes up in the mailing-list or IRC channel frequently:

Where do I put my media files?

Django can’t find my foo.gif!

How can I link my CSS?

First of all, just to make it clear; just because a server returns a response body with an internal URL doesn’t necessarily mean it will be available on that server. It is one thing that your templates produce the correct URL to a media file and another thing that your server actually serves that resource on that URL. Django development server doesn’t automagically serve media files1.

Settings

There are three settings to get right: MEDIA_ROOT, MEDIA_URL and ADMIN_MEDIA_PREFIX. MEDIA_ROOT is the absolute filesystem path where your media files are. I usually set it like:

MEDIA_ROOT = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'media')

This will set MEDIA_ROOT to point to the media directory in your project directory2. MEDIA_URL and ADMIN_MEDIA_PREFIX are URL’s:

MEDIA_URL = '/media/'
ADMIN_MEDIA_PREFIX = '/media/admin/'

With this setup, to serve admin media in production, all I need to do is to symlink media folder of admin app into my media directory. Of course you can set MEDIA_URL to point to another domain/subdomain. Such as http://media.mydomain.com/. But this way you can’t serve your media from development server.

URL Configuration

Add the following code snipplet at the end of your root urls.py:

   1 if settings.DEBUG:
   2     from django.views.static import serve
   3     _media_url = settings.MEDIA_URL
   4     if _media_url.startswith('/'):
   5         _media_url = _media_url[1:]
   6         urlpatterns += patterns('',
   7                                 (r'^%s(?P<path>.*)$' % _media_url,
   8                                 serve,
   9                                 {'document_root': settings.MEDIA_ROOT}))
  10     del(_media_url, serve)

settings.DEBUG == True doesn’t necessarily mean development server is running. But it is a good indicator since deploying with development server is not a good idea for many reasons. Notice here we don’t serve media unless MEDIA_URL is an absolute URL on our server.

Templates

Finally we need to specify media URL’s correctly. To avoid hard-coding media path we will be using {{ MEDIA_URL }} context variable in our templates. To have {{ MEDIA_URL }} included automatically in each template we need to do two things:

  1. Make sure you have django.core.context_processors.media in your TEMPLATE_CONTEXT_PROCESSORS.
  2. Make sure each view is using a RequestContext.

Afterwards all we need to do is to specify our media URL’s like this:

<img src="{{ MEDIA_URL }}img/header.jpeg" />

This will be translated to:

<img src="/media/img/header.jpeg" />

Bonus

While we are at it, why not serve our 500 and 404 pages statically. When DEBUG == True, 500 (server error) and 404 (not found) situations are handled with special debugging views. So there’s no chance to test your error pages. Add the following code, just like static serving code:

   1 if settings.DEBUG:
   2     urlpatterns += patterns('',
   3                             (r'^404/',
   4                                 'django.views.generic.simple.' \
   5                                 'direct_to_template',
   6                                 {'template': '404.html'}),
   7                             (r'^500/',
   8                                 'django.views.generic.simple.' \
   9                                 'direct_to_template',
  10                                 {'template': '500.html'}))

Now when you visit /500/ and /404/ on your development server you will be served a fake error page.


1: There is an exception here. If you configured your settings correctly, development server will serve admin media.

2: Assuming your settings.py is directly inside your project directory, hence the __file__.

Bookmark and Share

Sad State of Web Development Industry in Türkiye

Sunday, May 24th, 2009

I met the founders of kariyerGENÇ last week. It is job search service for new graduates. During our conversation Sinan asked told me I should sign up too. I must say I’ve found it flattering that he considered me young. So I signed up. The service is built around the main value of building your CV online easily. OK, let’s build a CV. Aren’t CV’s fun.

I have edited and edited and edited… Finally, last section; proficiencies. Huh, which proficiencies could a new graduate possibly have? Let’s make it the last part.1 I scrolled through the dropdown of options to find Python and Django. No Python! Unsurprisingly no Django. But wait, the list doesn’t have Ruby, RoR, LISP either. But they included some programming languages I have never heard of; like HTML. What is this HTML programming language?

Basically I was left with the choices of Java and C. I couldn’t select them because neither is my main programming language. I can only select them after I select Python. So I left that section blank and sent them a contact form thing. It’s not cool not having an e-mail in your contact us section guys. If you are concerned about SPAM there are ways to evade e-mail harvesting bots. Please don’t be afraid of people who prefer using e-mail. I got a timely reply. From a nobody. Again, not cool. I am person and I’d like to communicate with another person. It’s OK to publish a generic e-mail on the site. But the reply should come from a employee e-mail and contain the employees name. I give you my name and e-mail when I contact you, right? You should have the decency to do the same when you reply.

Anyway, the reply I got was brief. In short it said “OK, we’ll do it.” Nice. Except they didn’t. I checked yesterday and they still had HTML programming language and not Python. Oh, well. So much for the orange ties. I deleted my account for the reason of being totally useless. It could be useful feedback if you asked why, when your users delete their accounts, you know?

In fact kariyerGENÇ is not doing it wrong. They’re just taking a picture of The Industry. The picture is correct, The Industry is doing it wrong. But that’s another topic for discussion2. What was disappointing for me was the lack of agile in kariyerGENÇ. I don’t mean agile methodology here, I mean agile technology. Technology and infrastructure that changes and adopts fast. For instance; how long would it take to make the change in a Django project? Yes, Django. The framework most people ignore or haven’t even heard of. You would just log-in the admin, add a new proficiency, save, optionally run a custom management command. 10 minutes at most.

It doesn’t matter much even if they add new proficiencies now. First of all a fixed list of options doesn’t work well where there are virtually unlimited possibilities. Second of all that dropdown is hideous, and increasing the number of options will only make it worse. Instead they should just let people tag themselves, WordPress style. Django-tagging does that automagically. You know, Django, the framework you don’t list in your proficiencies.

What Most People Agree On Is Not Necessarily The Best

The language for web development in Türkiye is PHP. Java is on the rise, in a couple of years it will be as popular as PHP. I am not kidding myself; these two and that horrible propriety thing that I don’t even want to name are industry standart in the world. But they also use other technologies. At least they experiment. At least they know that other alternatives exist. Here those minorities doesn’t even exist.

I frequently hear people saying “Python is a toy language“. Because for them there are serious, proper programming languages for grown-ups and serious, proper frameworks for grown-ups as Cal Henderson said …and everything else is a toy. You can’t use Python/Django for anything professional. Well, Markafoni did, with much success.

Think about it for a second; if what the majority have generally chosen the best, wouldn’t we live in a better society & environment than this? Just think about it.

Meanwhile I’ll be playing with my toys. :D


1: Actually kariyerGENÇ got it right here; in reality most of the new graduates have no proficiency. So, they’ll pick I know Word and Excel here even though they aren’t really proficient.

2: …and flame wars.

Bookmark and Share

Nominate Qooxdoo for SourceForge Community Choice Awards

Saturday, May 16th, 2009

I’ve just voted Qooxdoo for Most Likely to Change the Way You Do Everything category.

You can use the link below to vote yourself:

Bookmark and Share

Django Permission System

Thursday, May 14th, 2009

Permission system that comes with django.contrib.auth allows you to create and assign permissions for your models. Permissions can be assigned to users or groups. A user has permissions directly assigned to her as well as permissions inherited from her groups. Permission scope is always model classess (as opposed to individual model instances). That means if you are just checking FooModel.can_do_bar permission on a user; that user can do bar on all FooModel instances.

This built-in permission system is simple; as in you shouldn’t expect complex authorization schemes to be implemented easily. It is mainly used by django.contrib.admin and you can use it without any hassle for your very simple authorization requirements. Beauty of this permission system is it’s simplicity. Here are a few strong points:

  • All your models get add, change and delete permissions created automatically.1 These three are all you need for most of your models.
  • Admin uses permissions internally. But more importantly permissions can be granted/revoked dynamically via admin.
  • Simple design encourages you to keep authorization scheme clean.2

The last point is very important; permissions shouldn’t leak into business logic. If you have a project that goes beyond a CMS, you probably have business objects. Business objects might have quite complex states and typically interact with each other in more than straightforward ways. On the other hand models in CMS style projects have simple states that are independent (of other models). Take a blog post; it is either published or not and its author is simply a ForeignKey to Users. If you depend heavily upon permissions for complex state transitions of your models you will soon find yourself in a dead end.

Permissions in Fixtures

One problem, and it’s a big problem, I’ve had with permissions is that; you can’t store permissions in fixtures. Here is what happens:

  1. Relationships are stored in fixtures as primary keys.
  2. Permission are created by the framework programmatically in a specific order.
  3. When you add/delete a new model or add/delete a custom permission this order changes.
  4. Therefore primary keys of permissions change.
  5. As a result permission data in your fixture are invalid.

I’m a big fan of fixtures. More often than not you have some models that will be used as definitions (they’re partially fixed data) or you want to kickstart your project with placeholder data. It’s quite easy in Django. After creating your models, launch admin and create your data. Then simply dumpdata, maybe post-process a little bit and you have your initial_data! Not so easy if you have permissions in that basket.

Best solution I can come up with now is to hook a post_syncdb function and add your permissions programmatically.

Authorization

Basic usage of permissions is to check if a user has a certain Permission and branch accordingly:

@login_required
def some_view(request):
    user = request.user
    if user.has_perm('foo.bar_baz'):
        # go on and bar that baz
    else:
        # display an error message
    # ...rest of the view

When you need to combine conditions, it is a good idea to abstract authorization check in a function:

class Foo(models.Model):
    # stuff that goes into a model

    def can_bar(self, user):
        return user.has_perm('app.bar_foo') and user.baz >= self.baz

You can combine this with techniques explained in here and here to achieve column level permissions.

Row level permissions are cooking as well. There is a branch for per object permissions. You can checkout this branch from subversion with the following command:

svn co http://code.djangoproject.com/svn/django/branches/per-object-permissions

Generic Permissions

Let’s define permissions problem in a more generic way. A permission determines if:

  • An actor can
  • Perform an action
  • On an object
  • Depending on arbitrary number of runtime conditions [optional]

In our little implementation actors will be auth.User instances and objects, naturally, models. Actions will simply be keys of string type. Because of the possibility that our permission can have conditions, it should be a callable. Then we will have all the power Python/Django has.

Let’s place our permissions on our models:

   1 class FooPermissions(PermissionMixin):
   2     def __warg(self):
   3         return max(self.quux - self.parent.quux, 1)
   4 
   5     @staticmethod
   6     def allows_bar_for(actor):
   7         return actor.has_perm('app.bar_foo') and actor.status == 'barrable'
   8 
   9     def allows_baz_for(self, actor):
  10         if self.qux_set.count() > 10:
  11             return True
  12         else:
  13             return self.__warg() > 5 or actor.is_staff
  14 
  15 
  16 class Foo(models.Model, FooPermissions):
  17     # fields, managers, etc...

First we created a permissions mixin, subclassing a PermissionMixin I’ll show it to you in a minute, and mixed it with our model definition. This way we keep permissions seperate at the source level. Also, for instance __warg method will not be accessible from our model. You can do anything inside your permissions class, you can even have side effects if you like. all you have to do is to follow a naming convention for your permission methods. Here is the PermissionMixin class:

   1 from django.contrib.auth.models import User
   2 
   3 
   4 class PermissionError(StandardError):
   5     pass
   6 
   7 
   8 class PermissionMixin(object):
   9     def attempt(self, action, actor, msg=None):
  10         return PermissionMixin._attempt(self, action, actor, msg=None)
  11 
  12     @classmethod
  13     def cls_attempt(cls, action, actor, msg=None):
  14         return PermissionMixin._attempt(cls, action, actor, msg=None)
  15 
  16     @staticmethod
  17     def _attempt(obj, action, actor, msg):
  18         if actor.__class__ != User or not isinstance(action, basestring):
  19             raise TypeError
  20         if getattr(obj, 'allows_%s_for' % action.lower().replace(' ',
  21                                                                  '_'))(actor):
  22             return True
  23         else:
  24             if msg is None:
  25                 msg = u'%s doesn\'t have permission to %s %s' % (actor.username,
  26                                                                  action.lower(),
  27                                                                  repr(obj))
  28             raise PermissionError(msg)

When you want to check for a permission you just need it’s key (name of the action) and have your User at hand:

# Check a permission on a model:
Foo.cls_attempt('bar', user)

# Check a permission on an instance:
foo.attempt('baz', user)

Notice that we are still using Permissions from auth. Our PermissionMixin can do complex logic, but its hard-coded. We still need something like Permissions for dynamic behaviour. Why not use what’s built-in?

You can find PermissionMixin‘s code at Django snipplets.

Conclusion

Before employing a similar mechanism, you should think hard if you absolutely positively need it. Maybe you can simplify your UI? Maybe there is a nice tradeoff between code complexity and interactivity of your application? Maybe you can move this logic into model.save() or form.save() or form.clean()? Or maybe your code will be best organized if you use PermissionMixins.

I just wanted to show that you can build on auth‘s Permissions. Just don’t get discouraged when you find out you can’t do x out of the box. Be it permissions or some model magic or making something available in your templates; Django is Python, it can be extended easily.


1: Provided you have auth app installed of course.

2: Ask yourself; “Is this really a must?”, and not only for permission related complications.

Bookmark and Share