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

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')
)

Related

django howto use select_related not using local id key

i have made two models where i would like to get them joined on the two name keys.
models.py
class MODELa (models.Model):
......
nameXX = models.CharField(_("name"), max_length=255, primary_key=True)
class MODELb (models.Model):
......
nameYY = models.CharField(_("nameYY"), max_length=255)
FKxx = models.ForeignKey(to=MODELa, on_delete=models.CASCADE, null=True)
views.py
rows = MODELb.objects.all().select_related('FKxx')
using debug toolbar, i can see the join is using the id field
LEFT OUTER JOIN "MODELa"
ON ("MODELb"."FKxx_id" = "MODELa"."nameXX")
How do i set it use the local key nameYY?

Query ManyToMany models to return number of instance of A that are linked to B and reverse?

EDIT 22/01/2021
Thanks for advices in models naming.
Here is diagram of my database: https://dbdiagram.io/d/5fd0815c9a6c525a03ba5f6f?
As you can see in this diagram, I have simplyed as Customers_Orders is in fact a ternary relationship with models comments. I decided to use an 'declared' throught models for this ternary relationship
Do I realy need to add a ManyToMany fields in Orders?
I have a look at Django's doc example with person, group and membership (https://docs.djangoproject.com/fr/3.1/topics/db/models/) and a manytomany fiels is added only in Group model, not in Person model
I have 2 models (Customers and Orders) with a declared throught models (Customers_Orders) to manage manytomany relationship.
But I did'nt understand how to query to have :
for one Customer, all its orders: How many orders were made by each customer
for one Order, all its customers: How many customers were associated for each order
I have understood, I should do:
c = Customers.objects.get(customer_id=1)
c.CustomersOrders.all() but it failled AttributeError: 'Customers' object has no attribute 'CustomersOrdersComments'
class Customers(SafeDeleteModel):
customer_id = models.AutoField("Customer id", primary_key = True)
orders = models.ManyToManyField(Orders, through = Customers_Orders, related_name = "CustomersOrders")
created_at = models.DateTimeField("Date created", auto_now_add = True)
class Orders(SafeDeleteModel):
order_id = models.AutoField("Order id", primary_key = True)
created_at = models.DateTimeField("Date created", auto_now_add = True)
class Customers_Orders(SafeDeleteModel):
order = models.ForeignKey("Orders", on_delete = models.CASCADE)
customer = models.ForeignKey("Customers", on_delete = models.CASCADE)
You can do this - Given these models:
class Customer(models.Model):
name = models.CharField(max_length=30, default=None, blank=True, null=True)
orders = models.ManyToManyField("Order", through="CustomerOrder", related_name="orders")
created_at = models.DateTimeField(auto_now_add=True)
class Order(models.Model):
created_at = models.DateTimeField(auto_now_add = True)
customers = models.ManyToManyField(Customer, through="CustomerOrder", related_name="customers")
class CustomerOrder(models.Model):
order = models.ForeignKey("Order", on_delete = models.CASCADE)
customer = models.ForeignKey("Customer", on_delete = models.CASCADE)
customer = Customer.objects.first()
# Get count of their orders
customer_orders_count = customer.orders.all().count()
order = Order.objects.first()
# Get count of order's customers
order_customers_count = order.customers.all().count()
The docs explains this quite well:
https://docs.djangoproject.com/en/3.1/topics/db/examples/many_to_many/
In your case, that would be something like:
customer = Customers.objects.get(customer_id=1) # Fetch a specific customer from DB
customer_orders = customer.orders.all() # Return a QuerySet of all the orders related to a given `customer`
c.CustomersOrders.all() can't work, because CustomersOrders is the class name of your through model, and your Customers model does not have any CustomersOrders field.

Django model order the queryset based on booleanfield True/False that related with User FK profile

I have two django models as follows:
The first one is a user profile, which has a FK to User model:
class Profile(models.Model):
PRF_user = models.OneToOneField(User, related_name='related_PRF_user', on_delete=models.CASCADE)
PRF_Priority_Support = models.BooleanField(default=False)
and the second is ticket model which has a FK to User model:
class ticket(models.Model):
ticket_status_options = [
('open', 'open'),
('wait_customer_reply', 'wait_customer_reply'),
('replied_by_staff', 'replied_by_staff'),
('replied_by_customer', 'replied_by_customer'),
('solved', 'solved'),
]
TKT_USER = models.ForeignKey(User, related_name='TKT_USER', on_delete=models.CASCADE)
TKT_DEB = models.ForeignKey('Site_departments', related_name='related_ticket_department', on_delete=models.CASCADE)
TKT_SUB = models.CharField(max_length=50, db_index=True, verbose_name="ticket subject")
TKT_BOD = models.TextField(verbose_name="ticket body")
TKT_image_attachment = models.ImageField(upload_to='TKT_img_attachment', blank=True, null=True , default=None)
TKT_CREATED_DATE = models.DateTimeField(auto_now_add=True)
TKT_UPDATED_DATE = models.DateTimeField(auto_now=True)
I want to sort the tickets based on user profile Priority_Support:
If the user profile PRF_Priority_Support is True, I want to sort it first inside my views QuerySet, otherwise (if PRF_Priority_Support is False) I want to sort it normally.
How can I do this?
You should name your model starting with a capital letter.
And for ordering the tickets, you can use something like this:
' queryset_list = ticket.objects.order_by('-TKT_USER__related_PRF_user__PRF_Priority_Support')
In filtering, when you want to span relationships, you use double underscore __ .
More on this here:
https://docs.djangoproject.com/en/3.1/topics/db/queries/#lookups-that-span-relationships
Another way is adding ordering to your model's Meta class.
For Example:
MyModel(models.Model):
class Meta:
ordering = ('-my_boolean_field ',)
Hi you should filter as follow:
Model.objects.filter(field=True) or False depending on what you need
Regards

