Pk fields always ends up in resulting query GROUP BY clause - python

I have my model hierarchy defined as follows:
class Meal(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
discount_price = models.DecimalField(blank=False, null=False, decimal_places=2, max_digits=4)
normal_price = models.DecimalField(blank=True, null=True, decimal_places=2, max_digits=4)
available_count = models.IntegerField(blank=False, null=False)
name = models.CharField(blank=False, null=False, max_length=255)
active = models.BooleanField(blank=False, null=False, default=True)
class Order(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
number = models.CharField(max_length=64, blank=True, null=True)
buyer_phone = models.CharField(max_length=32, blank=False, null=False)
buyer_email = models.CharField(max_length=64, blank=False, null=False)
pickup_time = models.DateTimeField(blank=False, null=False)
taken = models.BooleanField(blank=False, null=False, default=False)
class OrderItem(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='items')
meal = models.ForeignKey(Meal, on_delete=models.CASCADE)
amount = models.IntegerField(blank=False, null=False, default=1)
I'm trying to get some statistics about orders and I came up with django orm call that looks like this:
queryset.filter(created_at__range=[date_start, date_end])\
.annotate(price=Sum(F('items__meal__discount_price') * F('items__amount'), output_field=DecimalField()))
.annotate(created_at_date=TruncDate('created_at'))\
.annotate(amount=Sum('items__amount'))\
.values('created_at_date', 'price', 'amount')
The above however doesn't give me the expected results, because for some reason the id column still ends up in the GROUP BY clause of sql query. Any help with that?

To make it work I had to do the following:
qs.filter(created_at__range=[date_start, date_end])\
.annotate(created_at_date=TruncDate('created_at'))\
.values('created_at_date')\
.annotate(price=Sum(F('items__meal__discount_price') * F('items__amount'),
output_field=DecimalField()))
.annotate(amount=Sum('items__amount'))
Which kind of makes sense - I pull only the created_at field, transform it and then annotate the result with two other fields.

Related

How to filter product by its Attribute in Django - Django?

I'm working on a Django Ecommerce project where product has several attributes like. size, color( A single product can have multiple attributes with different size and color). No i'm trying to filter products using django_filters but unable to filter by its attributes.
Product Model:
class Product(models.Model):
variations = (
('None', 'None'),
('Size', 'Size'),
)
name = models.CharField(max_length=200, unique=True)
store = models.ManyToManyField(Store)
slug = models.SlugField(null=True, blank=True, unique=True, max_length=500)
sku = models.CharField(max_length=30, null=True)
tax = models.IntegerField(null=True, blank=True)
stock = models.CharField(max_length=10, null=True)
variations = models.CharField(choices=variations, max_length=20)
short_description = models.CharField(max_length=500, null=True)
details = RichTextUploadingField(null=True, blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
discounted_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
image = models.ImageField(upload_to='product/images', default='product.png', null=True,
blank=True)
image_one = models.ImageField(upload_to='product/images', null=True, blank=True)
image_two = models.ImageField(upload_to='product/images', null=True, blank=True)
image_three = models.ImageField(upload_to='product/images', null=True, blank=True)
image_four = models.ImageField(upload_to='product/images', null=True, blank=True)
image_five = models.ImageField(upload_to='product/images', null=True, blank=True)
tags = models.ManyToManyField(Tags)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True,
related_name='products')
status = models.CharField(max_length=20, choices=(('Active', 'Active'), ('Inactive',
'Inactive')))
brand = models.ForeignKey(Brand, on_delete=models.PROTECT, blank=True, null=True)
offer = models.ForeignKey(Offer, on_delete=models.CASCADE, null=True,
blank=True) # This is used only for filtration
Product attribute model
class ProductAttribute(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
size = models.ForeignKey(Size, on_delete=models.CASCADE, null=True, blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2, validators=
[MinValueValidator(1)])
discounted_price = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
stock = models.CharField(max_length=10, null=True)
The standard approach would be to define the attributes in the "Product" model. However, if you insist on doing this, the code will be:
filtered_ProductAttributes=ProductAttribute.objects.filter(size="12")
products=[filtered_ProductAttribute.product for filtered_ProductAttribute in filtered_ProductAttributes]
As you can see the code seems very inefficient, therefore, as was suggested in the beginning put the attributes in the "Product" model and you will have:
products=Product.objects.filter(size="12")
Refining your model will help you to filter.
With my experience following model approach will be more suitable:
class Attributes(models.Model):
name = models.CharField(max_length=50, default=None)
slug = models.SlugField(max_length=200, unique=True,null=True)
class AttributeTerms(models.Model):
name = models.CharField(max_length=50, blank =True)
attribute = models.ForeignKey(Attributes, on_delete=models.CASCADE)
class Products(models.Model):
name = models.CharField(max_length=250,null=True, blank=True,)
slug = models.SlugField(max_length=200, unique=True,null=True)
class ProductAttribute(models.Model):
product = models.ForeignKey(Products,on_delete=models.CASCADE, related_name='attributes', default=None)
attributes = models.ForeignKey(Attributes,on_delete=models.CASCADE, related_name='attributes', default=None)
values = models.ForeignKey(AttributeTerms, on_delete=models.CASCADE, related_name='attributes', default=None)
class ProductVariant(models.Model):
product = models.ForeignKey(Products,on_delete=models.CASCADE)
variant = models.ForeignKey(ProductAttribute,on_delete=models.CASCADE, null = True, default=None)
stock = models.IntegerField(default=None)
stock_threshold = models.IntegerField()
price = models.DecimalField(max_digits=10, decimal_places=2)
sku = models.CharField(max_length= 250, default=None)
sale_price = models.DecimalField(max_digits=10, decimal_places=2)

Django - Getting an object from an object

I'm trying to get the object "Book" from prommotion. Book is a ForeignKey in "prommotion", and I filtered all the prommotions that are active. I need to get the "Book" object from the Prommotion if its active and return it.
(And I know promotion is spelled wrong)
Views:
class Book_PrommotionViewSet(viewsets.ViewSet):
def list(self, request):
queryset = Prommotion.objects.filter(active=True)
serializer = PrommotionSerializer(queryset, many=True)
return Response(serializer.data, HTTP_200_OK)
Prommotion Model:
class Prommotion(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
precent = models.DecimalField(decimal_places=2, max_digits=255, null=True, blank=True)
active = models.BooleanField(default=False)
date_from = models.DateField()
date_to = models.DateField()
book = models.ForeignKey(Book, on_delete=models.SET_NULL, null=True, blank=True)
country = models.ForeignKey(Country, on_delete=models.SET_NULL, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = 'Prommotion'
verbose_name_plural = 'Prommotions'
Book Model:
class Book(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField(max_length=255, null=True, blank=True)
author = models.ForeignKey(Author, on_delete=models.SET_NULL, null=True, blank=True)
price = models.DecimalField(decimal_places=2, max_digits=255)
published = models.DateField()
edition = models.CharField(max_length=255)
isbn_code = models.CharField(max_length=255)
pages = models.IntegerField(blank=True, null=True, default=0)
description = models.TextField(null=True, blank=True)
cover = models.CharField(max_length=30, choices=Cover.choices(), default=None, null=True, blank=True)
genre = models.CharField(max_length=30, choices=Genre.choices(), default=None, null=True, blank=True)
language = models.CharField(max_length=30, choices=Language.choices(), default=None, null=True, blank=True)
format = models.CharField(max_length=30, choices=Format.choices(), default=None, null=True, blank=True)
publisher = models.CharField(max_length=30, choices=Publisher.choices(), default=None, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
class Meta:
verbose_name = 'Book'
verbose_name_plural = 'Books'
The first way to get all Books that are related to your active promotions is to extract the book ids from the queryset and pass it to a Book filter
active_promotions = Prommotion.objects.filter(active=True)
Book.objects.filter(id__in=active_promotions.values('book_id'))
Or simply filter books with active promotions by using the double underscore syntax to follow relationships
Book.objects.filter(prommotion__active=True).distinct()

Accessing a parent's model attributes from child model in django

please can anyone help me out am stuck i want to access items in the item model which i have referenced in the OrderItem model but am trying to access the item from Order model
here are the models
here is the model that am using to access
class Order(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
ref_code = models.CharField(max_length=20)
items = models.ManyToManyField(OrderItem)
start_date = models.DateTimeField(auto_now_add=True)
order_date = models.DateTimeField()
ordered = models.BooleanField(default=False)
shipping_address = models.ForeignKey('Address',on_delete=models.SET_NULL,related_name='shipping_address',blank=True,null=True)
payment = models.ForeignKey('Payment',on_delete=models.SET_NULL,blank=True,null=True)
being_delivered = models.BooleanField(default=False)
received = models.BooleanField(default=False)
refund_requested = models.BooleanField(default=False)
refund_granted = models.BooleanField(default=False)
Here is the parent model which i want to access
class OrderItem(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.CASCADE)
ordered_order = models.BooleanField(default=False)
item = models.ForeignKey(Item, on_delete=models.CASCADE)
quantity = models.IntegerField(default=1)
and the item am trying to access must in this model
class Item(models.Model):
title = models.CharField(max_length=100)
price = models.IntegerField()
discount_price = models.IntegerField(blank=True, null=True)
category = models.ForeignKey(Categories, on_delete=models.DO_NOTHING)
tags = models.ManyToManyField(Tags, related_name='itemTags')
slug = models.SlugField(max_length=200, unique=True)
size = models.CharField(blank=True, null=True, max_length=5)
model = models.CharField(max_length=100, blank=True, null=True)
description = models.TextField()
quantity = models.IntegerField(default=1)
image = models.ImageField()
color = models.CharField(max_length=100, blank=True, null=True)
brand = models.CharField(max_length=100, blank=True, null=True)
Display = models.DecimalField(max_digits=100,
decimal_places=1,
blank=True,
null=True)
ram = models.CharField(max_length=100, blank=True, null=True)
material = models.CharField(max_length=100, blank=True, null=True)
hdd = models.CharField(max_length=100, blank=True, null=True)
size = models.CharField(max_length=100, blank=True, null=True)
lens = models.CharField(max_length=100, blank=True, null=True)
created_at = models.DateField(auto_now_add=True)
i was trying with this but seem not to work
recent_orders=Order.objects.filter(user=2)
for item in recent_orders:
print(item.items.item.title)
can anyone help me out Please!!!
You need thi code:
recent_orders=Order.objects.filter(user=2)
for order in recent_orders:
for order_item in order.items:
print(order_item.item.title)
try to give different names to all your variables, this will make programming more effective

How to make a "SELECT" of a model in django?

I am new to django and I am trying to make a django view that will bring me certain values ​​from two models, this would be accomplished by doing a join if done directly in sql. What I intend to do with the obtained data is return it as JSON and use this json in an html page. I just don't know how to structure or if there is any way to get the data like sql.
Model device
class device(models.Model):
device_name = models.CharField(max_length=50, unique=True, help_text='Station Name', validators=[validate_slug])
parent_area_id = models.ForeignKey('area', on_delete=models.CASCADE, null=True, help_text='Parent Area')
f2cuid = models.CharField(max_length=100, unique=True, validators=[validate_slug])
ip_address = models.GenericIPAddressField(protocol='both', unpack_ipv4='True', default='127.0.0.1', blank=False, null=False)
tower_ip_address = models.GenericIPAddressField(protocol='both', unpack_ipv4='True', default='127.0.0.1', blank=True, null=True)
layered_tower = models.BooleanField(default=False, blank=True, help_text='Check if tower is multilayer')
layer = models.CharField(max_length=1, unique=False, null=True, default=None, help_text='Layer', choices=layer_choices)
target_oee = models.DecimalField(validators=[MinValueValidator(0), MaxValueValidator(100)], help_text='OEE Target', decimal_places=2, max_digits=6, default=0)
target_availability = models.DecimalField(validators=[MinValueValidator(0), MaxValueValidator(100)], help_text='Availability Target', decimal_places=2, max_digits=6, default=0)
target_performance = models.DecimalField(validators=[MinValueValidator(0), MaxValueValidator(100)], help_text='Performance Target', decimal_places=2, max_digits=6, default=0)
target_quality = models.DecimalField(validators=[MinValueValidator(0), MaxValueValidator(100)], help_text='Quality Target', decimal_places=2, max_digits=6, default=0)
Model notification_radio
class notification_radio(models.Model):
device_id = models.ForeignKey('device', on_delete=models.CASCADE, null=False)
event_id = models.ForeignKey('event', on_delete=models.CASCADE, null=False)
to_address = models.CharField(null=False, blank=False, max_length=100)
message = models.CharField(null=False, blank=False, max_length=100, default='ANDON ALERT')
notification_type = models.CharField(null=False, blank=False, choices=notification_type_choices, max_length=100)
notification_id = models.IntegerField(null=False)
requested_date = models.DateTimeField(null=True)
processed = models.BooleanField(default=False)
processed_date = models.DateTimeField(null=True)
Sentence SQL
SELECT
`and`.`device_name` AS `device_name`,
COUNT(`anr`.`device_id_id`) AS `notif_sended`
FROM
(`andon_notification_radio` `anr`
JOIN `andon_device` `and` ON ((`anr`.`device_id_id` = `and`.`id`)))
WHERE
(`anr`.`processed` = 1)
GROUP BY `device_name`
VIEW Django
def notif_count_by_station(request):
data = notification_radio.objects.all() \
device.objects.all()
return JsonResponse(list(data), safe=False)
This is how you would expect to get the JSON, you would get the device name and the notif_sended grouped by the device_name, it would output the notifications sent by each device_name.
Regards.
You can perform such a query with:
from django.db.models import Count
def notif_count_by_station(request):
data = device.objects.values('device_name').filter(
notification_radio__processed=1
).annotate(
notif_sended=Count('notification_radio')
)
return JsonResponse({'data': list(data)})
Please do not return data wrapped in an outer list, since it can be victim to cross-site request forgery.
i don't know what exactly you are looking foor, but if oy want to get device name and device id where processed =1.
data = Notification_radio.objects.filter(processed=1).values('device_id __device_name').annotate(total=Count('device_id'))

Django join multiple models using django ORM

I googled and read many articles but got confused in multiple table join.
My models looks like-
class ProductCategory(models.Model):
category_name = models.CharField(max_length=200,blank=True, null=True, unique=True)
category_image = models.ImageField(upload_to='category', null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True)
updated_at = models.DateTimeField(auto_now=True, blank=True, null=True)
status = models.CharField(max_length=10, default='Active', choices=status)
def __unicode__(self):
return '%s' % ( self.category_name)
class ProductSubCategory(models.Model):
category = models.ForeignKey(ProductCategory)
sub_category_name = models.CharField(max_length=200,blank=True, null=True, unique=True)
created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True)
updated_at = models.DateTimeField(auto_now=True, blank=True, null=True)
sub_category_image = models.ImageField(upload_to='subcategory', null=True, blank=True)
status = models.CharField(max_length=10, default='Active', choices=status)
def __unicode__(self):
return '%s' % ( self.sub_category_name)
class Product(models.Model):
category = models.ForeignKey(ProductCategory)
sub_category = models.ForeignKey(ProductSubCategory)
product_name = models.CharField(max_length=200,blank=True, null=True)
product_price = models.FloatField(default=0)
created_at = models.DateTimeField(auto_now_add=True, blank=True, null=True)
updated_at = models.DateTimeField(auto_now=True, blank=True, null=True)
# is_discountable = models.CharField(max_length=3, default='Yes', choices=option)
status = models.CharField(max_length=10, default='Active', choices=status)
def __unicode__(self):
return '%s' % ( self.product_name)
class ProductColor(models.Model):
product = models.ForeignKey(Product)
product_color = models.ForeignKey(Color, related_name='product_color_id', blank=True, null=True)
product_size = models.ForeignKey(Size, related_name='product_size_id', blank=True, null=True)
class ProductImages(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, blank=True, null=True)
product_image = models.ImageField(upload_to='images', null=True, blank=True)
Now in views, I want to get the product filters according to category and sub-category having all the images and colors. Query is something like-
SELECT product.*, productcolor.*, productimage.* FROM product
LEFT JOIN productcolor ON productcolor.product_id = product.id
LEFT JOIN productcolor.product_id = product.id
LEFT JOIN productimage ON productimage.product_id = product.id
WHERE product.category_id=1 and product.sub_category_id=1
Accordingly to your SQL, this will do.
Product.objects.filter(category=<category>, sub_category=<sub_category>) \
.prefetch_related('productcolor_set', 'productimages_set')
This query will prefetch all (with .prefetch_related()) ProductColor and ProductImages related to Product that have your <category> and <sub_category>, they would be stored in productcolor_set and productimages_set respectively.
Also i would suggest to rename your ProductImages model to ProductImage because it represents only one product image.
ProductCategory.productsubcategory_set.all()

Categories