How to create custom permission for specific fields in model? - python

I have created a model and I want to create an user group that has the permission of edit only one field of this model.
For example if I have Car model with some fields (for example model, brand, registration) and one basic user, I want that the user is able to change only registration field.
It is possible to do that?

models.py:
class Model(models.Model):
class Meta:
# your custom permissions
permissions = (
('model_can_edit_title', _('Can Edit Title')),
)
do makemigration and migrate.
admin.py:
def get_readonly_fields(self, request, obj=None):
# you can check user type. and make decision
if 'app.model_can_edit_title' in request.user.user.get_all_permissions():
# user can update all fields
return []
# user can't update title field, title will be read only.
return ['title',]
you can use
def get_exclude(self, request, obj=None):
method also.

Related

Updating User details in UserDetail Table using One-To-One Relation Django Rest API

I am new to Django.
I am trying to update User details in another table using One-To-One relation by following Django document but is giving me an error. Creating a new user is working fine
Models.py
class UserDetails(models.Model):
user=models.OneToOneField(User,on_delete=models.CASCADE)
phone=models.CharField(max_length=10)
address=models.CharField(max_length=200,default="")
created=models.DateTimeField(auto_now_add=True)
updated=models.DateTimeField(auto_now_add=True)
objects=models.Manager()
def __str__(self):
return self.user.username
Serializer.py
class UserDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = UserDetails
fields= ['phone','address']
class CreateUserSerializer(serializers.ModelSerializer):
userdetails=UserDetailsSerializer()
class Meta:
model = User
fields=['id','url','username','email','password','userdetails']
def create(self,validated_data):
user_data=validated_data.pop('userdetails')
user=User.objects.create_user(**validated_data)
UserDetails.objects.create(user=user,**user_data)
user.save()
return user
def update(self,instance,validated_data):
user_data=validated_data.pop('userdetails')
user=User.objects.update(**validated_data)
UserDetails.objects.update(user=user,**user_data)
user.save()
return user
The error I get - (1062, "Duplicate entry 'admin' for key 'auth_user.username'") I know we can update it column by column but I want to update all columns at once as I have a lot of columns. I want a method similar to create.
One more question is that can I combine these create and update as they are doing the same thing?
As #Abdul Aziz Barkat has mentioned, the way you use your User and UserDetails model in update updates all the rows on those tables.
What you need to do is to filter first, and then apply the changes, so something like:
def update(self, instance, validated_data):
user_data = validated_data.pop('userdetails')
instance = super().update(instance, validated_data)
UserDetails.objects.filter(user=instance).update(**user_data)
return instance

How to create a model that summary from others model, and not save into database but show in admin page?

I have a problem: I need to create a table in an admin page that summary data from others model but it's not an actual real model in the database.
How to implement this?
I am assuming you are going to use existing models to calculate summery. Create instance methods/ properties in your model(as this will help to calculate your row level summery on models). For admin side you can use ModelAdmin with your Model (Probably a proxy model) and then blocki the actions.
Model instance method/property will used to calculate row level summery
class YourMainModel(models.Model):
# Your Model code
def instanceMethod(self)
return some_summery_calculation_based_on_model_fields
#property
def instanceProperty(self)
return some_summery_calculation_based_on_model_fields
class YourMainModelProxy(YourMainModel)
class Meta:
proxy=True
Now in admin.py
class YourSummeryModelAdmin(admin.ModelAdmin):
#Configure your model admin (Somewhat like this)
model = YourMainModelProxy
list_display = ['instanceMethod','instanceProperty'...]
# Disable add permission
def has_add_permission(self, request):
return False
# Disable delete permission
def has_delete_permission(self, request, obj=None):
return False
I hope this will be help.

Separate permissions for records in a table in Django Admin

