How to get the most liked users in django rest-api - python

So I have a social media app, where users can like the posts of other users. Now I want to fetch the top 20 users who have received the most number of likes. I am pretty much confused how to query my Likes Model
My LIKES MODEL
class PostLike(models.Model):
user_who_liked = models.ForeignKey(User, on_delete=models.CASCADE)
post_liked = models.ForeignKey(Post, on_delete=models.CASCADE)
liked_on = models.DateTimeField(default=timezone.now)
SIMPLIFIED POST MODEL
class Post(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
caption = models.TextField()
date = models.DateTimeField(default=timezone.now)
likes = models.ManyToManyField(
User, blank=True, through=PostLike)
image = models.TextField()
class Meta:
ordering = ['-id']
SIMPLIFIED USER MODEL
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
user_name = models.CharField(max_length=100, unique=True)
date = models.DateTimeField(default=timezone.now)
profile_picture = models.TextField(
default="https://www.kindpng.com/picc/m/24-248253_user-profile-default-image-png-clipart-png-download.png")
bio = models.CharField(max_length=200, default="")
objects = CustomManger()
def __str__(self):
return self.user_name
** My View **
#api_view(["GET"])
#permission_classes([IsAuthenticated])
def leaderboard(request):
# I dont know how to query the PostLike model now to get the most liked users
pass

First I changed the user attribute in your Post model, I added related_name because otherwise the related names were clashing. This is the definition I used, otherwise your models are unchanged.
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='author')
I.e. the posts by a user are accessible on User via the author attribute.
The following query gives you the top 20 users by number of likes they received:
User.objects.annotate(num_likes=Count('author__likes')).order_by('-num_likes')[:20]
Explanation:
Query User model and
annotate each user by doing a count:
author leads to the posts by the user
likes follows to PostLike and counts all likes which are associated with a post by the user
then order by number of likes descending,
and limit the number of retrieved objects to 20.

Related

How to get specific objects based on ManyToMany field match

I'm doing a cookbook app, which help users find meal thay can do with their ingridients. I'm using Django RestFramework, and i need to return list of avaliable meals that user can do, but don't know how to do search by ingridients
My models.py:
#models.py
class Meal(models.Model):
name = models.CharField(max_length=250)
description = models.TextField(blank=True, null=True)
recipe = models.TextField()
is_published = models.BooleanField(default=False)
category = ForeignKey('Category', on_delete=models.CASCADE, null=True)
user = ForeignKey(User, verbose_name='User', on_delete= models.CASCADE)
difficulty = ForeignKey('Difficulty', on_delete=models.PROTECT, null=True)
ingridients = models.ManyToManyField('Ingridient')
class Ingridient(models.Model):
name = models.CharField(max_length=100, db_index=True)
ico = models.ImageField(upload_to="photos/%Y/%m/%d/", blank=True, null=True)
category = ForeignKey('CategoryIngridients', on_delete=models.CASCADE, null=True)
def __str__(self):
return self.name
class CookBookUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
ingridients = models.ManyToManyField('Ingridient')
serializer.py
class MealSerializer(serializers.ModelSerializer):
class Meta:
model = Meal
fields = "__all__"
views.py
class CraftWithUsersIngridientsListAPIView(generics.ListAPIView):
serializer_class = MealSerializer
def get_queryset(self):
return Meal.objects.filter(ingridients=CookBookUser.objects.filter(user_id = self.request.user.id).ingridients)
CraftWithUsersIngridientsListAPIView isn't working and I get AttributeError 'QuerySet' object has no attribute 'ingridients', can someone help fix this?
I tried building different serializer but it doesn't help
class CraftWithUsersIngridientsListAPIView(generics.ListAPIView):
serializer_class = MealSerializer
def get_queryset(self):
user_ingredients = CookBookUser.objects.get(user=self.request.user).ingredients.all()
return Meal.objects.filter(ingredients__in=user_ingredients)
This way, you first get the CookBookUser instance for the current user, then get all of their ingredients, and finally, filter the Meal objects that contain those ingredients. The __in query lookup is used to check if the meal ingredients are in the user's ingredients.

Displaying number of messages sent by each category of users

