Ran into a problem that has me scratching my head, documentation hasn't really pulled me any luck so I figured I'd see if anyone else has been in this situation. So let's say I have a model (among others):
Organization
That sort of acts like a psuedo-tenant. The thing is, users within my app may or may not have permissions to multiple organizations, some only 1, some this, some that. Point is, there's no clear structure on where permissions lay for each user. By default it should be none as it is now until manually granting, but instead of granting permissions to all or no organizations has anyone ran into a similar situation of being in need to control permissions in a granular/dynamic fashion? It is important to note that these 'organizations' are loaded dynamically via an API to an external product.
I realize this can be done by crafting my own sort of permissions system outside of Django but my main question is can this be done with conventional Django permissions or is this out of scope?
Thanks
You need object-level permission
As already mentioned, you could use something to help you with the object permissions, like django-guardian. However, if it is for one model and it is something simple, consider also defining a custom permission in your organisation model:
class Organisation(models.Model):
# your fields
class Meta:
permissions = (('my_shiny_permission', 'Some description for it'),)
And then in your views you can define the logic for it:
def my_view(request):
if request.user.has_perm('my_app.my_shiny_permission'):
# do something
More about custom permissions - here.
Related
Django allows you to give users and groups of users custom permissions on objects (and django-guardian adds some nice ways of using this). There are various ways of putting users into hierarchies. What I'd like to do is add two types of hierarchies to the permissions themselves. For example if someone has a given permission on a Book object, I want them to implicitly have that permission on each Page object. Also, if someone has an change permission on a Page, I want them to implicitly have the view permission on that Page.
In sum, I want page.has_permission('view', user) to check both page.has_permission('*edit*', user) and page *.book* .has_permission('view', user), and for book.has_permission('view', user) to itself check book.has_permission('*edit*', user). (Asterisks just for emphasis.)
I don't need anything as complex as django-rules, and I prefer the generic foreign key approach as I will not have large numbers and it keeps the model structure clean and focused. I'd like to avoid repeating the logic for these permission relationships in views, or cluttering models, and I'd ideally keep this permission structure centralized somewhere, ideally declaratively. For example
perm_hierarchy = {'view': ['edit', 'delete', ]}
model_perm_hierarchy = { Page : [Chapter, ],
Chapter : [Book, ]}
Then a layer that when checking for 'view' on Page checks for 'edit' and 'delete' permissions on that Page object.
And similarly when checking Page for a permission, checks Chapter for the same permission (if that same named permission is defined for Chapter).
Very happy to be told I'm thinking about this wrong.
I document a solution using django-rules on top of django-guardian in this answer
In the Django project I work on, the built in ('add', 'change', 'delete') permissions are next to useless for us, and only confuse our users in forms; we don't use the Django admin, it often doesn't make much sense for us to have three permissions instead of one edit permission, and our models are more granular than the objects we want to assign permissions to (as a made-up example, it doesn't make sense to have seperate permissions for an Invoice and an InvoiceLine - they're conceptually the same thing).
Anyway, at the moment I'm accomplishing this by subclassing each model from a custom abstract base model which has default_permissions = () in it's Meta, plus some ugly hacks to make the permissions from third-party models go away. Is there any way I can make Django not create those three permissions by default?
If you don't use the admin then just don't bother about the default permissions. They are not enforced by default anywhere else. Feel free to create your own Permission and check that the user has this in your edit views, with permission_required for function views and PermissionRequiredMixin for class based view (and Django>1.9).
I'm currently designing a Django based site. For simplicity lets assume that it is a simple community site where users can log in and write messages to other users.
My current choice is wether to use the buildin User-Model or to build something my own. I don't need much from the buildin User: there will be no username (you e-mail address is you username), but you an set an internal Name of your choice which can be used by multiple users (like Facebook). Additionally, I don't need the permission system, since access to others will not be based on groups. So I would end up using only the email, firstname, lastname and password fields from the buildin User and everything else would be placed in a UserProfile.
On the other hand, the buildin User system will come handy on the backend of the site, since there is the chance I will need a group based permission system there.
All in all, it looks to me, that I rather build my one User Model and use the buildin only for access to the admin backend.
Is there anything wrong with my reflections?
Is there anything wrong with my reflections?
Yes.
My current choice is wether to use the buildin User-Model or to build something my own.
There is a third choice.
http://docs.djangoproject.com/en/1.2/topics/auth/#storing-additional-information-about-users
everything else would be placed in a UserProfile
Correct.
build my one User Model and use the buildin only for access to the admin backend
Don't build your own.
Do this:
If you'd like to store additional
information related to your users,
Django provides a method to specify a
site-specific related model -- termed
a "user profile" -- for this purpose.
As the author of django-primate I would like to add some comments. Django-primate which easily lets ju modify the built in User model is meant for just that. You might need just something a little extra, then use django-primate.
But there are problems, although I do not think modifying the django User model per se is a problem at all. One problem is that the "users" are quite different, the admin user and some other user are often not related. This can cause problems when for example an admin is logged in and then wants to login to the site as a "normal user", they do not expect those accounts to be related and do not expect to be logged in automatically as the admin user. This causes headaches for no reason. It also causes a lot of other headaches to implement the recommended related Profile model, you often need to make sure there is a contrib user for every profile and a profile for every contrib user if you for example want to use the authentication decorators. Forms and administration of "users" make this even more cumbersome. In short: usually something will go wrong in this process at some point, it's a curse.
I have mostly abandoned the contrib User model for anything else but for admins. Building another user model is really what you want, but you also want the authenicating part for that user, hence the common use of django contrib User (using it for the wrong reasons). The best solution if you are in a situation like this is to build your own authenication for that custom user model. This is actually quite easy and I cannot recommend this approach enough. I think that the official recommendation is wrong and that there should instead be good tools for authenticating custom user models built into django.
You might want to have a look at the recently created django-primate: https://github.com/aino/django-primate
I once built a custom user model, inheriting from the default one. It works, however, I wouldn't recommend it.
Currently, you have some requirements, but over time they may change. Django's user system is quite straightforward, and using it allows to adapt more easily to some of the most common use cases.
Another aspect to think about, is that there are several applications already available that you can use, and that may require Django's users. Using your own model, may make usage of such modules much more difficult.
On the other hand, hacking the Django's user system in order to comply with your current requirements may be tricky.
Moreover, migrating a 'Custom-User' to a 'Django-User' is always possible, so you are not really closing that door.
Overall, I think it really depends on what you mean with 'user'.
If you mean just a registration, and no real interaction with the core Django features, then I think a separate model is enough, especially because you can migrate at any time, with relatively little effort.
However, if for your application a 'user' maps to something very similar to the what Django is for, then I would use the Django User-Model.
I have a couple of different profiles. I want to associate permissions with these profiles. I've done so like this:
class StudentProfile(UserProfile):
school = models.CharField(max_length=30)
class Meta:
permissions = (
("is_student","Can access student pages"),
)
however, when I try and check if that permission exists using has_perm on that profile object, I get an error "'StudentProfile' object has no attribute 'has_perm'" am I not supposed to check for permissions in this way? I've read the docs and that's what I thought I was supposed to do
Edit: After reading the docs again, it seems that has_perm is a method belonging to Users and NOT their profiles. However, when I try to show the permissions:
print user.get_all_permissions()
I get an empty set. Shouldn't I see something like "appname.is_student"
.has_perm is a method on the User object, not on a UserProfile object. If you are trying to validate that a user has the permission has_student, you'd need to do something like this:
user.has_perm('profiles.is_student')
assuming that your StudentProfile model is in a profiles application.
EDIT: To address your rephrased question, you should assign permissions the normal way, either to the group or to a particular user, and use User.has_perm. Your latter example goes completely against the point of the Django permission system.
I'm wondering what the common project/application structure is when the user model extended/sub-classed and this Resulting User model is shared and used across multiple apps.
I'd like to reference the same user model in multiple apps.
I haven't built the login interface yet, so I'm not sure how it should fit together.
The following comes to mind:
project.loginapp.app1
project.loginapp.app2
Is there a common pattern for this situation?
Would login best be handled by a 'login app'?
Similar to this question but more specific.
django application configuration
UPDATE
Clarified my use-case above.
I'd like to add fields (extend or subclass?) to the existing auth user model. And then reference that model in multiple apps.
Why are you extending User? Please clarify.
If you're adding more information about the users, you don't need to roll your own user and auth system. Django's version of that is quite solid. The user management is located in django.contrib.auth.
If you need to customize the information stored with users, first define a model such as
class Profile(models.Model):
...
user = models.ForeignKey("django.contrib.auth.models.User", unique=True)
and then set
AUTH_PROFILE_MODULE = "appname.profile"
in your settings.py
The advantage of setting this allows you to use code like this in your views:
def my_view(request):
profile = request.user.get_profile()
etc...
If you're trying to provide more ways for users to authenticate, you can add an auth backend. Extend or re-implement django.contrib.auth.backends.ModelBackend and set it as
your AUTHENTICATION_BACKENDS in settings.py.
If you want to make use of a different permissions or groups concept than is provided by django, there's nothing that will stop you. Django makes use of those two concepts only in django.contrib.admin (That I know of), and you are free to use some other concept for those topics as you see fit.
You should check first if the contrib.auth module satisfies your needs, so you don't have to reinvent the wheel:
http://docs.djangoproject.com/en/dev/topics/auth/#topics-auth
edit:
Check this snippet that creates a UserProfile after the creation of a new User.
def create_user_profile_handler(sender, instance, created, **kwargs):
if not created: return
user_profile = UserProfile.objects.create(user=instance)
user_profile.save()
post_save.connect(create_user_profile_handler, sender=User)
i think the 'project/app' names are badly chosen. it's more like 'site/module'. an app can be very useful without having views, for example.
check the 2008 DjangoCon talks on YouTube, especially the one about reusable apps, it will make you think totally different about how to structure your project.