IsOwnerOrReadOnly permission - python

I'm trying to do crud API with IsOwnerOrReadOnly permission but other users can still delete posts from the others. Could you please have a look? I have no clue what goes wrong. My guess is that it has something to do with this line
return obj.author == request.user.userprofile
// permissions.py
from rest_framework import permissions
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self,request,view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.author == request.user.userprofile
// views.py
class PostDetail(APIView):
permission_classes = [
permissions.IsAuthenticatedOrReadOnly,
IsOwnerOrReadOnly,
]
def get_obj(self, pk):
try:
return Post.objects.get(id=pk)
except Post.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
post = self.get_obj(pk)
serializer = PostSerializer(post)
return Response(serializer.data)
def put(self, request, pk, format=None):
post = self.get_obj(pk)
serializer = PostSerializer(post, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk, format=None):
post = self.get_obj(pk)
post.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
// models.py
class Post(models.Model):
id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False)
author = models.ForeignKey(Profile,on_delete=models.CASCADE)
title = models.CharField(max_length=200, blank=True, null=True)
content = models.TextField(blank=True, null=True)
tags = models.ManyToManyField('Tag',blank=True)
answers = models.IntegerField(default=0)
last_modified = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
class Profile(models.Model):
id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False)
user = models.OneToOneField(User, related_name='userprofile', on_delete=models.CASCADE)
name = models.CharField(max_length=200, blank=True, null=True)
email = models.EmailField(max_length=200, blank=True, null=True)
location = models.CharField(max_length=200, blank=True, null=True)
intro = models.CharField(max_length=200, blank=True, null=True)
bio = models.TextField(blank=True, null=True)
profile_image = models.ImageField(default=None, upload_to='profile/', blank=True, null=True)
github = models.CharField(max_length=200, blank=True, null=True)
linkedin = models.CharField(max_length=200, blank=True, null=True)
created = models.DateTimeField(default=timezone.now)
def __str__(self):
return str(self.user.username)

I think the object permission is never called, Try to explicitly call the APIVIew check_object_permissions in the get_obj method.
def get_obj(self, pk):
obj = get_object_or_404(Post, pk=pk)
self.check_object_permissions(self.request, obj)
return obj
https://www.django-rest-framework.org/api-guide/permissions/#object-level-permissions

Related

Upload Image album with multiple images in Django Rest Framework

I have the following Models
def get_upload_path(instance, filename):
model = instance._meta
name = model.verbose_name_plural.replace(' ', '_')
return f'{name}/images/{filename}'
class ImageAlbum(models.Model):
def default(self):
return self.images.filter(default=True).first()
def thumbnails(self):
return self.images.filter(width__lt=100, length_lt=100)
class Image(models.Model):
name = models.CharField(max_length=255, null=True, blank=True)
image = models.ImageField(upload_to=get_upload_path, null=True, blank=True)
default = models.BooleanField(default=False, null=True, blank=True)
width = models.FloatField(default=100, null=True, blank=True)
length = models.FloatField(default=100, null=True, blank=True)
description = models.CharField(max_length=2000, null=True, blank=True)
latitude = models.DecimalField(max_digits=11, decimal_places=2, null=True, blank=True)
longitude = models.DecimalField(max_digits=11, decimal_places=2, null=True, blank=True)
album = models.ForeignKey(ImageAlbum, related_name='images', on_delete=models.CASCADE, null=True, blank=True)
Following are my serializers
class ImageSerializer(serializers.ModelSerializer):
class Meta:
model = models.Image
fields = '__all__'
class ImageAlbumSerializer(serializers.ModelSerializer):
images = ImageSerializer(many=True)
class Meta:
model = models.ImageAlbum
fields = ['images']
def create(self, validated_data):
image_data = validated_data.pop('images')
album = models.ImageAlbum.objects.create()
for image_data in image_data:
models.Image.objects.create(album=album, **image_data)
return album
Following is my view.py
class ImageAlbumListApiView(APIView):
permission_classes = [IsAuthenticated]
parser_classes = [MultiPartParser, FormParser, ]
def get(self, request):
image_album = ImageAlbum.objects.all()
serializer = ImageAlbumSerializer(image_album, many=True)
return Response(serializer.data)
def post(self, request):
serializer = ImageAlbumSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(serializer.errors)
The following in the error received when i am trying to post data using Postman.
As i am relatively new to Django Rest Framework, i am unable to figure this out. Please help!!