This is what message_user_id contains:
message_user_id = Message.objects.all()
This is my context dictionary in admin.py:
'msg' : message_user_id,
This is my Message class in models.py:
class Message(models.Model):
message_text = fields.EncryptedTextField(blank=True, null=True)
conversation_id = models.ForeignKey(Conversation, on_delete=models.CASCADE)
origin_user = models.ForeignKey(UserInfo, on_delete=models.CASCADE, blank=False, default=None)
timestamp = models.DateTimeField(default=datetime.now, blank=True)
This is UserInfo class:
class UserInfo(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
is_buyer = models.BooleanField(default=False)
is_seller = models.BooleanField(default=False)
is_professional = models.BooleanField(default=False)
There are different roles for users that they can choose from while sign up. That information stays in UserInfo table which I'm referencing to as a foreign key in origin_user field in Message class. origin_user in Message contains the same information as id in UserInfo.
I want to display the number of messages sent by each category of users. How do I achieve this?.
As there are not many types you can do a search for each type and count how many messages there are, e.g. for Buyer:
messages_of_buyers = Message.objects.select_related(
'user_info'
).filter(
user_info__is_buyer=True
).count()

Use entered data from another django model

I have two class model, the User and Money Request. I am trying to access the data I entered in the User class so that whenever I requested money using the MoneyRequest class, I can also input my entered email, first and last name together with the withdraw_money.
I really need the data from the User class so that whenever I look at the admin page, I can see the name of the user who sent the money request.
Here is my models.py
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
username = models.CharField(max_length=100, unique=True)
first_name = models.CharField(max_length=100, blank=True, null=True)
last_name = models.CharField(max_length=100, blank=True, null=True)
class MoneyRequest(models.Model):
date_requested = models.DateTimeField(auto_now_add=True)
withdraw_money = models.DecimalField(null=True, blank=True, max_digits=8, decimal_places=2, help_text='Minimum withdrawal is ₱300.00.', validators=[minimum_money])
Here is my views for my MoneyRequest class
class UserAccountsView(CreateView):
model = MoneyRequest
fields = ['withdraw_money',] # Keep listing whatever fields
template_name = 'users/accounts.html'
def form_valid(self, form):
user = form.save()
user.save()
return redirect('users:user_account', self.request.user.username)
What should I put in my MoneyRequest class in order to get the data entered in the User class? Thank you!
In your moneyrequest model add
user = models.ForeignKey(User, on_delete=models.CASCADE)
Then you can get all User model data.

How to process data from one model field to another

I have models of Exercise, Training and Workout.
Training contains some exercises (Exercise)
Workout contains trainings (Training).
Snippet of my models.py:
class Exercise(models.Model):
user = models.ForeignKey(User, related_name='exercises',
on_delete=models.CASCADE)
name = models.CharField(max_length=80)
description = models.TextField(max_length=300)
details = models.ManyToManyField(ExerciseDetail, blank=True)
...
class Training(models.Model):
user = models.ForeignKey(User, related_name='trainings',
on_delete=models.CASCADE)
name = models.CharField(max_length=80)
description = models.CharField(max_length=250)
exercises = models.ManyToManyField(Exercise, related_name='trainings',
blank=True)
...
class Workout(models.Model):
user = models.ForeignKey(User, related_name='workouts',
on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now=True)
name = models.CharField(max_length=200)
description = models.TextField(max_length=400, blank=True)
trainings = models.ManyToManyField(Training, related_name='workouts',
blank=True)
...
I would like to have possibility to use something like Workout.objects.get(name='workout').exercises.objects.all() to get a list/set of all exercises included in trainings of chosen Workout.
I would also like to have possibility to use exercises`` field with Django Rest Framework to list all exercises, possibly with link to particularExercise``` model serializer.
Can someone give a hint how can I do that?
You can query this with:
Exercise.objects.filter(
trainings__workouts__name='workout'
)
With the consecutive underscores (__), you thus can look "through" relations.
This will thus return the Exercises that belong to Trainings that belong to Workouts with as name 'Workout'.

Django saving new record to a table with multiple foreign keys

So i basically have a model called Designations that derives foreign keys from 3 other models (curricula, role and staff) and i am trying to save a new record into the Designations model, the code below shows the Designations and Staffs model. However, for the Curriculum and role models i will not be showing as you can assume PK 1 in Curriula and role models will be used as data in the curriculum and role fields in Designation model
class Designations(models.Model):
designation_id = models.AutoField(primary_key=True)
curriculum = models.ForeignKey(Curricula, on_delete=models.CASCADE)
role = models.ForeignKey(Roles, on_delete=models.CASCADE)
staff = models.ForeignKey(Staffs, on_delete=models.CASCADE)
class Meta:
db_table = "arc_designations"
unique_together = ('curriculum', 'role', 'staff')
verbose_name_plural = "Designations"
ordering = ['designation_id']
def __str__(self):
return '%s of %s %s (%s)' % (self.role.role_name,
self.curriculum.course_period.course.course_abbreviation,
self.curriculum.module_period.module.module_abbreviation,
self.staff.staff_full_name)
class Staffs(models.Model):
staff_id = models.AutoField(primary_key=True)
admission_number = models.CharField(max_length=5,
unique=True,
help_text="Enter 5 digits",
validators=[numeric_only, MinLengthValidator(5)])
staff_full_name = models.CharField(max_length=70,
help_text="Enter staff's full name",
validators=[letters_only])
created_by = UserForeignKey(auto_user_add=True,
editable=False,
related_name="staff_created_by",
db_column="created_by")
created_at = models.DateTimeField(auto_now_add=True,
editable=False)
updated_by = UserForeignKey(auto_user=True,
editable=False,
related_name="staff_updated_by",
db_column="updated_by")
updated_at = models.DateTimeField(auto_now=True,
editable=False)
roles = models.ManyToManyField(Roles, through='Designations')
class Meta:
db_table = "arc_staffs"
verbose_name_plural = "Staffs"
ordering = ['staff_id']
def __str__(self):
return '%s (S%s)' % (self.staff_full_name, self.admission_number)
I have made a forms.py to get the admission_number(field in Staffs model)
class AssignRolesForm(forms.Form):
admission_no = forms.CharField(max_length=40,
widget=forms.TextInput(
attrs={'class': 'form-control', 'aria-describedby': 'add-btn'}))
Assuming that when i submit the form i would like for the admission_number entered in the form to reference into the Staffs model then get its PK to be saved into the staff field in Designations then as i said above, for the curriculum and role fields to referece PK 1 of their respective models. How would i write my function in views.py
**edit
I've tried writing out the view as suggested, heres the code below, currently i cant test my project so let me now if its correct
#require_POST
def setStaff(request):
form = AssignRolesForm(request.POST)
if form.is_valid():
Designations.staff = Staffs.objects.filter(admission_number=form.cleaned_data['admission_no'])
Designations.role = Roles.objects.get(pk=1)
Designations.curriculum = Curricula.objects.get(pk=1)
Designations.save()
return redirect('index')
Overriding the clean function on forms is probably where you want to put your logic. The django docs have a good example:
https://docs.djangoproject.com/en/2.0/ref/forms/validation/#cleaning-and-validating-fields-that-depend-on-each-other

Categories