Like functionality in Django - python

I'm developing a social platform and currently coding the like functionality for user posts. However, I can't seem to make it work. These are my Models.py:
class Post(models.Model):
user = models.ForeignKey(User)
posted = models.DateTimeField(auto_now_add=True)
content = models.CharField(max_length=150)
picturefile = models.ImageField(upload_to="post_content", blank=True)
class Like(models.Model):
user = models.ForeignKey(User, null=True)
post = models.ForeignKey(Post, null=True)
I pass the post ID through my url as 'post_id', and then in my views:
def liking(request, post_id):
newlike = Like.objects.create()
newlike.post = post_id
newlike.user = request.user
newlike.save()
return redirect(reverse('dashboard'))
However, it returns the following error:
Cannot assign "'47'": "Like.post" must be a "Post" instance.
Does anyone knows what I'm missing or doing wrong?

You are passing newlike.post a number (integer field) while it is expecting a Post instance.
This sould work:
from django.http.shortcuts import get_object_or_404
def liking(request, post_id):
post = get_object_or_404(Post, id=post_id)
newlike = Like.objects.create(user=request.user, post=post)
return redirect(reverse('dashboard'))
Note 1: Better use the handy shortcut get_object_or_404 in order to raise a 404 error when the specific Post does not exist.
Note 2: By calling objects.create will automatically save into the db and return an instance!

newlike.post should be a Post object, not an int.
You need to find post by id first:
post = Post.objects.get(pk=post_id)
newlike.post = post
or, if you don't want to do this lookup:
newlike.post_id = post_id

Related

Django Rest Framework with shortuuid, generics.RetrieveUpdateDestroyAPIView returning 404 {"detail": "Not found."}

I was remaking a social media site as a revision of Django and the rest framework, I didn't want to use the django default linear id count and didn't like how long the uuid library's ids was, so I used the shortuuid library. I've used them on the posts and the comments just to keep the anonymity of the count of both posts and comments. On the posts side everything works for the CRUD stuff (which should be proof that the issue isn't from the shortuuid library, as far as I know), although with the comments the Create Retrieve works perfectly but the Update Destroy doesn't. so here is the code we are working with:
starting with the models to know what kind of data we are working with (models.py):
from shortuuid.django_fields import ShortUUIDField
... # posts likes etc
class Comment(models.Model):
id = ShortUUIDField(primary_key=True, length=8, max_length=10)
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
body = models.TextField(max_length=350)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=True)
class Meta:
ordering = ['created']
def __str__(self):
return f'on {self.post} by {self.user}'
objects = models.Manager()
serializers.py:
class CommentSerializer(ModelSerializer):
username = SerializerMethodField()
def get_username(self, comment):
return str(comment.user)
class Meta:
model = Comment
fields = ['id', 'user', 'post', 'username', 'body', 'created', 'updated']
read_only_fields = ['id', 'post', 'user', 'username']
now with the routing (urls.py):
from django.urls import path
from .views import *
urlpatterns = [
...
path('<str:pk>/comments/' , Comments),
path('<str:pk>/comments/create/', CreateComment),
path('<str:pk>/comments/<str:cm>/', ModifyComment),
# pk = post ID
# cm = comment ID
]
views.py:
class ModifyComment(generics.RetrieveUpdateDestroyAPIView):
serializer_class = CommentSerializer
permission_classes = [permissions.AllowAny]
def get_queryset(self):
post = Post.objects.get(pk=self.kwargs['pk'])
comment = Comment.objects.get(post=post, pk=self.kwargs['cm'])
return comment
def perform_update(self, serializer):
print(Post.objects.all())
post = Post.objects.get(pk=self.kwargs['pk'])
comment = Comment.objects.filter(pk=self.kwargs['cm'], post=post)
if self.request.user != comment.user:
raise ValidationError('you can\'t edit another user\'s post')
if comment.exists():
serializer.save(user=self.request.user, comment=comment)
else:
raise ValidationError('the comment doesnt exist lol')
def delete(self, request, *args, **kwargs):
comment = Comment.objects.filter(user=self.request.user, pk=self.kwargs['cm'])
if comment.exists():
return self.destroy(request, *args, **kwargs)
else:
raise ValidationError("you can\'t delete another user\'s post")
ModifyComment = ModifyComment.as_view()
and the response to going to the url '<str:pk>/comments/<str:cm>/' comment of some post we get this:
side note, the perform_update function doesn't seem to be called ever, even putting a print statement at the beginning of the function doesn't get printed so the issue may have to do with the get_queryset even though I've tried using the normal queryset=Comment.object.all() and making the get_queryset function return the comment with the correct params but I couldn't make it work
For individual objects you need to overwrite the get_object method.
You are performing the request GET /str:pk/comments/str:cm/, this calls the retrieve method on the view, which in turn calls get_object. The default behaviour is trying to find a Comment object with id equal to pk since it's the first argument, since you need to filter through a different model you need to overwrite it.
classy drf is a good website for seing how the internals of the clases work.

