Django - querying 2 tables with a many to many mapping table - django

If i have two tables people and fruits with a third table that maps a many to many relationship between the two.
PEOPLE TABLE
id person
1 bob
2 alice
FRUITS TABLE
id fruit
1 apple
2 pear
3 orange
4 grapes
PEOPLE_FRUITS_MAP
id person_id fruit_id
1 1 1
2 1 3
3 1 4
4 2 1
How would I get a Django QuerySet containing the names of all the fruits related to bob for example.
I guess in SQL it would be something like:
SELECT
fruits.id AS fid,
fruits.name AS fn,
FROM
people
LEFT JOIN
people_fruits_map
ON people.id = people_fruits_map.person_id
LEFT JOIN
fruits
ON fruits.id = people_fruits_map.fruit_id
WHERE
person.id = 1;
result of query
fid fn
--------------
1 apple
3 orange
4 grapes
DJANGO MODELS
class Fruits(models.Model):
fruit = models.TextField(unique=True, blank=True, null=True)
class Meta:
managed = False
db_table = 'fruits'
class People(models.Model):
person = models.TextField(unique=True, blank=True, null=True)
class Meta:
managed = False
db_table = 'people'
class PeopleFruitsMap(models.Model):
fruit = models.ForeignKey(Fruits, models.DO_NOTHING, blank=True, null=True)
people = models.ForeignKey(People, models.DO_NOTHING, blank=True, null=True)
class Meta:
managed = False
db_table = 'people_fruits_map'

For querying a many to many relation from your given models, you can write an ORM query in Django.
First you can add a reverse relation name in your model to make it more readable. This is not a necessary step as Django adds a default name itself if you don't.
In your PeopleFruitsMap model, add related_name
class PeopleFruitsMap(models.Model):
fruit = models.ForeignKey(Fruits, models.DO_NOTHING, blank=True, null=True, related_name='fruit_to_people_mapping')
people = models.ForeignKey(People, models.DO_NOTHING, blank=True, null=True)
class Meta:
managed = False
db_table = 'people_fruits_map'
Now try running this query in your Django shell. Run python manage.py shell, then run this query, where 1 is the id you require:
from your_app.models import Fruit
fruits = Fruit.objects.filter(fruit_to_people_mapping__people__id=1)
Id can be passed dynamically if you want.
To match the query, you can always use print(fruits.query) to check the equivalent postgres query.
Related name helps you refer the reverse relations in model objects, in your case, it will check the PeopleFruitsMap model mapping and in that mapping, we can query the people relation for matching the id.
References:
Django Querying Documentation

Related

Group records in queryset

