I don't know if this possible, but here is what I want to achieve.
There's three default permissions in django admin. add/change/delete.
What I want is something like a "view" permission, kinda like the "change" permission but without the ability to edit.
I have a Country model, when a client is created, it needs to select a country as it's foreign key.
But if I set this field to readonly, it will not be able to select the country as I want. So I have to give the user "change" permission so that the country will be available when user create the client info.
Give the "change" permission is fine, but it would be better if it has a "view" permission.
I've done a lot search and didn't find a "perfect" solution.
I think there must be a lot of people would want this permission as well.
So how to make this custom "view" permission and integrate with django admin smoothly.
I hope this will help you:
class Client(models.Model):
name = models.CharField(...)
readonlyfield = models.CharField(...)
country = models.ForeignKey(...)
class Meta:
permissions = (
("change_country", "Can change country"),
)
class ClientAdmin(admin.ModelAdmin):
fields = ['name', 'readonlyfield', 'country']
def get_readonly_fields(self, request, obj=None):
if obj and not request.user.has_perm('yourapp.change_country'):
return['readonlyfield', 'country']
else:
return['readonlyfield']
So, if the user doesn't have the change_country permission, he’ll set the country field once (at creation)
Related
Let's say I have 2 models as follows. As there is a many to many relation between the models, django created the required table (clientreport) and the table's permissions can be set using django admin's group permission's tab. (see screenshot)
class Report(models.Model):
is_visible = models.BooleanField(default=False)
clients = models.ManyToManyField(Client)
class Client(models.Model):
name = models.CharField(max_length=32)
On django admin I granted change permissions to a specific user group (e.g. group_a) on Report model. I did NOT grant any permissions on clientreport model.
My desired output is, a group_a user can change is_visible field of any Report instance but could not change/delete client X reports (or clientreport).
However even if there is no actual table reference between Report and Client models, a group_a user can still edit client-reports from django admin panel. (see screenshot)
Is this really intended? If so, how can I get my desired goal?
If you need to restrict access to certain fields by certain rights, you can use the ModelAdmin.get_readonly_fields method.
Usage example:
class ReportAdmin(admin.ModelAdmin):
...
def get_readonly_fields(self, request, obj = None):
if request.user.groups.filter(name=groupname).exists():
return ('field1', 'field2')
else:
return super().get_readonly_fields(request, obj)
I need to set permission as per registered user using Django and Python. I have done something but confused whether it is fulfilled my requirement or not. I am providing my code below.
class Control(models.Model):
"""docstring for Control"""
user_id = models.ForeignKey(User)
control_reactor = models.IntegerField(default=0)
find_reactor = models.IntegerField(default=0)
view_reactor = models.IntegerField(default=0)
class Meta:
"""docstring for Meta"""
permissions = (
("view_reactor", "can view reactor"),
("find_reactor", "can find reactor"),
("controll_reactor", "can controll reactor"),
)
I am access those permission using Django decorator function like #permission_required. Here I need as per user I will set the 3 permissions but confused that this model class is doing as per requirement.
The meta class defined is just a representation that your model will have only these permissions so it becomes manageable.
You can create permissions programmatically using Permission class from django. From django docs
User objects have two many-to-many fields: groups and user_permissions. User objects can access their related objects in the same way as any other Django model
Permission models stores all the permission which can be set to a specific user. All you have to do is add the create a permission and add it to user.
from django.contrib.auth.models import Permission, User
content_type = ContentType.objects.get_for_model(BlogPost)
permission = Permission.objects.get(
codename='change_blogpost',
content_type=content_type,
)
user.user_permissions.add(permission)
The related name is user_permissions so you can directly add the permission same as M2M relationships.
Now suppose you want to check permissions
user.has_perm('app_name.code_name') # generic example
user.has_perm('myapp.change_blogpost')
Now for decorator you can do same
#permission_required('app_name.code_name')
EDIT: Generally you grant permission when you create a user so the example mentioned above can be put in your views where you signup the user. If you don't want to grant permissions right away then you can make a separate view which grants permission to the user
def grant_permissions(request):
user = request.user
# then you put the code mentioned above
for your case you can either create permissions in you code using permissions model or use meta class.
Just migrate the database to create permissions defined in the meta class. After migrate don't forget to give permission to user using the code mentioned above using Permissions model Then you can use the permissions in the decorator.
#permission_required('view_reactor')
custom_permissions
permissions
I am building an API that should have the following kind of users
super_user - create/manage admins
admin - manage events(model) and event participants
participants - participate in events, invited to events by admins
Additional i want to have each type of user to have phone number field
I tried
class SuperUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
phone_number = models.CharField(max_length=20)
class Admin(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
phone_number = models.CharField(max_length=20)
class Participant(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
phone_number = models.CharField(max_length=20)
But gut is telling me its a wrong way to handle this. Can someone please help.
One possible solution is:
Have only one User Model with role field, which defines what user role is.
Create a User Group and add each group needed permissions.
Add User to User Group
Limit access using a Django REST Framework (later DRF) Permission Class.
Explanation:
Using only one user model is a more simple and flexible solution. You can query all users, or filtered by feature (like user role). Standart Django auth system expects one UserModel.
Read more about Django user groups. See "Django Permissions Docs #1" and "Django Groups Docs #2". Also useful is "User groups and permissions".
You need to create a group for each user role, and add needed permissions for each group. (Django has a default model permission, created automatically, look at the docs on the given links) or create the needed permission manually in the model definition.
Manually or using a script, add User to the needed group by defining his role when a user is created or manually by Django Admin interface.
Now everything should be ready for limited access by the user's role. You can easily limit access to the DRF View using a permission class. See more information in the "DRF Permission Docs".
Let's define our own:
from rest_framework.permissions import DjangoModelPermissions
# Using DjangoModelPermissions we can limit access by checking user permissions.
# Rights need only for CreateUpdateDelete actions.
class CUDModelPermissions(DjangoModelPermissions):
perms_map = {
'GET': [],
'OPTIONS': [],
'HEAD': ['%(app_label)s.read_%(model_name)s'],
'POST': ['%(app_label)s.add_%(model_name)s'],
'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}
# Or you can inherit from BasePermission class and define your own rule for access
from rest_framework.permissions import BasePermission
class AdminsPermissions(BasePermission):
allowed_user_roles = (User.SUPERVISOR, User.ADMINISTRATOR)
def has_permission(self, request, view):
is_allowed_user = request.user.role in self.allowed_user_roles
return is_allowed_user
# ----
# on views.py
from rest_framework import generics
from .mypermissions import CUDModelPermissions, AdminsPermissions
class MyViewWithPermissions(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [CUDModelPermissions, ]
queryset = SomeModel.objects.all()
serializer_class = MyModelSerializer
You can add additional permission class to combine access limitation.
So in Django any user has a flag is_superuser that corresponds to your 'superuser'. So just use that - e.g. User.objects.create(is_superuser=True).
For the rest you can simply use a field for a normal User model to differentiate between subroles of a normal user.
class User(AbstractBaseUser):
can_participate_event = models.Boolean(default=False)
can_create_event = models.Boolean(default=False)
Or
class User(AbstractBaseUser):
permissions = models.CharField(default='') # and populate with e.g. 'create_event,participate_event'
Still you will need to check all those fields in your view probably. The more you add to your application, the hairier this becomes so I would suggest using a 3rd party library like rest-framework-roles (I'm the author) or guardian.
Despite having read several things on this topic, i'm seeing myself stuck.
I have a Django Rest Framework Application. I have this model :
class MyModel(Model):
name = CharField(_('name'), max_length=50)
type = CharField(_('type'), max_length=50)
users_selected = ManyToManyField(User)
Which i'm trying to serialize. I don't want to expose the users_selected as is seeing as it has all the users of the application whom selected this specific model, but just be able to put a boolean saying if it's selected for the current user, which i achieved by doing :
class MySerializer(ModelSerializer):
is_selected = SerializerMethodField('user_select')
def user_select(self, obj):
request = self.context['request']
return obj.users_selected.filter(id=request.user.id).exists()
class Meta(object):
model = MyModel
exclude = ('users_selected',)
This is working fine, but now i want to do it the other way around, meaning i want my client to be able to send a request with a is_selected set to true or false and then modify my users_selected accordingly. The SerializerMethodField implies the fact that this is a read-only field.
How should I achieve this? I've tried to search by using the restore_object method, but was unable to achieve anything...
Thanks.
To me, it feels a little strange to have MyModel point to all of the Users who selected it. This would be simpler if you subclassed the User, and gave the custom User either a Foreign key or a ManyToManyField to the MyModels selected.
If you did it this way, a single User could access his own selections, and there would be no permissions problems. The only time someone would need to access Users directly from the MyModel object would be for administrative work, at which time it would be perfectly fine to expose all of the users in the list. You could even have a separate view, something like MyModelUsersSelected, which could look like:
class MyModelUsersSelected(generics.ListAPIView):
serializer_class = UserSerializer
permission_classes = (permissions.IsAdminUser,)
def get_queryset(self):
return MyModel.objects.get(id=self.kwargs['pk']).user_set.all()
This would give you all of the Users who pointed to the MyModel instance passed as a pk in the url. Since this view is separate from the MyModel and User views, permissions can be set to admin users only.
[Django 1.5.1]
I've set up django-profiles and django-registration for my small site in a way that involves a 'custom' registration backend such that during registration, a new user fills out their username, password, and profile fields in one go. But I'm now trying to allow users to log in with Facebook and trying to use django-facebook. As far as I understand, logging in through django-facebook's Facebook login will authenticate someone, but that won't create a user object nor a profile object.
So the idea I had in mind would be to add an extra profile field which holds a user's potential Facebook ID(which I believe is unique) if they want to use Facebook. Then force a check on every template if the user has a profile or not, and if not, to direct them to the 'create profile' page. Then whenever a user logs in through the Facebook login, it'll somehow link their session with the corresponding profile object which matches the Facebook ID (and consequently the user object corresponding to the profile object). I think I'd have to apply a filter and then create a 'signal', but I'm not too sure how to do that.
This sounds very convoluted though. How might I be able to get this accomplished the right way?
Here's how I suggest you do things. Do away with django-facebook and look into django-allauth. It will handle accounts (registration, logic, connecting social accounts).
Also, django-profiles has many issues with 1.5+ for me and I don't bother with it and instead create my own profiles app and UserProfile model with any additional fields that wouldn't be handled by django-allauth.
An example from one of my implementations
class UserProfile(models.Model):
user = models.OneToOneField(User)
default_address = models.OneToOneField(Address, blank=True, null=True)
default_tshirt_size = models.CharField(blank=True, null=True, choices=constants.tshirt_sizes, max_length=50)
default_shoe_size = models.CharField(blank=True, null=True, choices=constants.shoe_sizes, max_length=50)
Your user profile model should be pointing to the same User model allauth is using.
from allauth.utils import get_user_model
User = get_user_model()
Here's a neat method I use to create the User's profile automatically if it hasn't been already.
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0])
Then just create a sign-up form class with a save() method that takes user as an argument. Link to this class in your settings file.
ACCOUNT_SIGNUP_FORM_CLASS = 'yourproject.yourapp.forms.SignupForm'
See this post on for a somewhat relevant example, except of course with your implementation you'll probably want to get the UserProfile based on the user.id at that point, and save the additional values to the profile instance.
How to customize user profile when using django-allauth