Let's suppose I have different users in my Django project and a model such that each record belongs to a certain user. The users are supposed to have an access to Admin panel.
class MyModel(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
...
Is there a standard way in Django to make any user list, create and edit ONLY HIS instances of MyModel in Admin panel, hiding the ones created by other users?
You can override queryset for your model admin. like this:
class MyModelAdmin(admin.ModelAdmin):
def get_queryset(self, request):
qs = super().get_queryset(request)
qs = qs.filter(user=request.user)
return qs
This will filter out instances based on the user they belong to.

Indirect inline in Django admin

I have the following models:
class UserProfile(models.Model):
user = models.OneToOneField(User)
class Property(models.Model):
user = models.ForeignKey(User)
I would like to create a TabularInline displaying every Property connected to a particular UserProfile on its Django admin page. The problem here is, of course, that Property does not have a ForeignKey directly to UserProfile, so I cannot simply write
class PropertyTabularInline(admin.TabularInline):
model = Property
class UserProfileAdmin(admin.ModelAdmin):
inlines = (PropertyTabularInline,)
How can I easily do what I want?
You can overwrite the User admin page to display both the Profile and the Property models.
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from myapp.models import *
class ProfileInline(admin.TabularInline):
model = Profile
class PropertyInline(admin.TabularInline):
model = Property
class UserAdmin(UserAdmin):
inlines = (ProfileInline, PropertyInline,)
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
You can also remove any unwanted/unused User properties from being displayed (e.g. Groups or Permissions)
more here: https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#extending-the-existing-user-model
and here:
https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#a-full-example
class PropertyTabularInline(admin.TabularInline):
model = Property
def formfield_for_dbfield(self, field, **kwargs):
if field.name == 'user':
# implement your method to get userprofile object from request here.
user_profile = self.get_object(kwargs['request'], UserProfile)
kwargs["queryset"] = Property.objects.filter(user=user_profile)
return super(PropertyInLine, self).formfield_for_dbfield(field, **kwargs)
once this is done, you can add this inline to user UserProfileAdmin like:
class UserProfileAdmin(admin.ModelAdmin):
inlines = (PropertyTabularInline,)
Haven't tested it, but that should work.
It is achievable by making one change in your models.
Instead of creating OneToOne relationship from UserProfile to User, subclass User creating UserProfile. Code should look like that:
class UserProfile(User):
# some other fields, no relation to User model
class Property(models.Model):
user = models.ForeignKey(User)
That will result in creating UserProfile model that have hidden OneToOne relation to User model, it won't duplicate user model.
After doing that change, your code will work. There are some changes under the hood, like UserProfile no longer have it's own ID, you can access fields from User inside UserProfile and it's hard to swap User model using settings.AUTH_USER_MODEL (that will require creating some custom function returning proper type and changing migration by hand) but if this is not a problem for you, it may be good solution.

Basic Django REST framework - Write only resource

I am trying to create a simple service which allows anonymous users to submit their name and email. I want to AllowAny on adding their info, and IsAuthenticated on everything else. I'm having trouble getting this granularity.
models.py
from django.db import models
class Invitee(models.Model):
name = models.CharField(max_length=255)
email = models.EmailField(max_length=70,blank=True)
modified = models.DateTimeField(auto_now=True)
serializers.py
class InviteeSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Invitee
fields = ('name', 'email')
def create(self, validated_data):
return Invitee(**validated_data)
views.py
class InviteeViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows groups to be viewed or edited.
"""
queryset = Invitee.objects.all()
serializer_class = InviteeSerializer
What and where should I put to make it so users can submit their name and email, but only admins can read, update, delete? Thanks any help.
The easiest and safest way to do this is with multiple serializers, one of each user class you need. You will also need to use custom permissions to enforce the read/write difference between authenticated and anonymous users.
class InviteeSerializer(serializers.HyperlinkedModelSerializer):
def create(self, validated_data):
return Invitee(**validated_data)
class LimitedInviteeSerializer(InviteeSerializer):
class Meta:
model = Invitee
fields = ('name', 'email', ) # a limited subset of the fields
class FullInviteeSerializer(InviteeSerializer):
class Meta:
model = Invitee
fields = ('name', 'email', "modified", ) # all of the fields
While right now it only looks like you need read/write, if you need full read/write/delete permissions I would recommend reading this Stack Overflow question.
You will also need to control what serializer is being used on the view level. This needs to be done using a combination of permissions and overridden view methods.
class InviteeViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows groups to be viewed or edited.
"""
queryset = Invitee.objects.all()
serializer_class = LimitedInviteeSerializer
def get_serializer_class(self):
if self.request.user.is_active:
return FullInviteeSerializer
return super(InviteeViewSet, self).get_serializer_class()
On the view you will need to override get_serializer_class to determine what serializer should be used based on if the user is active. Anonymous users should never be marked as active, so this is the best way to check while excluding deactivated accounts.
You will also need to create a custom permissions class that will do the opposite of the built-in IsAuthenticatedOrReadOnly permission. You are looking for authenticated users to do everything, and anonymous users to only be write-only. I've called this class IsAuthenticatedOrWriteOnly to match the other permission class.
class IsAuthenticatedOrWriteOnly(BasePermission):
"""
The request is authenticated as a user, or is a write-only request.
"""
def has_permission(self, request, view):
WRITE_METHODS = ["POST", ]
return (
request.method in WRITE_METHODS or
request.user and
request.user.is_authenticated()
)
You just need to add this your existing list of permission classes, or override it manually it on the view using permission_classes.

Categories

Resources