Combining django queryset objects having similar values - python

It's an eCom site that I am working on. I need to filter results based on email, as in obtain all those orders placed by this email account. On executing the query OrderItem.objects.filter(order__email='john#john.com').select_related('order') I get queryset object for each product. So if an order had multiple products then each product is a set on it's own. What I need is all items combined in the one set for an order ID so that I can iterate through them in the template.
OrderItem.objects.filter(order__email='john#john.com').select_related('order').values('order_id').annotate(ct=Count('pk')) - This query gets all those IDs and shows how many sets are there per ID. Likewise I just need the results but with other fields like created_date, order_id, transaction_id, product.
I have explored the django documentation but don't seem to find an appropriate solution. Another option is to manually loop through each item in the queryset and segregate based on the order_id having multiple items but then again that doesn't seem efficient. So any thoughts on how to proceed would be great.
Models are below:
class Order(models.Model):
payment_date = models.DateTimeField(blank=True, null=True)
created_date = models.DateTimeField(auto_now_add=True)
transaction_id = models.CharField(max_length=256, blank=True, null=True)
email = models.EmailField(max_length=128, blank=True, null=True)
def __str__(self):
return "Order #{},Date:{}".format(self.id,self.created_date)
class OrderItem(models.Model):
product = models.ForeignKey(PRODUCT_VARIANT_MODEL, on_delete=models.DO_NOTHING)
quantity = models.IntegerField(default=1)
order = models.ForeignKey(Order, related_name='items', on_delete=models.CASCADE)
def __str__(self):
return "Quantity:{},Item:{},{}".format(self.quantity, self.product.get_product_title(),self.order)
Result of query 1:(There are 2 orders placed by this email ID) OrderItem.objects.filter(order__email='john#john.com').select_related('order')
<QuerySet [<OrderItem: Quantity:3,Item:sampleitem 1,Order #21,Date:2019-06-07 06:14:22.929052+00:00>, <OrderItem: Quantity:1,Item:sampleitem 91,Order #22,Date:2019-06-24 05:33:16.509479+00:00>, <OrderItem: Quantity:2,Item:sampleitem 44,Order
#22,Date:2019-06-24 05:33:16.509479+00:00>, <OrderItem: Quantity:1,Item:sampleitem 8,Order #22,Date:2019-06-24 05:33:16.509479+00:00>]>
Result of query 2:
OrderItem.objects.filter(order__email='john#john.com').select_related('order').values('order_id').annotate(ct=Count('pk'))
<QuerySet [{'order_id': 21, 'ct': 1}, {'order_id': 22, 'ct': 3}]>

Related

How to model many-to-many database with 3 tables

I'm working on a django backend and I'm trying to model a database and want to do it the best practice way.
I need a "User" table, a "Portfolios" table and a "Stocks" table. A user can have multiple portfolios which consist of multiple stocks.
This is my code so far:
class User(models.Model):
user_id = models.AutoField(primary_key=True)
username = models.CharField(max_length=25)
in_cash = models.DecimalField(max_digits=15, decimal_places=2)
class Portfolios(models.Model):
portfolio_id = models.AutoField(primary_key=True)
user_id = models.ForeignKey("User", on_delete=models.CASCADE)
stock_id = models.ForeignKey("Stocks", on_delete=models.CASCADE)
buy_datetime = models.DateTimeField(default=datetime.now, blank=True)
number_of_shares = models.IntegerField()
class Stocks(models.Model):
stock_id = models.AutoField(primary_key=True)
stock_symbol = models.CharField(max_length=12)
In my head I would have an entry in the "Portfolios" table for each stock of a portfolio.
So "Portfolios" would look like
portfolioid 1, userid: 1, stockid: 1, buydate: 12.01.2019, shares: 20
portfolioid 1, userid: 1, stockid: 2, buydate: 19.02.2020, shares: 41
So there is one portfolio, which contains two stocks. But overall that doesn't seem right. If used like in my example above I can't have the portfolioid as a primary key, how can I improve this?
Thanks for your time
What confused me is the name portfolio, which I would call position. Your initial code was correct, although I changed it a bit, removing AutoField which is probably not needed, using a OneToOneField to connect a Customer to a User, removing the s at the end of class names, which are templates, and therefore should be singular, nor plural, adding price to the Stock. And finally changing Portfolio, which should be the sum of all the Positions.
from django.conf import settings
class Customer(models.Model):
customer = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,)
in_cash = models.DecimalField(max_digits=15, decimal_places=2)
def __str__(self):
return self.customer.username
class Position(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
stock = models.ForeignKey('Stock', on_delete=models.CASCADE)
number_of_shares = models.IntegerField()
buy_datetime = models.DateTimeField(default=datetime.now, blank=True)
def __str__(self):
return self.customer.customer.username + ": " + str(self.number_of_shares) + " shares of " + self.stock.stock_symbol
class Stock(models.Model):
stock_symbol = models.CharField(max_length=12)
price = models.DecimalField(max_digits=10, decimal_places=2)
def __str__(self):
return self.stock_symbol
In my head I would have an entry in the "Portfolios" table for each
stock of a portfolio. So "Portfolios" would look like
portfolioid 1, userid: 1, stockid: 1, buydate: 12.01.2019, shares: 20
portfolioid 1, userid: 1, stockid: 2, buydate: 19.02.2020, shares: 41
So there is one portfolio, which contains two stocks. But overall that
doesn't seem right. If used like in my example above I can't have the
portfolioid as a primary key, how can I improve this?
You are correct, except that should be applied to a Position, each of which is unique, not the Portfolio, which is all the Positions the Customer has.
As in usual many-to-many cases, you will need to create an intermediary table which is also called junction table/association table. Associative Entity
Your users are going to have several portfolios:
class UserPortfolio(models.Model):
user_id = models.ForeignKey("User")
portfolio_id = models.ForeignKey("Portfolio")
The portfolios will have multiple stocks in them:
class PortfolioStock(models.Model):
portfolio_id = models.ForeignKey("Portfolio")
stock_id = models.ForeignKey("Stock")
Now a user can have several portfolios, and those portfolios will include several stocks. In order to get access to the corresponding stocks for a user, you will need to join the tables.

Django: Queryset filtering based on min max set in another table

Consider the following models:
Class ExamResults:
...
Marks = models.IntegerField(default=0, null=True, blank=True)
Course = models.CharField(max_length=255, null=True)
Academic_year = models.CharField(max_length=255, null=True)
Class ExamSetting:
...
GradeAThreshold = models.IntegerField(default=0, null=True, blank=True)
GradeBThreshold = models.IntegerField(default=0, null=True, blank=True)
...
Course = models.CharField(max_length=255, null=True)
Academic_year = models.CharField(max_length=255, null=True)
Now I have an API to search/get results from ExamResults for all students. The API works fine and I use Q filters to filters the results. For e.g.
...
year_contains = self.request.GET.get("year_contains", "")
if year_contains:
q_filt |= Q(Academic_year__icontains=year_contains)
queryset = queryset.filter(q_filt)
...
Now I have a requirement to filter the queryset for the following condition:
List of exam results where the Marks exceed GradeAthreshold
List of exam results where the Marks are less than GradeAthreshold and exceed GradeBThreshold and so on
What would be the best way of doing this? The ExamResults table and ExamSetting has two common fields which can narrow down the thresholds. For e.g. I use the below code in serializer to check if the result has Grade A or not:
setting = ExamSetting.objects.filter(Academic_year=obj.Academic_year, Course=obj.Course, is_active=True).first()
if obj.Marks >= setting.GradeAThreshold:
# Grade A
...
This does work and I do get the results with grades. Now how do I add something like this in the queryset so that I can filter the results for Grade A or B results?.
As you mentioned ExamSetting and ExamResult both have common fields; assuming proper data integrity, you can pivot based on one of those.
For example, if you want all exam results with grade A or above for a similar course:
setting = ExamSetting.objects.filter(
Academic_year=obj.Academic_year,
Course=obj.Course,
is_active=True
).first()
query = Q(Course=setting.Course) & Q(Mark__gte=setting.GradeAThreshold)
ExamResults.objects.filter(query)

Need help in Django Orm query

I have 3 models and they are as follow
class Table(models.Model):
waiter = models.ForeignKey(get_user_model(), on_delete=models.CASCADE,
related_name='restaurant_table')
table_no = models.IntegerField()
objects = TableManager()
class Order(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
food = models.ManyToManyField(OrderFood, related_name='ordered_food')
order_status = models.ForeignKey(OrderStatus, on_delete=models.CASCADE)
table = models.ForeignKey(Table, on_delete=models.CASCADE)
datetime = models.DateTimeField(default=now)
class OrderStatus(models.Model):
CHOOSE = (
('Received', 'Received'),
('Cooking', 'Cooking'),
('WaiterHand', 'In Waiter Hand'),
('Delivered', 'Delivered'),
('Paid', 'Payment Completed'),
('Rejected', 'Rejected')
)
status = models.CharField(max_length=30, null=False, blank=False, choices=CHOOSE)
created_at = models.DateTimeField(auto_now=True)
updated_at = models.DateTimeField()
Actually I am creating a restaurant management system. So here a restaurant has tables associated with a or more waiter. But I need a new feature that is table status. I mean when an order is actively associated with the table that means that table is booked. Actually that is not a problem as I can do that in many ways.
One way is I will count the active order associated with this table and if I found any active order I will return the table is booked.
Another way is I will add an extra field with the table that is a flag. This flag store status of tables is booked or not I mean the boolean field.
But my question is not the solution. My question which one is better or there are any other good solutions. Please explain it briefly I want to know which solution is better and why.
you can put #property function under class Table which you can use directly with any table objects, in templates also.
#property
def check_table_status(self):
status = 'Not Booked'
if self.order_set.all().exists():
status = 'Booked'
return status

Django - how to decrease DB queries when iterating

I have a model ProductFilter in the project. An object of this model is a set of lookups defining a queryset. It's like a group of products - but just as a filter, not connected with a ForeignKey (ManyToManyField).
The product can be filtered in multiple ProductFilters and the first is taken to account.
It works like this:
User define their filters. Then assign to each filter some pricing (and other) rules.
For example filter with the name "Products under 10 USD" - rule - increase the price by 10%.
The problem is that when I render products or export them. I have to check for every product if they belong to any filter which means a lot of DB queries. There can be 100k products.
Do you have any ideas how to make it faster?
class Product(...):
...
#property
def price(self):
for filter in self.user.filters():
if filter.get_queryset().filter(pk=self.pk).exists():
return filter.rule.apply_rule(self.price)
class ProductFilter(BaseTimeStampedModel):
project = models.ForeignKey('projects.UserEshop', related_name='product_filters', on_delete=models.CASCADE)
order = models.IntegerField(default=0)
name = models.CharField(max_length=64)
categories = models.ManyToManyField('FeedCategory', blank=True)
brands = models.ManyToManyField('Brand', blank=True)
manufacturers = models.ManyToManyField('Manufacturer', blank=True)
import_price_no_vat_max = models.DecimalField(decimal_places=2, max_digits=64, null=True, blank=True)
import_price_no_vat_min = models.DecimalField(decimal_places=2, max_digits=64, null=True, blank=True)
name_icontains = models.CharField(max_length=128, null=True, blank=True)
def get_queryset(self):
# products for this project
qs = Product.objects.filter(source__user_eshop=self.project)
if self.categories.exists():
qs = qs.filter(category__in=self.categories.all().get_descendants(include_self=True))
if self.brands.exists():
qs = qs.filter(brand__in=self.brands.all())
if self.manufacturers.exists():
qs = qs.filter(manufacturer__in=self.manufacturers.all())
if self.import_price_no_vat_max is not None:
qs = qs.filter(import_price_no_vat__lte=self.import_price_no_vat_max)
if self.import_price_no_vat_min is not None:
qs = qs.filter(import_price_no_vat__gte=self.import_price_no_vat_min)
if self.name_icontains:
qs = qs.filter(name__icontains=self.name_icontains)
return qs
EDIT
My best idea is to create a ManyToManyField on ProductFilter to Product and everytime somethings changes I'll assign products to every filter. I'm just not sure if ManyToManyField is ok to hold thousands or hundreds of thousands products.

Django query with .order_by('title') doesn't give alphabetical order, sorts by date changed

With the following model in my Django app:
class Story(models.Model):
#1:1 Fields
title = models.CharField(max_length=100, unique=True)
tagline = models.TextField(blank=True, null=True)
contents = models.TextField()
updated = models.DateTimeField(auto_now_add=True)
slug = models.SlugField(unique=True)
full_length_book = models.BooleanField(default=False)
publish_now = models.BooleanField(default=False)
date_added = models.DateTimeField('date added to archive')
def __unicode__(self):
return self.title
class Meta:
verbose_name_plural = "Stories"
ordering = ['title']
I have several views that query the data from this model and use .order_by('title'), for example:
newlist = Story.objects.filter(tag=tag, publish_now=True).order_by('title')
The Problem: in the query results, as well as when I use the 'sort column' arrows in the django admin for the story group, it lists stories in order of last changed, and alphabetized within that ordering. I need the stories ordered firstly and solely by title, alphabetically. Why does it put recently changed entries at the end of the list?
I may be misunderstanding something about SQLite- even when I go into the database manager and call SELECT * FROM table_name ORDER BY LOWER(title) , the order is still by update first and then alphabetically within that.
edit The SQL Django is calling is as follows:
'SELECT "app_story"."id", "app_story"."title", "app_story"."tagline", "app_story"."contents", "app_story"."updated", "app_story"."slug", "app_story"."full_length_book", "app_story"."publish_now", "app_story","date_added" FROM "app_story" ORDER BY "app_story"."title" ASC'
This looks like it should order by title, so I still don't know why the order is coming up with newly edited items on the end of the list.

Categories