How can I make only superusers to edit model - python

I have 2 models: Recipe and Ingridient and I want that only superusers will be able to edit model Ingridient, normal users can only view ingridients and are not able to edit them. Right now is set that way that if you are logged in you have all permissions, but I want that only superusers have ability to edit Ingridients and normal logged in users are able to add recipes (not able to edit ingridients).
#views.py
class ReceptViewRetrieveUpdateDestroy(generics.RetrieveUpdateDestroyAPIView):
queryset = Recipe.objects.all()
lookup_field = 'id'
serializer_class = RecipeSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def delete(self, request, *args, **kwargs):
try:
id = request.data.get('id', None)
response= super().delete(request, *args, **kwargs)
if response.status_code == 204:
from django.core.cache import cache
cache.delete("{}".format(id))
return response
except Exception as e:
return response({
"Message":"Failed"
})
def update(self, request, *args, **kwargs):
response = super().update(request, *args, **kwargs)
if response.status_code == 200:
mydata = response.data
from django.core.cache import cache
cache.set("ID :{}".format(mydata.get('id', None)),{
'title':mydata["title"],
'description':mydata["description"],
'image':mydata["image"],
'galery':mydata["galery"],
'kitchen_type':mydata["kitchen_type"],
'num_persons':mydata["num_persons"],
'preparation_steps':mydata["preparation_steps"]
})
return response
class SestavinaViewRetrieveUpdateDestroy(generics.RetrieveUpdateDestroyAPIView):
queryset = Ingridient.objects.all()
lookup_field = 'id'
serializer_class = IngridientSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def delete(self, request, *args, **kwargs):
try:
id = request.data.get('id', None)
response= super().delete(request, *args, **kwargs)
if response.status_code == 204:
from django.core.cache import cache
cache.delete("{}".format(id))
return response
except Exception as e:
return response({
"Message":"Failed"
})
def update(self, request, *args, **kwargs):
response = super().update(request, *args, **kwargs)
if response.status_code == 200:
mydata = response.data
from django.core.cache import cache
cache.set("ID :{}".format(mydata.get('id', None)),{
'name':mydata["name"],
'quantity':mydata["quantity"],
'calories':mydata["calories"],
'protein':mydata["protein"],
'carbon':mydata["carbon"],
'fiber':mydata["fiber"],
'fat':mydata["fat"],
'saturated_fat':mydata["saturated_fat"]
})
return response
class RecipeListView(generics.ListCreateAPIView):
model = Recipe
serializer_class = RecipeSerializer
queryset = Recipe.objects.all()
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
class IngridientsListView(generics.ListCreateAPIView):
model = Ingridient
serializer_class = IngridientSerializer
queryset = Ingridient.objects.all()
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
#models.py
class Ingridient(models.Model):
name = models.CharField(default='', max_length=20)
quantity = models.IntegerField(default=0)
calories = models.IntegerField(default=0)
protein = models.IntegerField(default=0)
carbon = models.IntegerField(default=0)
fiber = models.IntegerField(default=0)
fat = models.IntegerField(default=0)
saturated_fat = models.IntegerField(default=0)
def __str__(self):
return self.name
class Recipe(models.Model):
title = models.CharField(default='', max_length=60)
description = models.TextField(default='',max_length=1000)
image = models.ImageField(default='',upload_to='pics', blank=True)
galery = models.ImageField(default='', blank=True)
kitchen_type = models.CharField(default='',max_length=35)
num_persons = models.IntegerField(default=0)
preparation_steps = models.TextField(default='')
Ingridients = models.ManyToManyField(Ingridient)
def __str__(self):
return self.title
#serializers.py
class IngridientSerializer(serializers.ModelSerializer):
class Meta:
model = Ingridient
fields = ('name', 'quantity', 'calories', 'protein', 'carbon', 'fiber', 'fat',
'saturated_fat')
class RecipeSerializer(serializers.ModelSerializer):
Ingridients = IngridientSerializer(many=True)
class Meta:
model = Recipe
fields = ('title', 'description', 'image', 'galery', 'kitchen_type', 'num_persons',
'preparation_steps', 'Ingridients')