Table as field in model

I have such models:
class Department(models.Model):
name = models.CharField(max_length=30)
schedule = models.ForeignKey('Schedule', on_delete=models.CASCADE)
class Schedule(models.Model):
post_name = models.CharField(max_length=30)
shift_start = models.TimeField(auto_now=False, auto_now_add=False)
shift_end = models.TimeField(auto_now=False, auto_now_add=False)
Each department have a schedule - some [post_name, shift_start, shift_end] lines for each post. If use ForeignKey there will be only one line instead of a list. Is it possible to create some Schedule tables and link each with certain Department?
Foreign key defines a 1 to N relationship between your models. If I understood right you would like 1 department to have N schedules. To achieve this each schedule should have a foreign key defining which department it belongs to.
So you should use ForeignKey for that, but put it in your Schedule model.
Here is how it should look:
class Department(models.Model):
name = models.CharField(max_length=30)
class Schedule(models.Model):
post_name = models.CharField(max_length=30)
shift_start = models.TimeField(auto_now=False, auto_now_add=False)
shift_end = models.TimeField(auto_now=False, auto_now_add=False)
department = models.ForeignKey(Department, on_delete=models.CASCADE, related_name='schedules')
Then after you've created a department and a couple of schedules for that department you can access them like so:
(lets assume the created department primary key is 1)
Department.objects.get(pk=1).schedules.all()

Get all related field in Django model

I am struggling to understand how one-to-many and many-to-many relation works in Django model. My schema looks something like this so far, I am open for suggestions to make it better.
A many-to-many relation between users and team. Also, there will be schedules that belong to a particular user of a team.
This is how my model looks like so far,
class Team(models.Model):
tid = models.AutoField(primary_key=True)
team_name = models.CharField(max_length=30)
manager_name = models.CharField(max_length=30)
class Schedule(models.Model):
sid = models.AutoField(primary_key=True)
user = models.OneToOneField(User)
date = models.DateField()
start_time = models.TimeField()
end_time = models.TimeField()
pay_rate = models.CharField(max_length=30)
location = models.CharField(max_length=50)
class BelongsTo(models.Model):
bid = models.AutoField(primary_key=True)
user = models.OneToOneField(User)
team = models.ForeignKey(Team, on_delete=models.CASCADE)
schedule = models.ForeignKey(Schedule, on_delete=models.CASCADE)
Question: I want to get the information of each user, what are their schedules and which team each schedule belongs to. How would I to do it? I have tried BelongsTo.objects.select_related().all(), but it is not working for me.
Note: I am open for suggestions, if something is wrong with my schema or model or the approach, please let me know.
BelongsTo is seems like utility table.So
BelongsTo.objects.all().values('user', 'team__team_name', 'schedule')
Your schema looks almost right, but I would modify it a little bit. In particular, I will change how Schedule is implemented. Instead of having a sid in the User Belongs To join-table, I would include the user and team in the Schedule table as foreign keys.
This is how the Django models should then look like:
class User(models.Model):
username = models.CharField(max_length = 200)
# put other fields like password etc. here
class Team(models.Model):
team_name = models.CharField(max_length=30)
manager_name = models.CharField(max_length=30)
user = models.ManyToManyField("User")
class Schedule(models.Model):
user = models.ForeignKey("User")
team = models.ForeignKey("Team")
date = models.DateField()
start_time = models.TimeField()
end_time = models.TimeField()
pay_rate = models.CharField(max_length=30)
location = models.CharField(max_length=50)
Note the following:
You don't need to have a primary key field in the models because Django adds a primary key field called pk or id automatically.
Note the absence of the User Belongs To model. Django implements join-tables like User Belongs To automatically when you use a ManyToManyField. See the Django docs on many-to-many relationships.
You also don't need on_delete = models.CASCADE on ForeignKey fields, because this is the default behavior.
To see how to get information about users, teams and schedule from this configuration of models, consult the Django documentation on making db queries. It's quite easy.