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)
Related
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()
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
These two classes have Foreign Key to each other and class OrderRow is representing a row which belongs to an Order, I want to know is there any way to set "rows" attribute inside class Order to show a list or query-set of all related order rows by calling it on an object or instance of class Order?
class OrderRow(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
order = models.ForeignKey('Order', on_delete=models.CASCADE)
amount = models.IntegerField(default=0)
class Order(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
order_time = models.DateTimeField(auto_now_add=True)
total_price = models.IntegerField(null=True, blank=True)
**rows = models.ForeignKey('OrderRow', on_delete=models.CASCADE, related_name='order_rows')**
You should have ForeignKey in OrderRows with related_name="rows". Like this:
class OrderRow(models.Model):
# rest of the fields
order = models.ForeignKey('Order', on_delete=models.CASCADE, related_name='rows')
Then you can use:
order = Order.objects.first()
for order_row in order.rows.all():
print(order_row)
For more information, please check the documentation.
I am using Django Rest Framework in the backend and Angular for frontend.
If the customer order multiple items, it should be be in a single invoice. For example, if the customer order apple, orange and banana, all these should be in a single invoice.
When the customer order again and it will be a new invoice.
class InvoiceItem(models.Model):
product = models.ForeignKey(
Product, on_delete=models.CASCADE, related_name='invoiceitems')
customer = models.ForeignKey(
Customer, on_delete=models.CASCADE, related_name='invoiceitems')
quantity = models.PositiveIntegerField()
date = models.DateTimeField(auto_now_add=True)
class Invoice(models.Model):
invoice_item = models.OneToOneField(
InvoiceItem, on_delete=models.CASCADE, related_name='invoice')
Now I have to link the InvoiceItem with Invoice. I thought about using post_save signals with InvoiceItem as a sender to create Invoice object and link it with the InvoiceItem.
#receiver(signals.post_save, sender=InvoiceItem)
def create_account(sender, instance, created, **kwargs):
Invoice.objects.update_or_create(invoice_item=instance)
How can I do it for multiple items?
Or there is a better way to implement my requirements?
There are multiple problems with your approach.
First of all, your
invoice_item = models.OneToOneField(
InvoiceItem, on_delete=models.CASCADE, related_name='invoice')
implies that there is only one type of Product per Invoice, i.e. having bought apples Customer can't buy also some oranges in the same Invoice.
If you try to fix that by creating ForeignKey on InvoiceItem, as others have already pointed out, you find yourself struggling with the second problem:
You attach InvoiceItem to a customer. Meaning that there's nothing stopping the system from creating a single Invoice that has 5 Oranges bought by Alice as an InvoiceItem and 7 Apples bought by Bob as another InvoiceItem, which seems wrong.
I would also move the date field from InvoiceItem to Invoice and rename it to timestamp, as I assume an Invoice is a set of Products bought together at one time.
You would end up with something like this:
class InvoiceItem(models.Model):
invoice = models.ForeignKey(
Invoice, on_delete=models.CASCADE, related_name='invoice_items')
product = models.ForeignKey(
Product, on_delete=models.CASCADE, related_name='invoice_items')
quantity = models.PositiveIntegerField()
class Invoice(models.Model):
customer = models.ForeignKey(
Customer, on_delete=models.CASCADE, related_name='invoices')
timestamp = models.DateTimeField(auto_now_add=True)
Now if you examine related_name arguments on the fields you'll see that you can do very useful things with your objects, like
customer = Customer.objects.get(id=<some_id>)
customer.invoices
will give you a QuerySet of all the Invoices belonging to a specific Customer. And having an Invoice object you can
invoice.invoice_items
to get all the items on an invoice
Or, for example, having a Product, say, 'apple', you can find all customers that ever bought an apple:
# Find all invoice_items for apple
inv_items = apple_product.invoice_items.all()
# Filter Customers
Customer.objects.filter(invoices__invoice_items__in=inv_items)
If you squint a little, you'll see that this whole structure is just a django M2M through relation (https://docs.djangoproject.com/en/3.0/topics/db/models/#extra-fields-on-many-to-many-relationships) and can be rewritten like this:
class InvoiceItem(models.Model):
invoice = models.ForeignKey(
Invoice, on_delete=models.CASCADE, related_name='invoice_items')
product = models.ForeignKey(
Product, on_delete=models.CASCADE, related_name='invoice_items')
quantity = models.PositiveIntegerField()
class Invoice(models.Model):
customer = models.ForeignKey(
Customer, on_delete=models.CASCADE, related_name='invoices')
timestamp = models.DateTimeField(auto_now_add=True)
products = models.ManyToManyField(Product, through='InvoiceItem')
So now you have Invoice directly connected to Products, and this connection also contains information about the quantity of this Product on this Invoice. I will leave the exploration of the benefits of this approach to the reader.
You should use a ForeignKey from the InvoiceItem to Invoice:
class InvoiceItem(models.Model):
...
invoice = models.ForeignKey(
Invoice, on_delete=models.CASCADE
)
...
You should be able to then remove the post_save. You'll create items like this:
>>> invoice = Invoice.objects.create(<insert arguments here>)
>>> invoice_item = InvoiceItem.objects.create(invoice=invoice, <insert other arguments>)
>>> invoice_item.invoice.pk == invoice.pk
[out] True (i.e., the invoice item is attached to the invoice)
I have three models:
class Product(models.Model):
name = models.CharField(verbose_name='Name', max_length=100, unique=True, db_index=True)
class Recipe(models.Model):
pass
class Ingredient(models.Model):
recipe = models.ForeignKey(Recipe, verbose_name='Recipe', db_index=True)
product = models.ForeignKey(Product, verbose_name='Product')
amount = models.DecimalField(verbose_name='Amount', max_digits=8, decimal_places=2)
How can I filter Recipe objects that have only the given Ingredients with the given amounts?
For ex., I need Recipes that have only the following inredients:
id = 1 and amount <= 10, and
id = 2 and amount <= 15.
If there are any other ingredients, those recipes shouldn't be returned by query.
You can make queries that span through relationship and make complex queries with q object. For example, in your case.
Recipe.objects.filter(Q(Q(ingredient_set__id=1, ingredient_set__amount__lte=10) | Q(ingredient_set__id=2, ingredient_set__amount__lte=15)))