I have following Schema in Django with PostgreSQL.
Class Person (models.Model):
name = models.CharField(max_length=255)
email= models.CharField(max_legth = 255)
created_at = models.DateTimeField()
Class PersonTask(models.Model):
person = models.ForeignKey(Person)
title = models.TextField()
created_at = models.DateTimeField()
Class PersonHistory(models.Model):
person = models.ForeignKey(Person)
note = models.TextField()
created_at = models.DateTimeField()
Now I need to query the DB like all values of Person with latest PersonTask__title as max_task and latest PersonHistory__note as max_note
Eg:
<Queryset: [
{name: "abc" ,email:"abc#gmail.com",created_at :"2019-01-02", max_task:"This is my latest tasktitle" , max_note: "This is my latest history note"},
{name: "abcd" ,email:"abcd#gmail.com",created_at :"2019-03-02", max_task:"This is my latest tasktitle for abcd" , max_note: "This is my latest history note for abcd"}
]>
But, I could max get is either id of Latest Task and Latest History by
Person.objects.filter(customer_id= 1).\
annotate( max_task = Max('persontask')).\
annotate(max_note = Max('personhistory')).\
order_by('-id')
Or a random task or note texts using below query
Person.objects.filter(customer_id= 1).\
annotate( max_task = Max('persontask__title')).\
annotate(max_note = Max('personhistory__note')).\
order_by('-id')
How this can be tackled??
As you did not mention the ForeignKeys between these models, I suspect that Task and History have FK to Person in field named person.
I would use Subquery with combination of OuterRef to tackle this query
from django.db.models import OuterRef, Subquery
result = (
Person.objects
.filter(customer_id=1)
.annotate(
task_title=Subquery(Task.objects.filter(person=OuterRef('pk')).order_by('-created_at').values('title')[:1]),
history_note=Subquery(HistoryNote.objects.filter(person=OuterRef('pk')).order_by('-created_at').values('note')[:1])
)
.order_by('-id')
)
If tasks and history are related with person you need a relationship between your models.
something like this.
class PersonTask(models.Model):
person = models.ForeignKey(Person, related_name="tasks", on_delete=models.CASCADE)
title = models.TextField()
created_at = models.DateTimeField()
class PersonHistory(models.Model):
person = models.ForeignKey(Person, related_name="histories", on_delete=models.CASCADE)
note = models.TextField()
created_at = models.DateTimeField()
And then you could get last task and last history:
person = Person.objects.get(name="name")
person.tasks.last()
person.histories.last()
Related
I've the following model:
class Quiz(models.Model):
name = models.CharField(max_length=255)
month = models.DateField()
class Question(models.Model):
title = models.CharField(max_lenght=255)
category = models.CharField(max_length=255)
status = models.CharField(max_length=255, status=(('Pending', 'Pending'), ('Approved', 'Approved'))
class Contest(models.Model):
quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE)
questions = models.ManyToManyField(Question, related_name='contest_questions')
Now I want to get list of quizes with all questions whose status=Pending?
Any help would be much appreciated!
Another approach is query directly from the M2M table using values_list():
quiz_ids = list(Contest.objects.filter(questions__status='Pending').values_list('quiz__id', flat=True))
quiz_query = Quiz.objects.filter(id__in=quiz_ids)
I have 3 django tables connected like this:
Is there anyway to make a query for table Table that will get id_equip from table equip?
models.py
class Vendor(models.Model):
vendor_name = models.CharField(max_length=50)
def __str__(self):
return self.vendor_name
class Equipment(models.Model):
equipment_name = models.CharField(max_length=50)
id_vendor = models.ForeignKey(Vendor, on_delete=models.CASCADE, default=None)
def __str__(self):
return self.equipment_name
class Table(models.Model):
table_name = models.CharField(max_length=100)
id_vend = models.ForeignKey(Vendor, on_delete=models.CASCADE, default=None)
id_equip = models.ManyToManyField(Equipment)
This part of the django docs is relevant and helpful, I definitely recommend your review at least that section and ideally the whole page.
Your models are already denormalized as evidenced by Table.id_equip which relates to Equipment so you could do:
table = Table.objects.get(SOME_FILTER)
equipment_ids = list(table.id_equip.all().values_list('id', flat=True))
If you wanted to go through the vendor I'd suggest:
table = Table.objects.get(SOME_FILTER)
equipment_ids = list(Equipment.objects.filter(vendor_set__table_set=table).values_list('id', flat=True))
I would recommend that you don't name your relationship fields with id_. With an ORM, these fields should represent the instances of the Model they are mapping to. For example:
class Table(models.Model):
name = models.CharField(max_length=100)
vendor = models.ForeignKey(Vendor, on_delete=models.CASCADE, default=None)
equipment = models.ManyToManyField(Equipment)
If you're trying to create the model on top of an existing table, you can make use of the db_column parameter when defining the field.
vendor = models.ForeignKey(Vendor, on_delete=models.CASCADE, default=None, db_column="id_vend")
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.
Hi I am facing difficulty with a query in Django ORM:
I have two models:
class Student(models.Model):
name = models.CharField(max_length=200)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class choices:
CHOICES = {(0, "Present"),(1, "Absent")}
class Attendance(models.Model):
student = models.ForeignKey(Student, on_delete=models.CASCADE,
related_name='attendance')
date = models.DateField()
state = models.PositiveSmallIntegerField(choices=choices.CHOICES)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
I want to get all the students who were present on their respective latest dates of attendance.
I have a solution that requires three separate database queries
First get the latest Attendance id for each student and get the latest Attendance id where the status is present.
latest_attendances = Attendance.objects.order_by('student', '-date').distinct('student').values_list('id', flat=True)
latest_present_attendances = latest_attendances.filter(state=0)
Using set arithmetic we can get all the Attendance ids that are common to both (latest and "present")
set(latest_attendances) & set(latest_present_attendances)
Then pass it to a Student query
Student.objects.filter(attendance__in=set(latest_attendances) & set(latest_present_attendances))
In the following model:
class Product(models.Model):
name = models.CharField(max_length = 255)
created_in = models.DateTimeField(auto_now=True)
class Price(models.Model):
product = models.ForeignKey(Product)
price = models.DecimalField(max_digits=6, decimal_places=2)
created_in = models.DateTimeField(auto_now=True)
I want to do things like this:
products = Product.objects.filter(price__gte=Decimal(10)) #It considered all prices I just need the last one
How do i query a product considering only the last price "created_in" related?
Thanks!!
latest() is what you need:
Returns the latest object in the table, by date, using the field_name
provided as the date field.
products = Product.objects.filter(price__gte=Decimal(10)).latest('price__created_in')