Search code examples
djangodjango-modelsdjango-managers

Django: How would one organize this big model / manager / design mess?


To sum things up before I get into bad examples, et al: I'm trying to make an application where I don't have to write code in all my models to limit choices to the current logged in account (I'm not using Auth, or builtin features for the account or login).

ie, I don't want to have to do something like this:

class Ticket(models.Model):
        account = models.ForeignKey(Account)
        client = models.ForeignKey(Client)  # A client will be owned by one account.
        content = models.CharField(max_length=255)

class TicketForm(forms.ModelForm):
        class Meta:
                model = Ticket
                exclude = ('account',)  #First sign of bad design?

        def __init__(self, *args, **kwargs):
                super(OrderForm, self).__init__(*args, **kwargs)
                if self.initial.get('account'):
                        # Here's where it gets ugly IMHO. This seems almost
                        # as bad as hard coding data. It's not DRY either.
                        self.fields['client'].queryset = Client.objects.filter(account=self.initial.get('account'))

My idea is to create an Account(models.Model) model with the following custom manager, and subclass it using multi-table inheritance with all of my models. It's giving me a huge brain ache though. Will I still need an account foreign key on each model? Can I access the parent class account for a certain model instance?

class TicketManager(models.Manager):
    def get_query_set(self):
        return super(TicketManager, self).get_query_set().filter(account=Account.objects.get(id=1))
        # Obviously I don't want to hard code the account like this.
        # I want to do something like this:
        # return super(ProductManager, self).get_query_set().filter(account=self.account)
        # Self being the current model that's using this manager
        # (obviously this is wrong because you're not inside a model
        # instance , but this is where the confusion comes in for me.
        # How would I do this?).

Please ignore any blaring syntax errors. I typed this whole thing in here.

Here's where I got the idea to do this: Django Namespace project


Solution

  • There are two closely related problems when it comes to Django.

    One is row level permissions where users/accounts need specific permission to view a specific row (object) in a table, as opposed to normal Django auth framework which has table level permissions.

    The project you linked to is one of several projects trying to implement row permissions. django-granular-permissions is another and a third (my favorite and the most active/maintained) is django-authority.

    The upcoming release of Django 1.2 will have hooks making row-level permissions easier to implement, and the author of django-authority will work on integrating his project.

    The second related problem is something called multi-tenant database which is a variation on row permissions. In this scheme you might have multiple users from a single company, for example, who all have access to data for that company but not other companies (tenants).

    I don't think this is what you're looking for but you might be able to use some of the same techniques. See how to enforce account separation in Django and multi-tenant django applications. Both have really sparse answers but are a starting point as well as looking at multi-tenant architecture for Rails apps and this article.

    As for a more specific answer to your question I think you should either use django-authority or write a custom manager and use the record ownership screener during development to verify your queries aren't bypassing the custom manager.