开发者

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))
        # Obviousl开发者_如何学运维y 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


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.


The basic problem here is, even though you don't use django.contrib.auth that information about the current logged in user is only available in the view and never in the model, because this information is bound to a request. So you will always have to do something like this in the view:

def some_view(request):
    account = get_account_by_request(request)

Then you can use the account to filter models. You can always make this approach more elegant by using a middleware or a decorator but beware that it doesn't get too tricky. Your design might break at unexpected points (happened to me) because of too much multiple inheritances with inherited managers and the like. Keep it simple and predictable.


In order to get current user into your code, you can use threadlocals middleware but there isn't anything fancy about that, just one big hack, so watch out :)

Personally, i think it's bad to put that kind of logic into models, because they're just data and should not be aware of such things.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