I have the following models in an django app.
class Person(models.Model):
name = models.CharField(max_length=50)
class Product(models.Model):
owner = models.ForeignKey(Person, on_delete=models.CASCADE)
class Order(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
buyer = models.ForeignKey(Person, on_delete=models.CASCADE)
I would like to get a list of orders placed by a buyer grouped according to the owner of the product in the order.
Something like this
orders placed by buyer 1
order__product__owner 1
order 1
order 2
order 3
order__product__owner 2
order 4
order 5
order 6
Based on this related answer you can do this with a little extra logic in Python, carefully constructed to use only a single database query:
buyer = Person.objects.get(pk=1)
grouped_orders = {}
for o in buyer.order_set.all():
grouped_orders.setdefault(o.product.owner, []).append(o)

Django - How can I filter and count related objects of different ids

Model 1:
class Member(models.Model):
id = models.AutoField(primary_key=True)
names = models.CharField(max_length=255, blank=True)
student = models.ForeignKey('School', on_delete=CASCADE, null=True,
blank=True)
Model 2:
class School(models.Model):
id = models.AutoField(primary_key=True)
I want to count the total students who are in different schools.
I tried total_student = Members.filter(school=1+5+8).count()but this is not working. Note: 1, 5 and 8 are the ids of the same type of schools in the school model which different members attend.
Please help me get this right.
Use the __in query filter of Django for filtering on multiple data per column:
total_student = Members.filter(student__in=[1,5,8]).count()

How to get a specific record in a lookup table in Django

I'm building a Django app that manages client data. I store phone numbers, email addresses, addresses, etc., in lookup tables. I would like to create a queryset that
returns the primary numbers for all clients.
Here is an abbreviated version of the client table:
id
last
first
etc
100426
Smith
John
etc
114988
Johnson
Thomas
etc
An example of the phones table:
id
client_id
type_id
is_primary
number
1
100426
1
t
427-567-8382
2
100426
2
f
427-567-7789
3
114988
1
t
914-223-4597
And finally, the phone_type table:
id
type
1
mobile
2
home
3
office
4
condo
An extract of the client model:
class Client(models.Model):
id = models.IntegerField(primary_key=True)
last = models.CharField(max_length=32)
first = models.CharField(max_length=32)
phone = models.ForeignKey(
Phone,
on_delete=models.PROTECT,
blank=False,
null=False
)
The phone model:
class Phone(models.Model):
id = models.IntegerField(primary_key=True)
client_id = models.IntegerField
type_id = models.ForeignKey(
PhoneType,
on_delete=models.PROTECT,
blank=False,
null=False)
is_primary = models.BooleanField
country_code = models.CharField(max_length=5)
phone_number = models.CharField(max_length=16)
And the phone_type:
class PhoneType(models.Model):
id = models.SmallIntegerField(primary_key=True)
type = models.CharField(max_length=16, blank=False, null=False)
My ClientListView:
class ClientListView(ListView):
model = Client
template_name = 'client/client_list.html'
context_object_name = 'clients'
def get_queryset(self):
return Client.objects.order_by('-id').filter(status_id=3).select_related(Phone)
The get_queryset function is a placeholder for now.
How can I replace the get_queryset function so that I'm able to list the primary phone number for each client based on the "is_primary" field in the phones table?
I would use the prefetch api with a queryset on top of it.
from django.models import Prefetch
Client.objects
.prefetch_related(
'phone_numbers',
queryset=Phone.objects.filter(is_primary=True),
)
But this would only work if your model is correctly defined.
In your Phone class, client_id seems to be a foreign key to Client.
So you should have:
Phone(models.Model):
client = models.ForeignKey('Client', related_name='phone_numbers')

Django - How do I annotate the count of multiple foreign key fields (within 1 model) to same model

I am trying to annotate the count of two foreign key fields, incorporation_country and acquired_country (both linked to Country model). The fields are within the same model (CustomerInformation). Below is my models.py for reference
class Country(models.Model):
country_id = models.AutoField(primary_key=True)
country = models.CharField(max_length=500)
class CustomerInformation(models.Model):
customer_id = models.AutoField(primary_key=True)
customer_name = models.CharField(max_length=100)
incorporation_country = models.ForeignKey('Country', related_name='customers_incorporation', on_delete=models.CASCADE, null=True, blank=True)
acquired_country = models.ForeignKey('Country', related_name='customers_acquired', on_delete=models.CASCADE, null=True, blank=True)
What I would want to accomplish is to get CustomerInformation.objects.all() and from there, annotate to get the count of each country for incorporation_country and acquired_country. For example, I would want the output to be something like :
[{ 'country': 'Singapore', 'incorporated_count': 5, 'acquired_count': 3 },
{ 'country': 'Taiwan', 'incorporated_count': 4, 'acquired_count': 7 }]
So far I managed to do this
qs = CustomerInformation.objects.all().values('incorporation_country__country').annotate(inc_count=Count('incorporation_country')).annotate(acq_count=Count('acquired_country'))
Which has a major loophole as in the case where the eg. Japan appears a total of 2 times in the acquired_country field, but 0 times in the incorporation_country field, Japan would not be annotated as the values depend on incorporation_country. I tried adding both incorporation_country and acquired_country within the fields but the output was wrong.
All help is appreciated, thanks all!

Django ORM - Join many-to-many with through table

I have 3 models:
Task: A task, can be assigned to many employees.
AssignedUser: (Bridge table) Store employees assigned to tasks
completed: Indicate whether the employee has done the assigned task yet.
Employee: An employee, can work on many tasks. (Extends from Django's user model)
class Task(models.Model):
name = models.CharField(max_length=50)
detail = models.TextField()
employees = models.ManyToManyField('Employee', through='AssignedUser', related_name='tasks')
class AssignedUser(models.Model):
task = models.ForeignKey(task, on_delete=models.CASCADE)
employee = models.ForeignKey(Employee, on_delete=models.CASCADE)
completed = models.BooleanField(default=False)
class Employee(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
department = models.ForeignKey(to=Department, on_delete=models.CASCADE, null=False)
# Note - department field is not relevant to this question.
If I want to select a task name and the assigned employee's username who hasn't done the task yet (completed=False) how can I do that with Django's ORM?
Here's the equivalent SQL I wrote for the desired result which I have no idea how to achieve that with Django's ORM.
SELECT t.name, u.username
FROM appname_task t
JOIN appname_assigneduser au
ON (t.id = au.work_id AND au.completed = false)
JOIN appname_employee ae
ON (au.employee_id = ae.id)
JOIN auth_user u
ON (ae.user_id = u.id)
You can obtain this with:
from django.db.models import F
Task.objects.filter(assigneduser__completed=False).values(
'name', username=F('employees__user__username')
)