operator does not exist: character varying = integer

I am building a BlogApp and I was working on a feature and I am stuck on a error.
operator does not exist: character varying = integer
LINE 1: ...d" = "taggit_tag"."id") WHERE "taggit_tag"."name" IN (SELECT...
I am trying to retrieve all the comments commented by user from Tags which were used in comment's post.
When I access the comments then it is keep showing that error when i access the variable in template.
models.py
class Post(models.Model):
post_user = models.ForeignKey(User, on_delete=models.CASCADE)
post_title = models.CharField(max_length=30)
tags = models.TaggableManager()
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post_of = models.ForeignKey(Post, on_delete=models.CASCADE)
views.py
class page(request):
tagQuery = Tag.objects.filter(post__comment__user=request.user)
#this is showing error
subquery = Comment.objects.filter(post_of__tags__name__in=tagQuery)
context = {'subquery':subquery}
return render(request, 'page.html', context)
It was showing
The QuerySet value for an exact lookup must be limited to one result using slicing.
So i used __in but then it keep showing that error.
Any help would be much Appreciated. Thank You
Rather than filtering according to queryset itself, you need to filter according to values of certain field:
class page(request):
tagQuery = Tag.objects.filter(post__comment__user=request.user)
subquery = Comment.objects.filter(post_of__tags__name__in=tagQuery.values_list('name'))
context = {'subquery':subquery}
return render(request, 'page.html', context)

How can i handle django nested models?

I have a User, Post and Tag model in Django. Tag model is not relevant for this topic. I can get all the data to the front end with nested objects. In the other hand when i want to create a new post i send the post data to django and in django view i am trying to update the data with relating the logged user to the "Post" but when i do that it gives me;
{'owner': {'username': [ErrorDetail(string='A user with that username already exists.', code='unique')]}}
error. How can i solve this error ?
models.py;
class Post(models.Model):
# Post specs
title = models.CharField(max_length=100, null=False)
place = models.CharField(max_length=100, null=False)
notes = models.CharField(max_length=10000, null=False)
tags = models.ManyToManyField(Tag)
start_date = models.DateField(null=True)
end_date = models.DateField(null=True)
created_at = models.DateField(auto_now=True)
owner = models.ForeignKey(User , null = True, on_delete=models.SET_NULL)
serializers.py;
class PostSerializer(serializers.ModelSerializer):
tags = serializers.SlugRelatedField(
many=True,
queryset=Tag.objects.all(),
slug_field='name'
)
owner = UserSerializer()
class Meta:
model = Post
fields = ('title','place','notes','start_date','end_date','created_at','id','owner','tags')
By the way if i change serializer.py like
owner = UserSerializer
it gives just primary key value. In front end i cant to anything with a integer number and i dont want to make an another api call for user model. Lastly view post function;
def post(self, request, format =None):
"""
Creates a post
"""
post = request.data ## copy dictionary to a variable
authenticatedUserDataAsDict = request.user.__class__.objects.filter(pk=request.user.id).values().first()
post.update( {'owner': authenticatedUserDataAsDict} ) ## attach authenticated user to post end
serializer = PostSerializer(data = post) ## serialize the dict
if serializer.is_valid():
serializer.save() ## if data valid save it.
return Response(serializer.data, status = status.HTTP_201_CREATED)
print("not valid->",serializer.errors)
return Response(serializer.errors, status = status.HTTP_400_BAD_REQUEST) # if it's not raise http 400
SOLVED
Hi again, it seems that rest framework have no idea about our request(create or get wise) because we are dealing with nested serializers.
So i found this article in medium and it helped me to solve my problem.

