I have a custom group model like this:
class MyGroup(models.Model):
name = models.CharField(max_length=200,null=True,blank=False,default="Group name")
members = models.ManyToManyField(get_user_model(), blank=True, related_name="grpmembers")
created_by = models.ForeignKey(get_user_model(), on_delete=models.DO_NOTHING, null=True, blank=False, related_name="createdby+")
created_at = models.DateTimeField(editable=False)
It works, it's fine, I override the save method in django admin so the created_by will point to the logged in user on save.
Problem #1
Even if you are the creator of the group, you can select yourself to either be in- or be removed from the group which kinda looks silly. I'm thinking of solving this by saying the user can view the group if they're in members or created_by.
Problem #2
Custom permission. I want to have some permissions, like:
Can view the group: which means the user is either the creator or is in the members list
Can edit the group: which means the user is the creator(can edit their own) or is staff(can edit anyone's stuff) or is superuser(root)
I can write it down and imagine how it would work, but I have no idea how to implement these.
I've found some ways, like creating a Meta and defining permissions there and also making the permissions as def(/functions), but how could I access the currently logged in user?
Like okay let's say I do
def can_view_group(self):
r1 = self.filter(members=req.user) # where req.uset is not accessible bc we're in models.py
, but how do I tell the permission to check the currently logged in user?
The problem is doing all the logic in models.
Over problem 1 you need to verify user is not owner before allow to leave in the views.py.
Over the problem 2 are 2 solutions, or you move can_view_group to views.py, or you define that in models as:
def can_view_group(self, user):
#all the logic
And you pass the user from views.py
Related
I am relatively new to Django and I'm looking for some guidance in how to setup permissions in a certain way. Basically I have an app that consists of a couple of models similar to this:
class Project(models.Model):
name = models.CharField(max_length=100)
users = models.ManyToManyField(CustomUser, related_name="projects")
class Task(models.Model):
name = models.CharField(max_length=100)
project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name="tasks")
class Asset(models.Model):
name = models.CharField(max_length=100)
project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name="assets")
My idea is that if a user is "assigned" to a project (via M2M field), that user will have access to all assets and tasks that are related to that Project. I have looked into django-guardian for per-object permissions and I think that could be the way to go, but to me it seems like I then would have to setup those permissions on each model..?
It feels like this should be a pretty common way of setting up permissions for any project-based app but I have a hard time finding similar examples and starting to wonder if I'm overthinking this or looking in the wrong direction?
Thank you,
Jonas
You can use django-rules to take advantage of object-level permissions without a database; with it, you can add permissions in many levels - models, views, templates, admin or DRF.
So, you'd need to create a predicate like
#rules.predicate
def is_project_manager(user, project):
return project.users == user
which will return True if the project's manager is the given user, False otherwise.
Then, to add it in a model, you'd do something like
import rules
from rules.contrib.models import RulesModel
class Project(RulesModel):
class Meta:
rules_permissions = {
"add": rules.is_project_manager,
"read": rules.is_authenticated,
}
There's ofc other considerations to attend to but I think that gives an overview of how it works.
Normally, after migrating the models located in models.py, in django-admin you get a set of default permissions for your models. For example:
models.py
class Appointment(models.Model):
time = models.TimeField(auto_now_add=False, auto_now=False, null=True, blank=True)
notes = models.TextField(blank=True, null=True)
In django-admin I can assign these permissions to a group:
appname|appointment|Can add appointment
appname|appointment|Can change appointment
appname|appointment|Can delete appointment
appname|appointment|Can view appointment
However, I want to add a new permission to this list that will be able to allow a staff user to change the notes field only, such as:
appname|appointment|Can change appointment notes
I know I can add it like this:
class Meta:
permissions = [
("change_notes", "Can change appointment notes"),
]
However, the lines above will only add the permission to the list, and after assigning it to a group nothing happens (the notes field cannot be changed).
How can I solve this? Is there any Django extension that can help me add a custom model permission in to edit a particular field?
I have a user named ExpertUser who should only be able to modify an attribute of the called model (money) of the users in the Django Admin.
I have tried adding permissions in the model using the Meta but when entering with that permission I can not modify anything since I do not have access to any user.
My model is this:
class Client(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
money = models.FloatField(default=1000)
def __str__(self):
return self.user.username
class Meta:
permissions = (("can_view_money", "Can view money"),)
Have a look at Django Guardian. That provides more flexibility in assigning permissions on various levels. Django only lets you set permissions at object level, so in your case you were only able to set permission to create/edit/delete Client objects.
http://django-guardian.readthedocs.io/en/v1.4.8/index.html
If that doesn't suffice, there are a few other packages with the desired functionality:
https://djangopackages.org/grids/g/perms/
I am new to Django and I have failing on a task now for a while. I have searched in the Django docs and here for some questions but there nothing is working for me.
Here is my view:
def user_accept(request):
if request.method == 'POST':
username = AddUser.objects.filter(owner=request.user).get().user_request
group_name = Projects.objects.filter(user=request.user).get().group_url
group = Group.objects.get(name=group_name)
group.user_set.add(username)
delete_user = AddUser.objects.filter(user_request=username)
delete_user.delete()
Here is my model:
class AddUser(models.Model):
owner = models.ForeignKey(User)
title = models.CharField(max_length=20, default='', blank=True)
user_request = models.CharField(max_length=30, default='')
answer = models.BooleanField(default=False)
AddUser model is used for users to request access to a specific project. So when a user is requesting a project, data is saved to the AddUser model.
The user field is set to the "owner" of the project so I can filter every project with a proper owner.
If the owner decides to accept the request my user_accept view is executed. Here is the problem, when I try to add the username into the group, I get the following error:
invalid literal for int() with base 10: 'myusername'
I have tried to convert the username with int(username) and tried to get the username's user_id but nothing seems to work... I think it have something to do how I filter my objects but I don't get it.
So thanks a lot for your time, and have a happy new year!
You need the user id or the user object itself to add a user with group.user_set.add.
You can either retrieve the user object from the username, provided usernames are unique and add that:
user = User.objects.get(username=user_request)
group.user_set.add(user)
Or change your AddUser model to store the requester's id via a OneToOne field or ForeignKey instead of username.
Django default groups doesn't allow you to add username into it.
You have to add User objects.
Try
add_user_object = AddUser.objects.filter(owner=request.user).get()
group.user_set.add(add_user_object.owner)
Due to my app requeriments I need an hierachy of users classes like:
class CommonUser(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
photo = models.ImageField(upload_to="profileImages",blank=True,null=True)
#HERE: Common properties for every user
class Meta:
abstract = True
class Installer(CommonUser):
#HERE: Specific properties for Installer Companies
class Administration(CommonUser):
#HERE: Specific properties for Administration
class Client(CommonUser):
#HERE: Specific properties for Clients
Well, in view.py I need to get the profile image for an user. I get the user by the request, so I have an models.User objects and I dont know to witch class it belong. Im not able to do:
request.user.commonuser.photo
because user object doesnt have any OnetoOne relation with commonuser but with installer/administration/client...
Any idea? Thanks!
I'm on mobile, so I can't test this, but...
I would set a "related_name" on your CommonUser.user field called "commonuser". Then you can just use:
request.user.commonuser.photo
like you are already.
I think the issue is that you are referencing a Django User object to reference a backwards relationship without the proper name.
First off, I think this model is more of a Profile than User. If you don't mind using 1.9 (and postgres) then this is a perfect usecase for a JSON field. You can filter with regular lookups and don't need to specify each type. That way you can also extend the user model in such a way that a user can fulfill many roles at once. The other thing I thought of was linking it the other way around:
class UserProfile(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
# ...
class Installer(models.Model):
profile = models.ForeignKey(UserProfile, related_name='installer')
#HERE: Specific properties for Installer Companies
class Administration(models.Model):
profile = models.ForeignKey(UserProfile, related_name='admin')
#HERE: Specific properties for Administration
class Client(models.Model):
profile = models.ForeignKey(UserProfile, related_name='client')
#HERE: Specific properties for Clients