I create a custom permission which authorizes GET, HEAD and OPTION for everyone and which authorizes NO other requests.
But my code doesn't work. I can make a POST request despite my permission ...
Anyone have a idea to solve my problem ?
My views.py:
class IsReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return False
class ViewPollViewSet(viewsets.ModelViewSet):
permission_classes = [
IsReadOnly,
]
serializer_class = ViewPollSerializer
queryset = ViewPoll.objects.all()
My serializers.py:
class ViewPollSerializer(serializers.ModelSerializer):
class Meta:
model = ViewPoll
fields = '__all__'
My models.py:
class ViewPoll(models.Model):
''' view poll '''
class Meta:
unique_together = ('poll', 'user')
poll = models.ForeignKey(Poll, on_delete=models.CASCADE, related_name="views", null=True)
user = models.ForeignKey(User,on_delete=models.CASCADE, related_name="views_poll", null=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.poll.question)[:30]
Call has_permission(...) method instead of has_object_permission(...) method
class IsReadOnly(permissions.BasePermission):
def has_permission(self, request, view):
if request.method in permissions.SAFE_METHODS:
return True
return False
Related
Here the class WriteByAdminOnlyPermission is not working perfectly. This if request.method == 'GET': working but remaining condition is not working. My target is, only the admin can change information and the other people just can see. How can I do it? And where I did do wrong? please give me a relevant solutionš„
Note: I used here custom User
class User(AbstractUser):
id = models.CharField(primary_key=True, max_length=10, default=uuid.uuid4, editable=False)
email = models.EmailField(max_length=50, unique=True, error_messages={"unique":"The email must be unique!"})
REQUIRES_FIELDS = ["email"]
objects = CustomeUserManager()
views.py:
class WriteByAdminOnlyPermission(BasePermission):
def has_permission(self, request, view):
user = request.user
if request.method == 'GET':
return True
if request.method in['POST' or 'PUT' or 'DELETE'] and user.is_superuser:
return True
return False
class ScenarioViewSet(ModelViewSet):
permission_classes=[WriteByAdminOnlyPermission]
serializer_class = ScenarioSerializer
queryset = Scenario.objects.all()
models.py:
class Scenario(models.Model):
id = models.CharField(primary_key=True, max_length=10, default=uuid.uuid4, editable=False)
Title = models.CharField(max_length=350, null=True, blank=False)
film_id = models.OneToOneField(Film, on_delete=models.CASCADE, related_name="ScenarioFilmID", null=True)
serializer.py:
class ScenarioSerializer(ModelSerializer):
class Meta:
model = Scenario
fields = "__all__"
urls.py:
router.register(r"scenario", views.ScenarioViewSet , basename="scenario")
Why you are using or operator in list, it can be simplified as ['POST', 'PUT', 'DELETE']so:
if request.method in ['POST', 'PUT', 'DELETE'] and user.is_superuser:
Edit
Try to use the IsAuthenticatedOrReadOnly class directly to allow unauthenticated users to perform GET requests and authenticated users to perform POST, PUT, and DELETE requests so:
from rest_framework.permissions import IsAuthenticatedOrReadOnly, BasePermission
class WriteByAdminOnlyPermission(BasePermission):
def has_permission(self, request, view):
user = request.user
if request.method in ['POST', 'PUT', 'DELETE'] and user.is_superuser:
return True
return IsAuthenticatedOrReadOnly().has_permission(request, view)
class ScenarioViewSet(ModelViewSet):
permission_classes = [WriteByAdminOnlyPermission]
serializer_class = ScenarioSerializer
queryset = Scenario.objects.all()
I have two permission class. IsAuthorGroup will check if the user belongs from the author group and IsOwnerOrReadOnly will restrict the user to performing post and delete if he is not the object's owner.
But the problem is anyone from IsAuthorGroup performing post and delete request event he isn't own of the object.
How to restrict anyone from IsAuthorGroup performing post and delete request if he isn't owner of the object?
here is my code:
class IsAuthorGroup(permissions.BasePermission):
def has_permission(self, request, view):
if request.user and request.user.groups.filter(name='AuthorGroup'):
return True
return False
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
# Write permissions are only allowed to the owner of the blog.
return obj.author == request.user or request.user.is_superuser
class BlogViewSet(viewsets.ModelViewSet):
queryset = Blog.objects.all()
serializer_class = BlogSerializer
pagination_class = BlogPagination
lookup_field = 'blog_slug'
permission_classes = [IsOwnerOrReadOnly & IsAuthorGroup]
my serializer.py
class BlogSerializer(serializers.ModelSerializer):
author_first_name = serializers.CharField(
source="author.first_name", required=False)
author_last_name = serializers.CharField(
source="author.last_name", required=False)
class Meta:
model = Blog
exclude = ("author", "blog_is_published")
lookup_field = 'blog_slug'
extra_kwargs = {
'url': {'lookup_field': 'blog_slug'}
}
I am creating an API for some polls. I need the author to be the only one who can view the votes, but the authenticated users to view the polls, questions, and to post votes. The author is just an instance of User, as like as the voters.
I'm using Djoser to provide the authentication API, model serializers, and breaking my mind between CBVs and viewsets.
Here are my models, if it can help.
from django.db import models
from django.contrib.auth import get_user_model
from django.utils import timezone
import datetime
User = get_user_model()
class Poll(models.Model):
title = models.CharField(max_length=200, verbose_name="Naslov ankete")
description = models.TextField(verbose_name="Opis ankete")
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="polls", verbose_name="Autor")
class Meta:
verbose_name = "Anketa"
verbose_name_plural = "Ankete"
def __str__(self):
return self.title
class Question(models.Model):
poll = models.ForeignKey(Poll, on_delete=models.CASCADE, related_name="questions", verbose_name="Anketa")
question_text = models.CharField(max_length=200, verbose_name="Tekst pitanja")
pub_date = models.DateTimeField(verbose_name="Datum objavljivanja")
class Meta:
ordering = ["pub_date", "question_text"]
verbose_name = "Pitanje"
verbose_name_plural = "Pitanja"
def __str__(self):
return self.question_text
def was_published_recently(self):
now = timezone.now()
return now - datetime.timedelta(days=1) <= self.pub_date <= now
def verbose_question_text(self):
return f"Pitanje: {self.question_text}"
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name="choices", verbose_name="Pitanje")
choice_text = models.CharField(max_length=200, verbose_name="Tekst opcije")
# votes = models.IntegerField(default=0, verbose_name="Glasovi")
class Meta:
ordering = ["-votes", "pk"]
verbose_name = "Opcija"
verbose_name_plural = "Opcije"
def __str__(self):
return self.choice_text
class Vote(models.Model):
choice = models.ForeignKey(Choice, on_delete=models.CASCADE, related_name="votes", verbose_name="Opcija")
def __str__(self):
return self.choice.choice_text
P. S.: If you think the voting could be solved in a better way, please suggest it too.
I think you can do it by creating a custom permission (extended from IsAuthenticated class) and override method has_object_permission, see documentation.
from rest_framework.permissions import IsAuthenticated
class AuthorFullAccessUserPostOnly(IsAuthenticated):
def has_object_permission(self, request, view, obj):
if request.method == "POST":
return True
# This part better to implement with query
# Also, it will work only for Vote object.
# You need to extend this method if you are going to use for another objects
return request.user == obj.choice.question.poll.author
You can create a Permission class for each view (or viewset), or extend this one, respecting an object type
If you are wanting to use different conditions for different request methods you can use some kind of this logic
class PoolPermissions(IsAuthenticated):
def has_object_permission(self, request, view, obj):
if request.method == "POST":
self.has_post_object_permission(request, view, obj)
elif request.method == "GET":
self.has_get_object_permission(request, view, obj)
elif request.method == "PUT":
self.has_put_object_permission(request, view, obj)
# etc.
...
def has_get_object_permission(self, request, view, obj):
# your logic
pass
def has_post_object_permission(self, request, view, obj):
# your logic
pass
def has_put_object_permission(self, request, view, obj):
# your logic
pass
I'm trying to make a rest API for a blog app using Django-rest-framework. I'm a beginner and it's hard for me to understand how to implement that system. I made an intermediary model for making connections between followers and followings and serializers for users. But my API showing absolutely wrong following and followers for each user and endpoints for following and unfollowing not working also. https://github.com/romon267/blog_api - my code
Models.py:
class UserFollowing(models.Model):
class Meta:
constraints= [
models.UniqueConstraint(fields=['user_id', 'following_user_id'], name='unique_following')
]
ordering = ['-created']
user_id = models.ForeignKey('auth.User', related_name='following', on_delete=models.SET_NULL, null=True,blank=True)
following_user_id = models.ForeignKey('auth.User', related_name='followers', on_delete=models.SET_NULL, null=True,blank=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f'{self.user_id} is following {self.following_user_id}'
serializers.py:
class UserSerializer(serializers.HyperlinkedModelSerializer):
following = serializers.HyperlinkedRelatedField(many=True, view_name='user-detail', read_only=True)
followers = serializers.HyperlinkedRelatedField(many=True, view_name='user-detail', read_only=True)
posts = serializers.HyperlinkedRelatedField(many=True, view_name='post-detail', read_only=True)
class Meta:
model = User
fields = ['url', 'id', 'username', 'posts', 'following', 'followers']
def get_following(self, obj):
return FollowingSerializer(obj.following.all(), many=True).data
def get_followers(self, obj):
return FollowersSerializer(obj.followers.all(), many=True).data
class UserFollowingSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = UserFollowing
fields = '__all__'
class FollowingSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = UserFollowing
fields = ['id', 'following_user_id', 'created']
class FollowersSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = UserFollowing
fields = ['id', 'user_id', 'created']
views.py:
class UserViewSet(viewsets.ReadOnlyModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
class UserFollowingViewSet(viewsets.ModelViewSet):
queryset = UserFollowing.objects.all()
serializer_class = UserFollowingSerializer
class UserFollow(APIView):
"""
Retrieve, update or delete a snippet instance.
"""
def get_object(self, pk):
try:
return User.objects.get(pk=pk)
except User.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
user = self.get_object(pk)
serializer = UserSerializer(user)
return Response(serializer.data)
def post(self, request, pk, format=None):
user = request.user
follow = self.get_object(pk)
UserFollowing.objects.create(user_id=user.id, following_user_id = follow.id)
serializer = UserSerializer(follow)
return Response(serializer.data)
def delete(self, request, pk, format=None):
user = request.user
follow = self.get_object(pk)
UserFollowing.objects.delete(user_id=user.id, following_user_id = follow.id)
serializer = UserSerializer(follow)
return Response(serializer.data)
How do I access a current logged in user from a class-based view?
In a function-based view we can pass a request parameter but I can't pass a request parameter from a class view.
I have seen ways to do it in the internet but I can't understand it.
my models.py file
class Category(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return (self.name)
def get_absolute_url(self):
return reverse("home")
class Post(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey(User, on_delete=models.CASCADE)
body = models.TextField(max_length=3500)
category = models.CharField(max_length=255, default="uncategorized")
views.py
class HomeView(ListView, LoginRequiredMixin):
model = Post
template_name = "home.html"
Thank you.
You can use self.request.user inside methods of class-based views; as an example:
class HomeView(ListView, LoginRequiredMixin):
model = Post
template_name = "home.html"
def get_context_data(self):
current_loggedin_user = self.request.user
# ...
Edit (just print the username):
class HomeView(ListView, LoginRequiredMixin):
model = Post
template_name = "home.html"
def __init__(self, *args, **kwargs):
print(self.request.user, self.request.user.username) # print user & username
return super().__init__(*args, **kwargs)