If you're willing to set up Django model permissions for your Ingredient model instead (i.e. add an "Ingredient Editors" group, and add all superusers to that group, then allow those users to edit Ingredients), you can use the DjangoModelPermissions permissions class instead of IsAuthenticatedOrReadOnly.
If you really do simply want to give all superusers write permissions, you can adapt DRF's IsAuthenticatedOrReadOnly permission class
to something like
class IsAdminOrReadOnly(BasePermission):
def has_permission(self, request, view):
return bool(
request.method in SAFE_METHODS or
request.user and
request.user.is_superuser
)
and then use that permission class instead.

Related

How to validate fields with foreign keys in serializers context?

I'm working on some validations, and I want to check whether the requesterid entered is referencing to a userroleid value of "3" from userTable model. If it doesn't meet the criteria, it will raise a validation error. How can I access a field from a referenced table?
What I have tried is something like request = self.context.get("requesterid__userroleid") but it doesn't validate correctly. Do you have any idea on how to do this?
Here's my serializers:
class RequestCreateSerializer(serializers.ModelSerializer):
parts=PartSerializer(many=True)
class Meta: model = requestTable
fields = ['rid', 'requesterid', (...)]
def create(self, validated_data):
(...)
def validate(self, data):
request = self.context.get("requesterid__userroleid") #how to access userroleid? if request == 3:
return data
raise serializers.ValidationError("Sorry! Your role has no permission to create a request.")
Here's my views:
class RequestListCreateView(ListCreateAPIView):
queryset = requestTable.objects.all()
serializer_class = RequestCreateSerializer
def create(self, request, *args, **kwargs):
(...)
def get_serializer(self, *args, **kwargs):
serializer_class = self.get_serializer_class()
kwargs['context'] = self.get_serializer_context()
return serializer_class(*args, **kwargs)
def get_serializer_context(self):
return {
'request': self.request,
'format': self.format_kwarg,
'view': self
}
Here's my models:
class requestTable(models.Model):
rid = models.AutoField(primary_key=True)
requesterid = models.ForeignKey(userTable, on_delete=models.CASCADE)
(...)
class userTable(models.Model):
userid = models.UUIDField(primary_key=True, default = uuid.uuid4, editable=False) userroleid = models.ForeignKey(roleTable, on_delete=models.CASCADE)
class roleTable(models.Model):
roleid = models.IntegerField(null=False, primary_key=True)
role = models.CharField(max_length=255, null=False)
Solved it using this code:
def validate(self, data):
if data['requesterid'].userroleid_id != 3:
raise serializers.ValidationError("Sorry! You entered a user with no requester permissions.")
return data

How to override the update action in django rest framework ModelViewSet?

These are the demo models
class Author(models.Model):
name = models.CharField(max_lenght=5)
class Post(models.Model):
author = models.ForeignKey(Author, on_delete=models.CASCADE)
title = models.CharField(max_lenght=50)
body = models.TextField()
And the respective views are
class AuthorViewSet(viewsets.ModelViewSet):
queryset = Author.objects.all()
serializer_class = AuthorSerializer
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostStatSerializer
I am trying to perform an update/put action on PostViewSet and which is succesfull, but I am expecting different output. After successful update of Post record, I want to send its Author record as output with AuthorSerializer. How to override this and add this functionality?
You can override update method for this:
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostStatSerializer
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False)
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
# this will return autor's data as a response
return Response(AuthorSerializer(instance.parent).data)
I figured out some less code fix for my issue.
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostStatSerializer
def update(self, request, *args, **kwargs):
super().update(request, *args, **kwargs)
instance = self.get_object()
return Response(AuthorSerializer(instance.author).data)

Filtering by Foreign Key in ViewSet, django-rest-framework

