Saturday 25 August 2012

django-model-permissions

I have created a small library to help create access control in Django projects. It uses a "lock" paradigm, where you can lock certain methods in Django Models (right now it actually works on every class, but I may be adding features that need more tight integration).

The purpose is to take access control to the Model layer, and out of the Controller (view) layer.

Here's how you use it (taken from the readme)

First, apply locks to some model methods:
class Example(models.Model, LockingMixin):
    class Meta:
        permissions = [
            ('example_can_get_id', 'Can get ID')
        ]

    name = models.CharField(max_length=33)

    @locks.permission('app.example_can_get_id')
    def get_id(self):
        return self.id
Then, instance it and use it. When you try to access a locked method, modelpermissions will throw an exception.
model = Example.objects.create(name='name')

try:
    model.get_id()
    assert False #remove this
except PermissionDenied:
    'django.core.exceptions.PermissionDenied was thrown'

But now let's unlock this model instance and try again

model.unlock(user)

model.get_id()
'no exception thrown'
 
Locks aren't limited to permission locks. You can use @locks.conditional and pass it a function that receives the model instance and do more flexible stuff with it.

And the future looks nice, too. Instead of just raising PermissionDenied, I feel that this could make more interesting stuff, like use an alternate method, when the user doesn't have enough permissions (even return a different Proxy model from the lock() method, which will start to return the model instance itself for chainability), and a different LockingMixin to lock things only when you explicitly call lock() on the Model.

It's available on github. Try it out!

Tuesday 14 August 2012

Django Test Coverage

The django-test-coverage (https://github.com/srosro/django-test-coverage) is an exciting project that leverages coverage.py in Django projects.
It is most helpful in figuring out if your project is being comprehensibly covered by your automated tests. When you run ./manage.py test, django-test-coverage outputs coverage information. It doesn't help much that the coverage information prints out even when tests fail, making you scroll your command line session a bit more, but this might be fixable.
I was looking for a way to check test coverage for a Django application, and noticed that the project didn't have its own setup.py, nor was it ready for Django 1.4 because of a python package issue. I forked the project, created an appropriate python package, and moved the files in. Then I created a setup.py.
Not a big change, but it allowed me to test my application with coverage on Django 1.4, as well as install django-test-coverage much more cleanly, using pip.
My own fork is here: https://github.com/fabiosantoscode/django-test-coverage. You can report any bugs, suggest stuff to be added, fork, pull request...
Django becomes an even greater and more useful framework because of small plugins like these. It's fascinating how a few lines of code mostly wrapping external functionality can make such a big difference.