Django - Filter on ManyToManyField - django

I have two models:
class Student(models.Model):
first_name = models.CharField()
last_name = models.CharField()
class Group(models.Model):
name = models.CharField()
students = models.ManyToManyField(Student)
Some data (first_name and last_name concatenated):
Group #1 | Blak Coleman
Group #1 | Miguel Scott
Group #2 | Jordan Barnes
Group #2 | Jordan Gustman
Group #2 | Jekson Barnes
Group #3 | Jordan Smith
As you can see theres three students by name Jordan. So I need to return groups which in students queryset has only students by name Jordan.
I tried this:
groups = Group.objects.filter(students__first_name='Jordan')
But group.first().students.all() contains all the students not only Jordan. Expected result:
Group #2 | Jordan Barnes
Group #2 | Jordan Gustman
Group #3 | Jordan Smith
How could I do this?

Try this:
groups = Group.objects.filter(students__first_name__in=['Jordan'])

This worked:
from django.db.models import Prefetch
students = Student.objects.filter(first_name='Jordan')
prefetch = Prefetch('students', queryset=students)
groups = Group.objects.prefetch_related(prefetch)
groups = [group for group in groups if len(group.students.all())]

For all students you can write query like this...
groups = Group.objects.filter(students__id__in=[i.id for i in Students.objects.all()])

Related

How to get count of subjects taken by each student?

I've two models 'Students' and 'Enrollments'.
The schema for these is as below:
class Students(models.Model):
id = models.AutoField(primary_key=True, unique=True)
name = models.CharField()
class Enrollments(models.Model):
enroll_id = models.AutoField(primary_key=True, unique=True)
student_id = models.ForeignKey(Students, on_delete=models.CASCADE)
subjects = models.charField()
I'm trying to achieve the result of following SQL query in Django Rest Framework, for getting number of subjects enrolled by students (individually).
select
s.id, s.name, count(e.subjects) as count
from Students as s
left outer join Enrollments as e
on e.student_id_id = s.id
group by s.id, s.name, e.subjects
order by count asc;
This query returns result like:
---------------------------
| id | name | count |
---------------------------
| 1 | a | 1 |
| 2 | b | 0 |
| 3 | c | 2 |
---------------------------
Can anyone please help me acheive this kind of result.
Note: I need 0 count students details also.
What you can do is when you are creating a serializer, you can add a serializer method field which will get the count for you.
Add this at the top of your serializer:
count = serializers.SerializerMethodField('get_count')
Then add a function inside your serializer like this:
def get_count(self, obj):
try:
return Enrollments.objects.filter(student_id=obj.id).count()
except:
return None
Finally, add 'count' to your field list. You can then add as many fields as you want. I hope this will get you your desired result. Also don't forget to use "select_related" in the ORM inside your view to reduce the amount of queries.

How to count quantity with distinct?

I have 2 templates, one representing a product sheet and the other one an actual product in stock.
The stock can have several products that have the same product sheet.
Example:
I can have a product record "Water bottle", and several "water bottle" in the stock.
My models:
class Stock(models.Model):
machine = models.ForeignKey(
"machine.Machine",
verbose_name=_("machine"),
related_name="machine_stock",
on_delete=models.CASCADE
)
product = models.ForeignKey(
"client.Product",
verbose_name=_("product"),
related_name="product_stock",
on_delete=models.CASCADE
)
epc = models.CharField(_("EPC"), max_length=80)
dlc = models.DateField(_("DLC"))
class Product(models.Model):
name = models.CharField(_('Name'), max_length=255)
[...]
I want to retrieve the products in stock sorted by both DLC and name.
On my frontend I want to display a table with for each row:
the name of the product
the dlc
the number of products with this name and this dlc
Example:
If I have 2 product sheets (Product Model) :
water bottle
bottle of coca cola
and I have 5 products in stock (Stock Model) :
2 bottles of water whose dlc is 02/04/2022
2 bottles of cola whose dlc is the 02/04/2022
1 bottle of cola whose dlc is 03/04/2022
I want to display 3 lines in my table:
Quantity | Name | DLC
2 | water | 02/04/2022
2 | cola | 02/04/2022
1 | cola | 03/04/2022
I tried with
queryset = (
Stock.objects.all()
.select_related('product')
.select_related('machine')
.annotate(quantity=Count("product__name", distinct=True))
.distinct("dlc", "product__name",)
.order_by("-dlc")
)
But django ORM don't accept annotate and distinct in the same query.
It's works with:
queryset = (
Stock.objects.all()
.select_related('product')
.select_related('machine')
#.annotate(quantity=Count("product__name", distinct=True))
.distinct("dlc", "product__name",)
.order_by("-dlc")
)
for result in queryset:
result.quantity = Stock.objects.filter(dlc=result.dlc, product__name=result.product.name).count()
But I'm not sure that is the best method to use.

