Django - Query by foreign key in views - python

I am trying to make a button on the post that when a user cliks on it, is requesting to be added to the post as an attendance and then, the author of the post has to approve that request.
Models.py
class Attending(models.Model):
is_approved = models.BooleanField(default=False)
attending = models.ManyToManyField(User, related_name='user_event_attending')
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField(blank=True)
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
attending = models.ForeignKey(Attending, on_delete=models.CASCADE, verbose_name='atending', null=True)
My problem here is that every time I writte a query for the button is giving me erros and I couldn`t figure it out how to get the reverse of the foreign key.
This is my code on my views.py
def request_event(request, pk):
previous = request.META.get('HTTP_REFERER')
try:
query = Attending.objects.get(pk=pk)
request_attending = query.post_set.add(request.user)
messages.success(request, f'Request sent!')
return redirect(previous)
except query.DoesNotExist:
return redirect('/')
Thank you very much for your help in advance!

This: query.post_set is just relationship. You cannot call method add just like that. You can add to ManyToMany relation and I believe you want to add user to Attending.attending field, not directly to Post object. Change that to:
...
query = Attending.objects.get(pk=pk)
query.attending.add(request.user)
messages.success(request, f'Request sent!')
....
| Update |
I think you should consider rearraning your relationships. If I understand your plan, you should go this way:
class Attending(models.Model):
...
attendant = models.ForeignKey(User, related_name='events_attending', on_delete=models.CASCADE)
post = models.ForeignKey('Post', on_delete=models.CASCADE)
class Post(models.Model):
...
author = models.ForeignKey(User, on_delete=models.CASCADE)
For one Post object there can be many Attending objects, then you can use relations like that:
att = Attending.objects.first()
att.post # get related Post object from ForeignKey | there is only one
post = Post.objects.first()
post.attending_set.all() # get all related Attending objects
Post.objects.get(attending=att) # get Post object that the Attending object have in ForeignKey field
user = User.objects.first()
user.post_set.all() # get all Post objects that User is author in
user.events_attending.all() # get all related Attending objects
For more check Django Docs.

Related

Django - Annotate post query set data with if current user has "liked" the post

So I have this model
model.py
class Post(models.Model):
uuid = models.UUIDField(primary_key=True, default=generate_ulid_as_uuid, editable=False)
created = models.DateTimeField('Created at', auto_now_add=True)
updated_at = models.DateTimeField('Last updated at', auto_now=True, blank=True, null=True)
creator = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="post_creator")
body = models.CharField(max_length=POST_MAX_LEN, validators=[MinLengthValidator(POST_MIN_LEN)])
class LikePost(AbstractSimpleModel):
creator = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="like_post")
post = models.ForeignKey(Post)
class User(AbstractDatesModel):
uuid = models.UUIDField(primary_key=True)
username = models.CharField(max_length=USERNAME_MAX_LEN, unique=True, validators=[
MinLengthValidator(USERNAME_MIN_LEN)])
created = models.DateTimeField('Created at', auto_now_add=True)
updated_at = models.DateTimeField('Last updated at', auto_now=True, blank=True, null=True)
Then I also have this annotator for returning a bunch of data outside of the Post table
annotator.py
def query_to_full_post_data_serializer(post_query_set: QuerySet):
query_set_annotated = post_query_set.annotate(
creator_username=F('creator__username'),
user_liked=F(<flag for each post in query for if user liked the post>)
reply_count=Count('postreply', distinct=True),
like_count=Count('likepost', distinct=True),
).prefetch_related(
Prefetch('photo', Photo.objects.order_by('-created')),
Prefetch('video', Video.objects.order_by('-created'))
)
return FullPostDataSerializer(query_set_annotated, many=True)
I'd like to return a field called "user_liked", which returns a boolean for each post in a query set that is True if the current logged in user has liked it or is the creator of a LikePost to a Post. When the request comes in I get the current user making the request so I can get their uuid. I'd like to use that uuid to check if the user has liked a post in the query set. How do I check if the current logged in user has liked a Post object in a query set Django?
I'm assuming you can do something like user_liked=F('likepost', filter=creator__uuid=current_user_uuid), but that wouldn't be a boolean that'd return user. If I really wanted I could do user_liked=Count('likepost, filter=creator__uuid=current_user_uuid) seems kind of inefficient though.
Sorry for my previous wrong answer.
So I'm quite rusty in the Django ORM so that query might not have best optimisation. Here is what I have come up with
Post.objects.all().annotate(liked_by_user=Q(likepost__creator=user))
The issue being that it will add duplicates for each reverse relationship it finds. That's why you should not use that and use a many to many.
For example, with a many to many, it would be as simple as
class User2(models.Model):
_id = models.BigAutoField(primary_key=True)
class Post2(models.Model):
_id = models.BigAutoField(primary_key=True)
creators = models.ManyToManyField(User2)
and now you only need
Post2.object.all().annotate(liked_by_user=Q(creator__in=[user]))
which is way better.

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.

Django efficient Queryset with Foreign Key Models

I'm trying to find the most efficient way (as less db queries as possible) for the following model structure.
In my template I then want to pass all the data from all 3 models because I would have to show the post data as well as looping through the comments to create a comments list and display all the attachments for the different comments.
class Post(BaseModel):
user = models.ForeignKey('User', blank=True, null=True,
title = models.CharField(max_length=128)
content = models.TextField()
class Comment(BaseModel):
post = models.ForeignKey('Post', on_delete=models.CASCADE)
user = models.ForeignKey('User', on_delete=models.SET_NULL)
text = models.TextField()
class CommentAttachment(BaseModel):
comment = models.ForeignKey('Comment', on_delete=models.CASCADE)
name = models.CharField(max_length=128)
Should I fetch all data from CommentAttachment direction (meaning fetching all CommentAttachments where comment__post__id is the post id and then get all other data with select_related) or is there another way to start from the Post Model?
You can use prefetch_related or select_related in your query:
posts = Post.objects.filter(user=some_user).prefetch_related(
'comment_set', 'comment_set__commentattachment_set'
)
For example, after making a query as mentioned, the following command may retrieve all the comments for the first post in the queryset without making a SQL query:
posts.first().comment_set.all()

Django filtering queries with multi-table inheritance

I'm using Django multi-table inheritance to implement a notifications system.
It looks like this:
class Notification(models.Model):
# this allows us to check the type without having to query another table
type = models.CharField(max_length=2, choices=type_choices)
user = models.ForeignKey(User, related_name='+', null=True)
date = models.DateTimeField(default=datetime.now)
read = models.BooleanField(default=False)
class Meta:
ordering = ["-date"]
# Users can comment on items.
class CommentNotification(Notification):
comment = models.ForeignKey(Comment, related_name='+')
class ShareNotification(Notification):
share = models.ForeignKey(Share, related_name='+')
# If user unsubscribes from an item, they will not receive notifications of comments on that item
class UnsubscribeItem(models.Model):
user = models.ForeignKey(User, related_name='+')
item = models.ForeignKey(Item, related_name='+')
class Comment(models.Model):
item = models.ForeignKey(Item, related_name='comments')
user = models.ForeignKey(User, related_name='+')
comment = models.TextField()
If I want to get all notifications for a user, I can simply query the Notification table. But I also want to exclude any CommentNotification entries if the user has unsubscribed from that item (only if there is an UnsubscribeItem with user=request.user and item=comment.item).
The problem of course is the field I want to filter is not on the base class. Is it possible to modify the queryset itself to exclude those entries? Or do I need to exclude them while serializing the collection? (I'm using django-rest-framework to serialize for my API, if that helps.)

NOT NULL constraint failed when linking a model with User

I have a model:
class UserProfile(models.Model):
user = models.OneToOneField(User)
first_name = models.CharField(max_length=30, default='anon')
last_name = models.CharField(max_length=30, default='anon')
interest = models.CharField(max_length=30, default='nothing')
def __str__(self):
return 'Username:%s' % self.user.username
class Post(models.Model):
title = models.TextField(default='No title')
text = models.TextField(max_length=220)
vote = models.IntegerField(default=0)
user_post = models.ForeignKey(User, related_name='post')
class Comments(models.Model):
name = models.TextField(default='Anon', null=True)
comment = models.TextField(max_length=2000, null=True)
post = models.ForeignKey(Post)
def __str__(self):
return 'comment:%s' % self.comment
In the post you can see I'm linking a post with the User. I read that you can access a users comments this way by using user.post_set.all(), so I gave it a try. When I attempt to migrate the change(adding the foriegn key in Post), I get an error.
django.db.utils.IntegrityError: NOT NULL constraint failed:
boardapp_post__new.user_post_id
I notice the post__new, so here is my view named new, which creates a post.
def new(request):
if request.method == 'POST':
post = Post.objects.create(title=request.POST.get('post_title', ''),
text=request.POST.get('post_text',''),vote=0
)
return HttpResponseRedirect(reverse('view_post', args=(post.id,)))
return render(request, 'new.html')
I'm new to creating users in django and am confused as to where not null consraint is failing. Thanks
When you migrate to your new database format, there should be a default User for the user_posts that already exist.
Django will try to fill this value for you in existing user_posts, but it doesn't know which value to chose, since null is not allowed in your current model.
Thus: you need to tell Django either 1) not to worry about Posts without a user (null = True) or 2) supply a default User, which is a bit harder (probably would require a function call that creates some dummy User on the fly).
So the easiest solution is to alter your Post model and change user_post:
user_post = models.ForeignKey(User, related_name='post', null=True)
It is failing because your model field is
user_post = models.ForeignKey(User, related_name='post')
it means every post will be assigned to a user(not null field).
From your view, you are not assigning any user to the post, so change your code to
post = Post.objects.create(title=request.POST.get('post_title', ''),
text=request.POST.get('post_text',''), vote=0, user_post=request.user
)

Categories