I want my api to return certain objects from a database based on the foreign key retrieved from the url path. If my url looks like api/get-club-players/1 I want every player object with matching club id (in this case club.id == 1). I'm pasting my code down below:
models.py
class Club(models.Model):
name = models.CharField(max_length=25)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True)
def __str__(self):
return self.name
class Player(models.Model):
name = models.CharField(max_length=30)
club = models.ForeignKey(Club, on_delete=models.SET_NULL, blank=True, null=True)
def __str__(self):
return self.name
serialziers.py
class ClubSerializer(serializers.ModelSerializer):
class Meta:
model = Club
fields = 'id', 'owner', 'name'
class PlayerSerializer(serializers.ModelSerializer):
class Meta:
model = Player
fields = 'id', 'name', 'offense', 'defence', 'club', 'position'
views.py, This is the part where I get the most trouble with:
class ClubViewSet(viewsets.ModelViewSet):
queryset = Club.objects.all()
serializer_class = ClubSerializer
class PlayerViewSet(viewsets.ModelViewSet):
queryset = Player.objects.all()
serializer_class = PlayerSerializer
class GetClubPlayersViewSet(viewsets.ViewSet):
def list(self, request):
queryset = Player.objects.all()
serializer = PlayerSerializer(queryset, many=True)
def retrieve(self,request, clubId):
players = Player.objects.filter(club=clubId, many=True)
if not players:
return JsonResponse({'error': "No query found!"})
else:
serializer = PlayerSerializer(players)
return Response(serializer.data)
urls.py
from rest_framework import routers
from django.urls import path, include
from .views import (GameViewSet, PlayerViewSet, ClubViewSet,
GetClubPlayersViewSet, create_club, set_roster)
router = routers.DefaultRouter()
router.register(r'clubs', ClubViewSet, basename="clubs")
router.register(r'players', PlayerViewSet, basename="players")
router.register(r'get-club-players', GetClubPlayersViewSet, basename="club-players")
urlpatterns = [
path('', include(router.urls)),
]
EDIT:
Now views.py looks like that:
class GetClubPlayersViewSet(viewsets.ViewSet):
queryset = Player.objects.all()
def list(self, request):
serializer = PlayerSerializer(self.queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, *args, **kwargs):
clubId = kwargs['get-club-players']
players = Player.objects.filter(club=clubId, many=True)
if not players:
return JsonResponse({'error': "No query found!"})
else:
serializer = PlayerSerializer(players)
return Response(serializer.data)
http://127.0.0.1:8000/api/get-club-players/ returns all of the player objects, but when I ad a clubId into url I get this error:
EDIT 2:
class GetClubPlayersViewSet(viewsets.ViewSet):
queryset = Player.objects.all()
def retrieve(self, request, *args, **kwargs):
queryParams = self.request.GET.get('abc')
if queryParams is None:
queryset = Player.objects.none()
else:
queryset = Player.objects.filter(club = queryParams)
serializer = PlayerSerializer(queryset)
return Response(serializer.data)
def list(self, request):
serializer = PlayerSerializer(self.queryset, many=True)
return Response(serializer.data)
You can get url parameters using kwargs attribute. You will need to modify the signature of your retrieve method for it.
def retrieve(self, request, *args, **kwargs):
clubId = kwargs['get-club-players']
players = Player.objects.filter(club=clubId, many=True)
....
EDIT
For the queryset error, it is due to DRF requiring either the queryset class attribute or implementation of get_queryset() function. In your case, you can get around it like this:
class GetClubPlayersViewSet(viewsets.ViewSet):
queryset = Player.objects.all()
def list(self, request):
serializer = PlayerSerializer(self.queryset, many=True)
So you can define your queryset like -
def get_queryset(self):
queryParams == self.request.GET.get('abc') # get queryparameter from url
if queryParams is None:
#queryset = anyModel.objects.all()
queryset = anyModel.objects.none()
else:
queryset = anyModel.objects.filter(anyProperty = queryParams)
return queryset
and your url will be like -
api/get-club-players/?abc=1
this abc can be id or any other property from the model.
Use this get_queryset logic in your retrieve method.
rest_framework.viewsets.ViewSet has an attribute named lookup_field which you can override. By default the value of lookup_field is id.
When adding the viewset in router, the lookup_field is added as the argument name in the url (e.g. /api/get-club-players/:id/).
You can either override the lookup_field of GetClubPlayersViewSet or access the correct kwargs key by changing
clubId = kwargs['get-club-players'] to clubId = kwargs['id']
Or a bit of both:
class GetClubPlayersViewSet(viewsets.ViewSet):
lookup_field = "clubId"
queryset = Player.objects.all()
# ....
def retrieve(self, request, *args, **kwargs):
clubId = kwargs[self.lookup_field]
players = Player.objects.filter(club=clubId, many=True)
if not players:
return JsonResponse({'error': "No query found!"})
else:
serializer = PlayerSerializer(players)
return Response(serializer.data)

