Good Day,
My Problem is:
my project depending on giving points to users who make any actions like (post, comment, favorite, likes ......).
So, in users list page i want to list all users and other data for each user (name, points, badges, ....)
to give the users points i have to count his posts, comments, likes, and so on.....
i tried several methods and ways but all is failed to get annotate or prefetch_related or select_related
Models.py
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
Views.py
def user_list(request):
users = User.objects.all()
template = 'user/users_list.html'
nested_posts = {}
for user in users:
posts = user.posts.all()
nested_posts[user, posts] = posts.count()
print("nested : ", nested_posts)
context = {
'users': users,
'user':user,
'posts': posts,
'nested_posts': nested_posts,}
return render(request, template, context)
when i print nested .. i found the count of every user' posts .. but how can i make it as variable to re-use in calculated field
QuerySet
nested : {(<User: Fareed>, <QuerySet [<Post: Senior Purchasing Specialist>]>): 1,
(<User: Hussein>, <QuerySet [<Post: Senior Software Development Engineer in Test>]>): 1,
(<User: Karima>, <QuerySet []>): 0,
(<User: Yahia>, <QuerySet []>): 0}
and i also tried :
GetUserID = User.objects.get(id=2)
var01 = GetUserID.posts.all().count()
but this was for one user with (id=2) .. and sure all users got the total posts of user(id=2) not for each of them.
and i also tried :
Posts_count_per_user = User.posts.annotate(posts_count=Count('posts'))
User_Score_of_posts = Posts_count_per_user.aggregate(posts_score=Count('posts_count') * 1000)
but i got this error:
'ReverseManyToOneDescriptor' object has no attribute 'annotate'
any suggestions please ...
Thanks in advance,
Have you tried this,
user_qs = User.objects.annotate(posts_count=Count('posts'))
# usage
for user_instance in user_qs:
print("post count: ", user_instance.posts_count)
print("post score: ", user_instance.posts_count * 1000)
Or you can annotate the post score in the DB level itself,
from django.db.models import F, Count
user_qs = User.objects.annotate(posts_count=Count('posts'), posts_score=F('posts_count') * 1000)
Related
i'm trying to call back unique constraints field , in my project i have to count number of M2M selected
class Booking(models.Model):
room_no = models.ForeignKey(Room,on_delete=models.CASCADE,blank=True,related_name='rooms')
takes_by = models.ManyToManyField(Vistor)
#property
def no_persons(self):
qnt = Booking.objects.filter(takes_by__full_information=self).count()#but this doesnt work
return qnt
Cannot query "some room information": Must be "Vistor" instance.
class Vistor(models.Model):
full_name = models.CharField(max_length=150)
dob = models.DateField(max_length=14)
city = models.ForeignKey(City,on_delete=models.CASCADE)
class Meta:
constraints = [
models.UniqueConstraint(fields=['full_name','dob','city'],name='full_information')
]
def __str__(self):
return f'{self.full_name} - {self.city} - {self.dob}'
it it possible to access full_information through Booking model ? thank you ..
If you want to count the number of Visitors related to that booking, you can count these with:
#property
def no_persons(self):
self.taken_by.count()
This will make an extra query to the database, therefore it is often better to let the database count these in the query. You can thus remove the property, and query with:
from django.db.models import Count
Booking.objects.annotate(
no_persons=Count('takes_by')
)
The Bookings that arise from this QuerySet will have an extra attribute no_persons with the number of related Visitors.
I'm workin on a blog page and I'm trying to filter posts by tag, the problem is that I get several pages when only 1 or 2 posts match the query (I have per_page set to 6).
I have another filter by followed posts that works correctly, therefore I guess the problem is in the query object.
This is part of my code for the view index:
if current_user.is_authenticated:
show_followed = bool(request.cookies.get('show_followed', ''))
if show_followed:
query = current_user.followed_posts
elif show_tag:
tag = Tag.query.filter_by(tag_name=show_tag).first()
query = tag.post
else:
query = Post.query
page = request.args.get('page', 1, type=int)
pagination = query.order_by(Post.timestamp.desc()).paginate(
page, per_page=current_app.config['POSTS_PER_PAGE'],
error_out=False)
posts = pagination.items
return render_template("index.html", posts=posts, pagination = pagination, show_followed=show_followed, show_tag=show_tag)
when I try to replicate the issue in the console, I check that the query for a sample tag matches 3 posts, nevertheless pagination.total (total items) returns 23! What does the pagination object take as items??
I did this to test in the flask shell:
tagname = 'pruebatag'
tag = Tag.query.filter_by(tag_name=tagname).first()
query = tag.post
#here if y try query.all() I get 3 posts in return
pag_object = query.paginate(per_page = 6)
pag_object.total
>>> 23
pag_object.pages
>>> 4
pag_object.items #returns items for current page
>>> [<Post example>] #only one post returned for this page? why 4 pages?
I copy my definition of the tags table:
class Tag(db.Model):
__tablename__ = 'tags'
id = db.Column(db.Integer, primary_key=True)
tag_name = db.Column(db.String(40), unique=True)
def add_tag(self):
if not self.id:
db.session.add(self)
db.session.commit()
tag_join = db.Table('tag_join',
db.Column('post_id', db.Integer, db.ForeignKey('post.id')),
db.Column('tag_id', db.Integer, db.ForeignKey('tags.id'))
)
And in the post model:
tags = db.relationship('Tag',
secondary=tag_join,
backref=db.backref('post', lazy='dynamic'),
lazy='dynamic')
Any help would be highly appreciated as I am wasting a lot of time on this issue
Just let mi know if you need more details.
Thanks!
Finally I found what happens, I realize that table tag_join is full of duplicates, that cause the issue.
I want to write all types of complex queries,
for example :
If someone wants information "Fruit" is "Guava" in "Pune District" then they will get data for guava in pune district.
htt//api/?fruit=Guava&?district=Pune
If someone wants information "Fruit" is "Guava" in "Girnare Taluka" then they will get data for guava in girnare taluka.
htt://api/?fruit=Guava&?taluka=Girnare
If someone wants information for "Fruit" is "Guava" and "Banana" then they will get all data only for this two fruits, like wise
htt://api/?fruit=Guava&?Banana
But, when I run server then I cant get correct output
If i use http://api/?fruit=Banana then I get all data for fruit which is banana, pomegranate, guava instead of get data for fruit is only banana. So I am confuse what happen here.
can you please check my code, where I made mistake?
*Here is my all files
models.py
class Wbcis(models.Model):
Fruit = models.CharField(max_length=50)
District = models.CharField(max_length=50)
Taluka = models.CharField(max_length=50)
Revenue_circle = models.CharField(max_length=50)
Sum_Insured = models.FloatField()
Area = models.FloatField()
Farmer = models.IntegerField()
def get_wbcis(fruit=None, district=None, talkua=None, revenue_circle=None, sum_insured=None, area=None,min_farmer=None, max_farmer=None, limit=100):
query = Wbcis.objects.all()
if fuit is not None:
query = query.filter(Fruit=fruit)
if district is not None:
query = query.filter(District=district)
if taluka is not None:
query = query.filter(Taluka=taluka)
if revenue_circle is not None:
query = query.filter(Revenue_circle= revenue_circle)
if sum_insured is not None:
query = query.filter(Sum_Insured=sum_Insured)
if area is not None:
query = query.filter(Area=area)
if min_farmer is not None:
query = query.filter(Farmer__gte=min_farmer)
if max_farmer is not None:
query = query.filter(Farmer__lt=max_farmer)
return query[:limit]
Views.py
class WbcisViewSet(ModelViewSet):
queryset = Wbcis.objects.all()
serializer_class = WbcisSerializer
def wbcis_view(request):
fruit = request.GET.get("fruit")
district = request.GET.get("district")
taluka = request.GET.get("taluka")
revenue_circle = request.GET.get("revenue_circle")
sum_insured = request.GET.get("sum_insured")
area = request.GET.get("area")
min_farmer = request.GET.get("min_farmer")
max_farmer = request.GET.get("max_farmer")
wbcis = get_wbcis(fruit, district, taluka,revenue_circle,sum_insured,area, min_farmer, max_farmer)
#convert them to JSON:
dicts = []
for wbci in wbcis:
dicts.append(model_to_dict(wbci))
return JsonResponse(dicts)
Serializers.py
from rest_framework.serializers import ModelSerializer
from WBCIS.models import Wbcis
class WbcisSerializer(ModelSerializer):
class Meta:
model = Wbcis
fields=('id','Fruit','District','Sum_Insured','Area','Farmer','Taluka','Revenue_circle',)
whats need changes in this code for call these queries to get exact output?
I don't think that you're actually calling that view, judging by your usage I presume you're calling the viewset itself and then ignoring the query params.
You should follow the drf docs for filtering but essentially, provide the get queryset method to your viewset and include the code you currently have in your view in that
class WbcisViewSet(ModelViewSet):
queryset = Wbcis.objects.all() # Shouldn't need this anymore
serializer_class = WbcisSerializer
def get_queryset(self):
fruit = self.request.query_params.get("fruit")
....
return get_wbscis(...)
I have a little tricky question (I think)
I have a model called Post(models.Model): with a many to many relation called shares
class Post(models.Model):
account = models.ForeignKey(Account, related_name="account")
shares = models.ManyToManyField(Account, related_name="shares_account", through='Share')
...
class Share(models.Model):
post = models.ForeignKey(Post)
account = models.ForeignKey(Account)
new = models.BooleanField(default=True)
...
Now I need a way to filter Posts which has exact "x" shares .. eg. I have a post with id "222" this post is shared with account id 12, 13, 16 .. now I want to filter on all post shared with account id 12,13 and 16 .
How can I do so?
My bad try is like so:: ;)
posts = Post.objects.filter(reduce(and_, [Q(shares=aid) for aid in account_ids]))
I suppose this should work for you.
Post.objects.annotate(cc=Count('shares')).filter(cc=3).filter(Q(shares__id=12) & Q(shares__id=13) & Q(shares__id=16))
See this -> Django ManyToMany filtering by set size or member in the set
This is how to achieve it with reduce
post_qs = Post.objects.all()
post_qs = reduce(lambda posts, share: posts.filter(shares=share), account_ids, post_qs)
Which I would probably make it more reusable this way:
class PostQuerySet(models.QuerySet):
def having_shares(self, share_ids):
return reduce(lambda posts, share: posts.filter(shares=share), share_ids, self)
class Post(models.Model):
...
objects = PostQuerySet.as_manager(
#usage
posts = Post.objects.all().having_shares(share_ids)
I have a like feature which allows students to like in other pictures.
def LikePicture(request,picture_id):
p = Picture.objects.get(pk=picture_id)
new_like, created = Like.objects.get_or_create(user=request.user, picture_id=picture_id)
return HttpResponseRedirect(reverse('world:Boat', kwargs={'animal_id': the_id }))
At the moment , I'm trying to implement a restriction toward the people who likes a particular picture.
I want only the people who are friends with a particular users to like their's picture and the only way
I can do this is by implementing a friend models.
2 Users can only be friends if both users add each other.
I'm planning to use this friendship models unless someone has a better friendship models that we can both use to implement the restriction.
class Friendship(models.Model):
from_friend = models.ForeignKey(User, related_name='friend_set')
to_friend = models.ForeignKey(User, related_name='to_friend_set')
def __unicode__(self):
return u'%s, %s' % (
self.from_friend.username,self.to_friend.username)
class Meta:
unique_together = (('to_friend', 'from_friend'), )
We can bulid a relationship with
>>> user1 = User.objects.get(id=1)
>>> user2 = User.objects.get(id=2)
>>> friendship1 = Friendship(from_friend=user1, to_friend=user2)
>>> friendship1.save()
>>> user1.friend_set.all()
[<Friendship: user1, user2>, <Friendship: user1, user3>]
but inorder to become friends . the other users has to add the other users.
>>> friendship3 = Friendship(from_friend=user2, to_friend=user1)
>>> friendship3.save()
>>> user2.friend_set.all()
[<Friendship: user2, user1>]
Now , The problem is how can we edit the like function so it would only allows friends to like a picture.
I have thought of a way but I couldn't find a way to implement it into my like function. My idea is because we can grab the User object who own the picture and the guy who's trying to like the picture . We can compare each other friendlist .
We get the user object who own the picture and we get the user object whose trying to like the picture.We then compare if the user who's own the picture has the user who's trying to like his picture on his friendlist >>> user1.friend_set.all()
and we do it in reverse . If the user owner's picture is inside the request.user friendlist .Allow Like!!! .
So it's like , if both users have each other in the same friendlist . Allow like!!!
It would be something similar to this function
def Like(request,picture_id):
user1 = User.objects.get(user=request.user) # The Guy who trying to like the picture
p = Picture.objects.get(pk=picture_id)
user2 = User.objects.get(picture=p) # The owner of the picture
if user1 is in user2.friend_set.all() AND if user2 is in user1.friend_set.all():
new_like, created = Like.objects.get_or_create(user=request.user, picture_id=picture_id)
else:
message ('You can only like if you are an friend ')
return HttpResponseRedirect(reverse('world:Boat', kwargs={'animal_id': the_id }))
return HttpResponseRedirect(reverse('world:Boat', kwargs={'animal_id': the_id }))
Or we can compare Users object
>>> [friendship.to_friend for friendship in
user1.friend_set.all()]
[<User: user2>, <User: user3>]
Little bit improvement required:
class FriendshipManager(models.Manager):
def is_friends(self, user_a, user_b):
if self.get_query_set().filter(
user__id=user_a.id, friend__id=user_b.id, is_accepted=True).exists() or \
self.get_query_set().filter(
user__id=user_b.id, friend__id=user_a.id, is_accepted=True).exists():
return True
else:
return False
class Friendship(models.Model):
user = models.ForeignKey(User)
friend = models.ForeignKey(User, related_name='friends')
is_accepted = models.BooleanField(default=False)
objects = FriendshipManager()
def __unicode__(self):
return u'%s, %s' % (
self.user.username, self.friend.username)
class Meta:
unique_together = (('user', 'friend'), )
def Like(request, picture_id):
user = request.user
p = get_object_or_404(Picture, pk=picture_id)
pic_owner = p.user # i hope you have user relationship with pic here
if Friendship.objects.is_friends(user, pic_owner) \
pic_owner.id == user.id:
# if the pic owner and requested user are friends
# or user is liking his own pic
new_like, created = Like.objects.get_or_create(
user=user, picture=p)
else:
message ('You can only like if you are friends')
return HttpResponseRedirect(
reverse('world:Boat', kwargs={'animal_id': the_id }))
return HttpResponseRedirect(
reverse('world:Boat', kwargs={'animal_id': the_id }))
Added is_accepted field to make sure other person approved the friend request.
Added is_friendsmethod in manager which will be used to check if two users are friends.
In view also checked if requested user and picture owner are same then it means user can like its own posted picture. This code is untested but I hope this will serve you well.