I'm new to Django and I'm trying to figure out which path to take to solve a problem.
Many-to-one OR Django Groups?
I have a cat model and now I want to associate any new cats entered into my app with cat litters. (think new kittens)
I've been reading about Django Groups but only see this associated with the User model. Can Django Groups be used for any models I create? Is that the best path to get to what I want or should I just rely on a regular Many-to-One model relationship and build out regular models that way?
Groups: A generic way of applying labels and permissions to more than one user.
In your case I would use,
one to many: assuming each kitten belongs to one cat.
many to many: if you link kitten to utmost two cats.
You should use groups for user authentication things, like admin group or specific user group and assign permission or rights to specific groups.
So use accordingly.
more info on user authentication and groups
I have a Django app that uses the stock User and Group models. I have two groups, group A and group B. I would like to prevent any user from being in both group A and group B, being in just one group or the other is fine. what is the best way to go about this? I was looking into model validation but I would like to do this without modifying or sub classing the stock User model. It would even be enough for me to be able to check and make sure that this wasnt being set on the django admin edit user page.
I'd like to add the capability for users to create their own groups and invite others to join the group (or leave the group) or for users to request access to a group to a django app.
The group in this case is basically a pool of potential players for a football match, from which the actual players will be chosen.
Is the standard django auth groups system the correct thing to use here or is there another way? I'd need it to be able to do invitations and stuff, obviously.
I can obviously write my own but is there another app out there that already does this kind of thing nicely?
If not, has anyone got any tips on how to go about this?
Creating your own model will give you more control to add extra information to those groups, such as the invitation system you described. The auth groups models was designed for classifying users by what level of control they have over the data on the website, which isn't what you want a groups model for. A new model will be much easier for you work with than trying to extend the existing groups model; which has built in functionality that you won't use, and probably has security features that will make it difficult to work with.
I want to be able to efficiently find all user with a particular permission and then among those users find all users who have a particular flag (I extended the base User model and created my own, which has the flag). I was wondering what is the easiest/efficient way to do this? I was reading the below (among other sites):
How can I get all objects a user has specific permissions to in django guardian?
But they dont seem to be helping in my situation. Please let me know if there is an article I missed, thanks!
[EDIT]
I read the following page:
http://digitaldreamer.net/blog/2010/5/10/get-all-users-group-django/
Basically I want to get all users who have a particular permission AND who have a certain flag. Right now I can do: User.objects.filter(organization_id = id) to get all users within a particular organization as per my code. But within that list, I want all users who have a particular permission.
I asked a question related to django-guardian and answered it myself after much research. I believe you should be able to find your answer here: Django Groups and Permissions. Extending Groups to have a FK?
UPDATE
You could do something like this:
user_list = []
for user in User.Objects.filter(organization_id=id):
if user.has_perm('PERM NAME'):
user_list.append(user)
See this link for more details: http://packages.python.org/django-guardian/userguide/check.html
You could do it as mentioned above. But by chaining filter statements, it is also possible. I referred to this other question: How to get a list of all users with a specific permission group in Django. By chaining both filter statements, I can get all users with a permission and all users with the certain organization_id.
I'm looking for some input on how others would architect this. I'm going to provide class (django group) based views.
For example, a user's group will determine what views/templates he or she will have access to. I'm thinking of perhaps storing paths to view functions in a table to determine what a user's link bar will consist of. Filter specifications can also be stored to determine what rows will fill these templates.
A good example is a hospital nursing units. Nurses at one unit need not see the entire hospital's patients. They only need to see their patients. Doctors on the same unit need only to see those patients as well, but they should have access to much greater functionality.
Has this been done via some third party application? And how would you approach this problem?
Thanks,
Pete
Django already has a groups and permissions system, which may be sufficient for your purpose.
http://docs.djangoproject.com/en/dev/topics/auth/
Generally in your code you check if a user has a permission. A user has his own permissions and those of the groups he belongs to. You can administer this pretty easily from the admin console.
There are two parts you need to look at.
Check that a user requesting a page
has permission to do so.
Only display links to the user if he
has the permission.
For 1. you can check permissions in a decorator as such:
from django.contrib.auth.decorators import permission_required
#permission_required('polls.can_vote')
def some_view(request):
For 2. the currently logged-in user's permissions are stored in the template variable {{ perms }}. This code checks the same permission as above.
{% if perms.polls.can_vote %}
vote
{% endif %}
To generate a list of links you can iterate over user.get_all_permissions() and fetch the links (or function that generates the link) from a dict:
def more_elaborate_list_of_links_for_a_perm(user):
return ["/link1", ...]
_LINKS = {
'polls.can_vote' : lambda u: ["/user/specific/link/" + u.id],
'polls.can_close': lambda u: ['/static/link/1', 'static/link/2'],
'polls.can_open' : more_elaborate_list_of_links_for_a_perm
}
def gen_links(user):
# get_all_permissions also gets permissions for users groups
perms = user.get_all_permissions()
return sum((_LINKS[p](user) for p in perms if p in _LINKS), [])
There are probably many other approaches.
We had a similar problem. Django's groups aren't REALLY suited to this, but you can shoehorn them in.
The way we did it was as follows:
Every access-controlled object has a ManyToMany relation to the groups table. Each group was used to define a specific type of permission ("can view patient basics", "can edit patient contact info", and so on). Users are added to the groups that they should have permissions for (in your example of seeing only patients in this hospital, you could have a "valley-view-hospital" group).
Then when you go to display a list of records to a user, you filter based on the conjunction of the two groups. A user has to have all the associated group permissions to view a given object.
If your system requires it, you can keep a separate ManyToMany of negative permissions, or separate read/write permissions. You could also define a set of meta-groups (doctor, nurse) that result in your lookup filter retrieving the actual subset of permissions.
As far as your link-bar problem goes, you can generate those programatically using the same system - filter based on the classes of objects the user can see or edit, and then use a get_absolute_url() type function (maybe call it get_index_url()) to return the links for the index of each class of object.
Because all this is fairly complex, you'll probably end up wanting to do some level of caching for these things, but get it working before you bother optimizing. It is possible, and it's less ugly in code than it is in words.
There is a new very interesting project about role based permissions in Django:
http://bitbucket.org/nabucosound/django-rbac
I had a similar problem not too long ago. Our solution did the trick, though it might be too simple for your situation. Like everyone is suggesting, we used the django permission system to control what user interactions with models. However, we didn't just try to group users, we also grouped objects through a GenericForeignKey.
We built a model that linked to itself to allow for hierarchies to be developed.
class Group( models.Model ):
name = models.CharField( ... )
parent = models.ForeignKey( 'self', blank=True, null=True)
content_type = models.ForeignKey( ContentType )
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey( 'content_type', 'object_id' )
...
To make it work, we also created a model to serve as the django User model's user profile. All it contained was a ManyToManyField linked to the Group model above. This allowed us to give users access to zero or more Groups as required. (documentation)
class UserProfile( models.Model ):
user = models.ForeignKey( User, unique=True )
groups = models.ManyToManyField( Group )
...
This gave us the best of both worlds and kept us from trying to shoehorn everything into django's permission system. I'm using this basic setup to control user's access to sports content (some users can access whole leagues, some only one or two conferences, some only have access to individual teams), and it works well in that situation. It could probably be a generalized enough to fit your needs.
If you don't need real per-object ACLs, then you can just use the Django permission system. To get a list of all available permissions:
from django.contrib.auth.models import Permission
perms = Permission.objects.all()
There is an API for other authentication and authorization sources, so you do not need to stick with this permissions table.
You may hack this Django system to fit your needs in terms of this authorization model (RBAC) or you may come up with an ACL-like solution.
On a site for an expert on Pinot Noir wine we created per-object access based on a number of different criteria. If the inbound link had a referer field that matched the domain name of a featured winery, then the user got a 'winery token' which expanded to all articles, tasting notes, etc. related to that winery. We use 'named tokens' for give aways at tasting events and they gave access to specific parts of the site. We even use this to grant certain types of permissions to search engine spiders and then make sure that links that come from those search engines have the same permissions as the spider did (ie. no cloaking games).
The short version is that you can create a class (we called them TokenBuckets which hold Tokens) and each object (on a detail page, or a list page, or whatever) can ask the user's TokenBucket if a certain level of access is allowed.
Basically it's a weird kind of ACL system. It wasn't that hard to create the mechanics. All of the magic is in determining under what circumstances which tokens go into the bucket.
You can use django user roles
https://github.com/dabapps/django-user-roles
This question has been asked in Oct 2009 and the problem still exists in July 2012.
I have searched for a good Role-Based app, and found django-permission as the best result.
Three important features that I needed were Roles, view Decorators and Templatetag; apparently django-permissions has all of them. Read it's docs for it's usage.
The only drawback is that it's under development.
We used a role base system for a similar problem.
Basically users have permissions to assume different roles.
View functions got decorated:
def needs_capability(capability,redirect_to="/cms/"):
def view_func_wrapper(view_func):
def wrapped_view_func(request,*args,**kwargs):
if not request.role._can(capability):
return HttpResponseRedirect(redirect_to)
return view_func(request,*args,**kwargs)
return wrapped_view_func
return view_func_wrapper
The rest of the magic is inside the request.role attribute which got
set inside a context processor. Authenticated users got a Role, for the unwashed masses
a DummyRole.
Access to information was restricted further inside the templates:
{% if not request.role.can.view_all_products %}
Lots of products, yeah!
{% endif %}
Not the cleanest solution in my opinion, but worked as expected.