Getting error during adding a comment as notnull

I don't understand why I am getting this one I tried making null=True, blank=True but still, it does not solve my error, at last, I thought of sending it here ...!! please tell me where I am going wrong
Models.py
class Certification(models.Model):
id = models.UUIDField(default=uuid.uuid4,
unique=True, primary_key=True, editable=False)
course_name = models.CharField(max_length=255)
course_image = models.ImageField()
course_taken = models.CharField(max_length=255)
mentor_name = models.CharField(max_length=20, blank=True, null=True)
slug = models.SlugField(unique=True, null=True)
#body = RichTextField()
body = RichTextUploadingField()
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.course_name
class Comment(models.Model):
certificate = models.ForeignKey(Certification, on_delete=models.CASCADE)
name = models.CharField(max_length=200)
body = models.TextField(null=True, blank=True)
forms.py
class CommentForm(ModelForm):
class Meta:
model = Comment
fields = '__all__'
exclude = ['certificate']
def __init__(self, *args, **kwargs):
super(CommentForm, self).__init__(*args, **kwargs)
self.fields['name'].widget.attrs.update(
{'class': 'form-control form-control-sm',})
self.fields['body'].widget.attrs.update(
{'class': 'form-control form-control-sm',})
Views.py
def certificatePage(request,pk):
certi = Certification.objects.get(id=pk)
count = certi.comment_set.count()
comments = certi.comment_set.all()
form = CommentForm()
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.certi = certi
comment.save()
context = {
"certi": certi,
'count':count,
'comments':comments,
'form':form
}
return render (request, 'base/certificate.html',context)
Click On The Image To See The Error

Implementing follower system using a many to many field through a model

I am adding a following/follower system and I made it a foreign field in my profile model. The thing is, I want to be able to save multiple information to it like the time when a particular user started following and other future info, so I made the many to many field pass through a model, as shown here: https://docs.djangoproject.com/en/3.0/topics/db/models/#extra-fields-on-many-to-many-relationships
Please, I will like to know what I am missing as I am getting
AttributeError at /api/opeyemi-odedeyi-ikx5yh/follow/
'User' object has no attribute 'following'
I have tried this:
models.py
class Profile(models.Model):
SEX= (
('M', 'Male'),
('F', 'Female'),
)
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='profiles')
date_of_birth = models.DateField(blank=True, verbose_name="DOB", null=True)
bio = models.TextField(max_length=500, blank=True, null=True)
sex = models.CharField(max_length=1, choices=SEX, blank=True, null=True)
following = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, through='Follow', related_name='followed_by')
updated_on = models.DateTimeField(auto_now=True)
def __str__(self):
return self.user.fullname
class Follow(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='user_following')
profile = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name='profile')
date_followed = models.DateField(auto_now_add=True)
serializers.py
class ProfileDetailedSerializer(serializers.ModelSerializer):
user = serializers.SlugRelatedField(read_only=True, slug_field='slug')
age = serializers.SerializerMethodField(read_only=True)
following = serializers.SlugRelatedField(read_only=True, slug_field='slug', many=True)
following_count = serializers.SerializerMethodField(read_only=True)
user_has_followed = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Profile
fields = "__all__"
read_only_fields = ('pk', 'user')
def get_age(self, instance):
today = date.today()
dob = instance.date_of_birth
if dob==None:
return None
return today.year - dob.year - ((today.month, today.day) < (dob.month, dob.day))
def get_following_count(self, instance):
return instance.following.count()
def get_user_has_followed(self, instance):
request = self.context.get("request")
return instance.following.filter(pk=request.user.pk).exists()
views.py
class UserFollowAPIView(APIView):
'''
Can follow(post) and unfollow(delete) the user
'''
serializer_class = ProfileDetailedSerializer
permission_classes = [IsAuthenticated]
def delete(self, request, slug):
followUser = get_object_or_404(User, slug=slug)
user = self.request.user
followUser.following.remove(user)
followUser.save()
serializer_context = {"request": request}
serializer = self.serializer_class(followUser, context=serializer_context)
return Response(serializer.data, status=status.HTTP_200_OK)
def post(self, request, slug):
followUser = get_object_or_404(User, slug=slug)
user = self.request.user
followUser.following.add(user)
followUser.save()
serializer_context = {"request": request}
serializer = self.serializer_class(followUser, context=serializer_context)
return Response(serializer.data, status=status.HTTP_200_OK)
urls.py
path("<slug:slug>/follow/", UserFollowAPIView.as_view(), name="user-follow"),
Note:
I used slug because I am not using username in my app, so I had a slug field and made it the lookup field instead of the normal pk
The serializer_context is so the user cannot follow when it returns true (i.e they cannot follow twice)