Django reading data from 2 models with foreignkey and make single list

I'm new to django. I've been coding with sql but django orm is hard for me to convert my knowledge of sql to orm models.
I've client model
class client(models.Model):
c_id = models.AutoField(primary_key=True)
name= models.TextField()
age=models.IntegerField()
and address model
class address(models.Model):
c_id = models.ForeignKey(client, on_delete=models.CASCADE)
addr = models.CharField(max_lenght=20)
city= models.CharField(max_lenght=20)
This is my table
---------------------------
| c_id|Name | age |
---------------------------
| 1 | John | 23 |
----------------------------
| 2 | Rose | 20 |
----------------------------
------------------------------
| c_id|addr | city |
------------------------------
| 1 | buspark | florida|
------------------------------
| 2 | homesquare| florida|
------------------------------
how to get allclient with address in list
Look at values() docs
The values() method takes optional positional arguments, *fields,
which specify field names to which the SELECT should be limited. If
you specify the fields, each dictionary will contain only the field
keys/values for the fields you specify. If you don’t specify the
fields, each dictionary will contain a key and value for every field
in the database table.
__ allows get related data, so in your case it could look like this
address.objects.values('c_id__c_id', 'c_id__name', 'c_id__age', 'addr', 'city')

Django join on multiple foreign fields (left join)

I'm using django 1.10 and have the following two models
class Post(models.Model):
title = models.CharField(max_length=500)
text = models.TextField()
class UserPost(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
approved = models.BooleanField(default=False)
How do I get a list of all the posts including the 'approved' property for the logged in user if exists? So instead of multiple queries, it would be one left join query, pseudo-code:
select * from posts as p
left join user_posts as up
on up.post_id = p.post_id
and up.user_id = 2
Output
post_id | title | text | user_id | approved
1 | 'abc' | 'abc' | 2 | true
2 | 'xyz' | 'xyz' | null | null
3 | 'foo' | 'bar' | 2 | true
I created the models this way because the 'approved' property belongs to the user. Every user can approve/reject a post. The same post could be approved and rejected by other users. Should the models be setup differently?
Thanks
Update:
I'm trying to create a webpage to display all available posts and highlight the ones that the current user approved. I could just list all posts and then for each post check if the 'UserPost' table has a value, if yes get the approved property else ignore. But that means if I have 100 posts I'm making 100 + 1 calls to the db. Is it possible to do 1 call using ORM? If this is not possible, should the models be setup differently?
Then I think you need something like this:
Post.objects.all().annotate(
approved=models.Case(
models.When(userpost_set__user_id=2,
then=models.F('userpost__approved')),
default=models.Value(False),
output_field=models.BooleanField()
)
)

django accessing raw many to many created table fields

Model:
class Subjects (models.Model):
name = models.CharField(max_length=100)
places = models.CharField(max_length=100)
class Student (models.Model):
name = models.CharField(max_length=40)
lastname = models.CharField(max_length=80)
subjects = models.ManyToManyField(Subjects, blank=True)
Django creates appname_student_subjects when I use model above.
appname_student_subjects table looks for example, like this:
id | student_id | subjects_id
-----------------------------------------
1 | 1 | 10
2 | 4 | 11
3 | 4 | 19
4 | 5 | 10
...
~1000
How can I access subjects_id field and count how many times subjects_id exists in the table above (and then do something with it). For example: If subject with id 10 exists two times the template displays 2. I know that I should use "len" with result but i don't know how to access subject_id field.
With foreign keys I'm doing it like this in a for loop:
results_all = Students.objects.filter(subject_id='10')
result = len(results_all)
and I pass result to the template and display it within a for loop but it's not a foreign key so it's not working.
You can access the through table directly.
num = (Students.subjects # M2M Manager
.through # subjects_students through table
.objects # through table manager
.filter(student_id=10) # your query against through table
.count())