How to use deleteview for particular user and particular item in Django?

I have these models. Each reply can have none, one or more post. Post is user specific. How to make delete view so that user can only delete his post and not of posts by other on a reply.
I tried this many time but my view is deleting post of some other user as well. Means any user can delete post of any other user.
I want to make a button next to each post to delete, but button should be seen only to those who have written the post.
class Reply(models.Model):
User = models.ForeignKey(settings.AUTH_USER_MODEL)
Question = models.ForeignKey(Doubt, on_delete=models.CASCADE)
reply = models.TextField(max_length=40000)
last_updated = models.DateTimeField(auto_now_add=True)
image = models.ImageField(upload_to = upload_image_path, null = True, blank = True)
created_at = models.DateTimeField(auto_now_add=True)
def Post(self):
return reverse("community:post", kwargs={"pk": self.pk})
class Post(models.Model):
post = models.TextField(max_length=4000)
reply = models.ForeignKey(Reply, on_delete = models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
time = models.DateTimeField(null=True)
User = models.ForeignKey(settings.AUTH_USER_MODEL)
If you have the AuthenticationMiddleware enabled in settings.py, the request object in a view function will include a user model. Your view will then look something like this:
from django import http
def get_post_from_request(request):
... something to pull up the post object from the request ...
return the_post
def delete_post(request):
the_post = get_post_from_request(request)
if request.user == the_post.User:
the_post.delete()
return http.HttpResponseRedirect("/your/success/url/")
else:
return http.HttpResponseForbidden("Cannot delete other's posts")
If you're using generic class based views, your view might look more like this:
from django.views.generic import DeleteView
from django import http
class PostView(DeleteView):
model = Post
success_url = '/your/success/url/'
# override the delete function to check for a user match
def delete(self, request, *args, **kwargs):
# the Post object
self.object = self.get_object()
if self.object.User == request.user:
success_url = self.get_success_url()
self.object.delete()
return http.HttpResponseRedirect(success_url)
else:
return http.HttpResponseForbidden("Cannot delete other's posts")
If you'd like help navigating class based views (they have a dense inheritance hierarchy) I can recommend http://ccbv.co.uk - their breakdown on the Delete view is here

Django using add to add a ManyToManyField where the object is 'self'

When I execute the following code profile is added to user_profile.following as was expected, but user_profile is also added to profile.following (this is unwanted) why is this happening, I have a feeling it has something to do with the ForeignKey being 'self', but I'm not sure how to fix it...Here is the view:
def follow(request, profile_id):
user = request.user
profile = get_object_or_404(Profile, pk=profile_id)
user_profile = get_object_or_404(Profile, pk=user.id)
user_profile.following.add(profile)
return HttpResponseRedirect(reverse('twitter:profile', args=(profile.id,)))
and the model:
class Profile(models.Model):
user = models.ForeignKey(User)
bio = models.TextField()
image = models.ImageField(upload_to='images/%Y/%m/%d/')
following = models.ManyToManyField('self')
Well I finally found the answer to this after some more snooping, in case anyone else wants to know the solution: in the model, following should be
following = models.ManyToManyField('self', symmetrical=False)

Categories