Need help writing a Unit Test in Django

Good day everybody.
I need to test a view, and I got stuck ( I am a django newbie).
I got through all other url and view tests, but I'm really not sure how to write this one.
I tried writing it on my own, I read through the testing docs, I even tried testing this using model_mommy ( but I got a custom HTML field in my models ) which is not supported by mommy. I tried with mommy Recipes, also without luck. I really gotta do this test today, it's a part of a task I was given.
here is the view :
class CommentCreateView(CreateView):
def get(self, request, *args, **kwargs):
context = {'form': CommentForm()}
return render(request, 'news/add_comment_to_article.html', context)
def post(self, request, *args, **kwargs):
form = CommentForm(request.POST)
if form.is_valid():
article = get_object_or_404(Article, pk=kwargs.get('pk'))
print(article)
comment = form.save(commit=False)
comment.post = article
comment.save()
return HttpResponseRedirect(reverse('news:article', kwargs={'article_id': article.pk}))
else:
form = CommentForm()
return render(request, 'news/add_comment_to_article.html', {'form': form})
and here are my models:
class Article(models.Model):
title = models.CharField('title', max_length=200, blank=True)
slug = AutoSlugField(populate_from='title', default="",
always_update=True, unique=True)
author = models.CharField('Author', max_length=200, default="")
description = HTMLField()
is_published = models.BooleanField(default=False)
article_text = HTMLField('Article text', default="")
pub_date = models.DateTimeField(default=datetime.now, blank=True)
article_category = models.ForeignKey(Category, on_delete="models.CASCADE", blank=True, null=True, default="")
my_order = models.PositiveIntegerField(default=0, blank=False, null=False)
class Meta(object):
ordering = ['my_order']
def __str__(self):
print(self.image.all())
return self.title
class ArticleImages(models.Model):
article = models.ForeignKey(Article, on_delete="models.CASCADE", related_name="image")
image = models.ImageField("image")
my_order = models.PositiveIntegerField(default=0, blank=False, null=False)
def image_tag(self):
return mark_safe('<img src="/media/%s" width="150" height="150" />' % (self.image))
image_tag.short_description = 'Image'
class Meta(object):
ordering = ['my_order']
def __str__(self):
return self.image.url
class Comment(models.Model):
post = models.ForeignKey('Article', on_delete=models.CASCADE, related_name='comments')
author = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=datetime.now, blank=True)
approved_comment = models.BooleanField(default=False)
def approve(self):
self.approved_comment = True
self.save()
def __str__(self):
return self.text
I would REALLY appreciate someone helping me out here.
Thank you, and have a good day !

Put request.user.email inside another model field

I try to get user email from user model and put it in a field on another model the code is working and the counter count perfect but the add line not this is the code:
models.py
class Posts(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
title = models.CharField(blank=True, max_length=100)
slug = models.SlugField(null=True, blank=True)
contain = models.TextField(blank=True)
post_image = models.ImageField(upload_to="post_img", default='post_img/humanity.jpg')
post_published = models.DateTimeField(blank=True, auto_now_add=True)
post_updated = models.DateTimeField(blank=True, auto_now=True)
post_deleted = models.BooleanField(default=False)
post_approved = models.BooleanField(default=False)
post_sponsored = models.CharField(max_length=100, null=True, blank=True, default = None)
post_sponsored_accepted = models.BooleanField(default=False)
def save(self, *args, **kwargs):
if not self.slug:
self.slug= slugify(self.title)
super(Posts, self).save(*args, **kwargs)
def __str__(self):
return f'Author: {self.author}, Title:{self.title}, Posted: {self.post_published}'
views.py
def post_check(request, pk):
pos = Posts.objects.get(pk=pk)
u = UserProfile.objects.get(user=request.user)
if u.sp_counter < 3:
pos.post_sponsored = request.user.email
u.sp_counter += 1
u.save()
messages.success(request, 'طلبك قيد المراجعة، الرجاء انتظار الموافقة')
return redirect('index')
else:
messages.warning(request, 'وصلت الحد الاقصى للكفالات')
return redirect('index')

Categories