I have a model and I’d like to restrict access to objects based on the authentication level of the user. Anonymous users may only see a subset of the objects while authorized users have access to all objects. Reading through the django book I find that I could use checks like, is_authenticated() in my views and implement my logic based on that condition. But I don’t want these checks shotgun splattered all over my code, instead I’d like to be able to give my models some intelligence: the models should only make available the objects that are visible to the current user’s permissions.
Here are a couple of the models I’m working with:
class Collection(models.Model):
VISIBILITY_CHOICES = (
('P', 'Private'),
('SP', 'Semi-Private'),
('PUB', 'Public')
)
name = models.CharField(max_length=40)
visibility = models.CharField(max_length=3, choices=VISIBILITY_CHOICES)
category = models.CharField(max_length=50)
class Image(models.Model):
image = models.ImageField(upload_to= get_upload_to)
collection = models.ForeignKey(Collection)
An example of a query I make would be: collection_ids = Image.objects.values_list(‘collection’,flat=True).distinct() — In this case I’d only want to check against those collection ids that the user is authorized to view (i.e public/private/semi-private).
Is it possible or do I have to put authorization logic in my views?
Note that in the query example you give (
Image.objects.values_list('collection',flat=True).distinct()),requestis never mentioned – so how would that query be able to check the current user?But that doesn’t mean you have to do it in the views – you can add methods to the model itself, or even subclass QuerySet to add a method to check against the current request, like
.filter(foo=bar).allowed_for_user(user).See this answer:
https://stackoverflow.com/a/4576649/16361
Which links to an ancient blog post by yours truly, although I seem to have broken the link in that answer, sorry – the correct URL is:
EDIT: Remove a domain taken over by NSFW content and a dead link.
Also, just to make it clear – the code itself doesn’t have to live in your views, but ultimately will need to involve the request object one way or another, so you will have to call it from your view.