I want to restrict a particular user from creating a saved class object if the current user id is different from the id in the live class object

My models.py
from django.db import models
from django.contrib.auth.models import User
import datetime
from django.utils import timezone
# Create your models here.
class LiveClass(models.Model):
standard = models.IntegerField()
no_of_students_registered = models.IntegerField(default=0)
class Meta:
verbose_name_plural = 'Class'
def __str__(self):
return str(self.standard) + ' class'
class User_details(models.Model):
name = models.OneToOneField(User, on_delete = models.CASCADE, max_length=30)
standard = models.IntegerField(default=0)
email = models.EmailField()
mobile_number = models.IntegerField()
class Meta:
verbose_name_plural = 'User_details'
def __str__(self):
return str(self.name)
class Mentor(models.Model):
name = models.CharField(max_length=30)
details = models.TextField()
ratings = models.FloatField(default=2.5)
class Meta:
verbose_name_plural = 'Mentors'
def __str__(self):
return self.name
class LiveClass_details(models.Model):
standard = models.ForeignKey(LiveClass, on_delete=models.CASCADE)
chapter_name = models.CharField(max_length=30)
chapter_details = models.TextField()
mentor_name = models.ForeignKey(Mentor, max_length=30, on_delete=models.CASCADE)
class_time = models.DateTimeField()
end_time = models.DateTimeField(default=timezone.now())
isDoubtClass = models.BooleanField(default=False)
doubtsAddressed = models.IntegerField(default=0)
class Meta:
verbose_name_plural = 'LiveClass_details'
def __str__(self):
return self.chapter_name
class SavedClass(models.Model):
class_details = models.ForeignKey(LiveClass_details, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
is_registered = models.BooleanField(default=False)
is_attended = models.BooleanField(default=False)
class Meta:
verbose_name_plural = 'SavedClasses'
def __str__(self):
return 'SavedClass : ' + str(self.class_details)
my views.py
from django.shortcuts import render
from rest_framework import mixins
from rest_framework import generics
from django.contrib.auth.mixins import LoginRequiredMixin
from rest_framework import status
from django.contrib.auth.models import User
from rest_framework.response import Response
from django.contrib.auth import authenticate
from . import serializers
from . import models
# Create your views here.
class ListLiveClass(mixins.ListModelMixin, LoginRequiredMixin, generics.GenericAPIView):
queryset = models.LiveClass_details.objects.all()
serializer_class = serializers.LiveClass_details_serializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
class LiveClassView(mixins.ListModelMixin,
mixins.CreateModelMixin,
LoginRequiredMixin,
generics.GenericAPIView):
queryset = models.LiveClass_details.objects.all()
serializer_class = serializers.LiveClass_details_serializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
if request.user.is_superuser:
return self.create(request, *args, **kwargs)
else:
return Response(status=status.HTTP_403_FORBIDDEN)
class LiveClassViewId(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
LoginRequiredMixin,
generics.GenericAPIView):
queryset = models.LiveClass_details.objects.all()
serializer_class = serializers.LiveClass_details_serializer
lookup_field = 'id'
def get(self, request, id=None, format=None):
if id:
return self.retrieve(request)
else:
return Response(status=status.HTTP_400_BAD_REQUEST)
def put(self, request, id, format=None):
if request.user.is_superuser:
return self.update(request, id)
else:
return Response(status=status.HTTP_403_FORBIDDEN)
def delete(self, request, id, format=None):
if request.user.is_superuser:
return self.destroy(request, id)
else:
return Response(status=status.HTTP_403_FORBIDDEN)
class ListMentors(mixins.ListModelMixin, LoginRequiredMixin, generics.GenericAPIView):
queryset = models.Mentor.objects.all()
serializer_class = serializers.Mentor_serializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
class ListUserDetails(mixins.ListModelMixin, LoginRequiredMixin, generics.GenericAPIView):
queryset = models.User_details.objects.all()
serializer_class = serializers.User_details_serializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
#api endpoints to save and register live classes
class SavedClassView(LoginRequiredMixin, mixins.ListModelMixin, mixins.CreateModelMixin, mixins.DestroyModelMixin, generics.GenericAPIView):
serializer_class = serializers.SavedClass_serializer
def get_queryset(self):
user = self.request.user
return models.SavedClass.objects.filter(user=self.request.user.id)
def get(self, request):
return self.list(request)
def post(self, request):
cur_user = self.get_object()
#return self.create(request)
return Response(status=status.HTTP_403_FORBIDDEN)
my serializers.py
from rest_framework import serializers
from . import models
class LiveClass_serializer(serializers.ModelSerializer):
class Meta:
model = models.LiveClass
fields = '__all__'
class SavedClass_serializer(serializers.ModelSerializer):
class Meta:
model = models.SavedClass
fields = '__all__'
class User_details_serializer(serializers.ModelSerializer):
saved_class = SavedClass_serializer()
class Meta:
model = models.User_details
fields = '__all__'
class LiveClass_details_serializer(serializers.ModelSerializer):
class Meta:
model = models.LiveClass_details
fields = '__all__'
class Mentor_serializer(serializers.ModelSerializer):
class Meta:
model = models.Mentor
fields = '__all__'
In savedClass view in GET request i am rendering all the savedClass Model wherever user in the model matches with the current user , now in POST request i want to create the savedclass row only for the current user and forbids the user from creating a view with different user id
I also needs help in my SavedClass model where whether a user is registered or not can only be decided by the admin
I tries different possible things but not able to do it, i am new to Django so needs help
You can get the logged in user from request.user so something like this should work
def post(self, request):
cur_user = request.user
if cur_user.id == request.data.user:
return self.create(request)
return Response(status=status.HTTP_403_FORBIDDEN)
assuming that only logged in users can call this api

DRF, "required": "This field is required."

I can't get a valid response, always get this error
{"required": "This field is required.", "null": "This field may not be null.", "not_a_list": "Expected a list of items but got type \"{input_type}\".", "empty": "This list may not be empty."}
Here is some code (models, serializers, views)
models
class Task(models.Model):
name = models.CharField(max_length=50)
description = models.CharField(max_length=500, blank=True, default='')
pub_datetime = models.DateTimeField()
priority = models.CharField(choices=PRIORITY_CHOICES, max_length=1)
status = models.BooleanField(default=False)
owner = models.ForeignKey('auth.User', on_delete=models.CASCADE)
agenda = models.ForeignKey(Agenda, on_delete=models.CASCADE, blank=True, default=None, null=True)
def __str__(self):
return self.name
serializers
class TaskSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
agenda = serializers.ReadOnlyField(source='agenda.name')
class Meta:
model = Task
fields = ('id', 'name', 'description', 'pub_datetime', 'priority', 'status', 'owner', 'agenda')
views
class TaskListView(generics.ListCreateAPIView):
serializer_class = TaskSerializer
permission_classes = (permissions.IsAuthenticated,)
renderer_classes = [JSONRenderer, TemplateHTMLRenderer]
template_name = 'task_list.html'
def get(self, request, *args, **kwargs):
queryset = Task.objects.filter(owner=self.request.user)
if self.request.accepted_renderer.format == 'html':
return Response({'tasks': queryset})
else:
serializer = TaskSerializer(data=queryset, many=True)
if serializer.is_valid():
return JsonResponse(serializer.data, safe=False)
else:
return JsonResponse(serializer.error_messages)
def post(self, request, *args, **kwargs):
name = request.data['name']
description = request.data['description']
priority = request.data['priority']
new_task = Task.objects.create(name=name, description=description, pub_datetime=datetime.datetime.now(),
priority=priority, status=False, owner=self.request.user)
new_task.save()
return redirect('task-list')
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
I'm not sure, at what place I'm wrong. Can someone help?
just text to avoid errors
The queryset or object instance should be passed to serializer as instance, not data, so:
queryset = Task.objects.filter(owner=self.request.user)
serializer = TaskSerializer(instance=queryset, many=True)
Also, I believe that serializer can't validate instance, so in this case you should be able to just return return JsonResponse(serializer.data)

Categories