I have three tables CATEGORIES, TIPODESPESA and EXPENSE. I would like to perform a simpler and faster query to return the total expenses grouped by TYPODESPESA and by CATEGORY.
The expected result is the one below:
enter image description here
What I would like is a result in which the totals are grouped first by CATEGORY, then by TYPE EXPENSE and then show the EXPENSES.
However, for me to be able to do this, I perform the following queries below and I'm trying to simplify and make it faster, but I'm still learning a few things. If you can help with the query.
registros = CategoriaDespesa.objects.filter(
tipodespesas__tipodespesa_movimentodespesa__data__lte=data_final,
tipodespesas__tipodespesa_movimentodespesa__data__gte=data_inicio, ) \
.order_by('description')
categorias_valores = CategoriaDespesa.objects.filter(
tipodespesas__tipodespesa_movimentodespesa__data__lte=data_final,
tipodespesas__tipodespesa_movimentodespesa__data__gte=data_inicio,
tipodespesas__tipodespesa_movimentodespesa__company=company.company) \
.values('id').annotate(total=Coalesce(Sum(
F("tipodespesas__tipodespesa_movimentodespesa__valor"), output_field=FloatField()), 0.00))
tipos_valores = TipoDespesa.objects.filter(
tipodespesa_movimentodespesa__data__lte=data_final,
tipodespesa_movimentodespesa__data__gte=data_inicio,
tipodespesa_movimentodespesa__company=company.company).values('id') \
.annotate(total=Coalesce(Sum(F("tipodespesa_movimentodespesa__valor"), output_field=FloatField()), 0.00))
The classes I'm using are:
class CategoriaDespesa(models.Model):
description = models.CharField(max_length=200, help_text='Insira a descrição')
active = models.BooleanField(default=True, help_text='Esta ativo?', choices=CHOICES)
user_created = models.ForeignKey(User, on_delete=models.PROTECT)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class TipoDespesa(models.Model):
category = models.ForeignKey(
CategoriaDespesa, on_delete=models.CASCADE, related_name="tipodespesas")
description = models.CharField(max_length=200, help_text='Insira a descrição')
active = models.BooleanField(default=True, help_text='Esta ativo?', choices=CHOICES)
user_created = models.ForeignKey(User, on_delete=models.PROTECT)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Despesa(models.Model):
company = models.ForeignKey(
Company, on_delete=models.CASCADE, related_name="companies_movimentodespesa")
tipodespesa = models.ForeignKey(
TipoDespesa, on_delete=models.CASCADE, related_name="tipodespesa_movimentodespesa")
description = models.CharField(
max_length=200, help_text='Insira a descrição')
data = models.DateField(help_text="Data do Movimento",
default=datetime.now, blank=False)
valor = models.DecimalField(
max_digits=8, decimal_places=2, default=0.00, help_text='Valor')
user_created = models.ForeignKey(User, on_delete=models.PROTECT)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
My goal is to create a report that shows the levels and summed values by level.
Related
Models.py
class BaseModel(models.Model):
branch = models.ForeignKey(Branch, on_delete=models.PROTECT, blank=True, null=True)
company = models.ForeignKey(
Company, on_delete=models.PROTECT, blank=True, null=True
)
class Meta:
abstract = True
class MealMenu(BaseModel):
employee = models.ForeignKey(
Employee, on_delete=models.PROTECT, null=True, blank=True
)
item_name = models.CharField(max_length=50, null=True, blank=True)
quantity = models.PositiveIntegerField()
price = models.FloatField()
def __str__(self):
return f"{self.item_name} {self.price}"
class MealOrder(BaseModel):
RECEIVED = "Received"
PENDING = "Pending"
REJECTED = "Rejected"
MEAL_CHOICES = (
("Breakfast", "Breakfast"),
("Lunch", "Lunch"),
("Dinner", "Dinner"),
)
STATUS_CHOICES = (
(RECEIVED, "Received"),
(PENDING, "Pending"),
(REJECTED, "Rejected"),
)
id = models.UUIDField(primary_key=True, default=uuid.uuid4, null=False)
total_items = models.IntegerField(null=True, default=0)
total_amounts = models.FloatField(default=0.0)
menu = models.ForeignKey(MealMenu, on_delete=models.PROTECT)
quantity = models.PositiveIntegerField(default=1, blank=False)
meal_time = models.CharField(max_length=25, choices=MEAL_CHOICES)
employee = models.ForeignKey(Employee, on_delete=models.PROTECT)
date = models.DateField(auto_now=True)
status = models.CharField(max_length=25, choices=STATUS_CHOICES, default=PENDING)
I have two models. In First Model i have created a menu item_name,price and quantity.
In MealOrder i have foreign key MealMenu Model and created quantity field separately.
I want to select multiple items with their multiple quantities. But i can't understand the scenario.
So you could have a separate model to handle the quantity for different items in an order.
Like this:
class MealOrderItem(BaseModel):
order = models.ForeignKey(
MealOrder, on_delete=models.PROTECT, null=True, blank=True
)
quantity = models.PositiveIntegerField()
meal = ForeignKey(
MealMenu, on_delete=models.PROTECT, null=True, blank=True
)
This will help you create multiple meal menu selections for an order with each having its own quantity.
How to assign a product in ProductInStore to an instance of a Product already in store? Basically i have a scrapper and im looping through all the products and i need to first create the Product instance, and then A ProductInStore instance which is connected to Product via foreignKey. And when i try to put in ProductInStore(product=id) thats how i wanted to reference that Product, i get an error ValueError: Cannot assign "11393": "ProductInStore.product" must be a "Product" instance.
Do you have any idea how to reference it?
class Product(models.Model):
name = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, unique=True, null=False, editable=False)
created_at = models.DateTimeField(editable=False, default=timezone.now)
updated_at = models.DateTimeField(default=timezone.now)
product_category = models.ManyToManyField(EcommerceProductCategory)
description = RichTextField(max_length=2000, null=True, blank=True)
product_producer = models.ForeignKey('ProductProducer', on_delete=models.CASCADE)
creator = models.ForeignKey('users.CustomUser', on_delete=models.CASCADE, null=True, blank=True, related_name='product_creator')
points_from_reviews = models.DecimalField(max_digits=6, decimal_places=2, default=0, help_text='Średnia ocena produktu')
unique_id = models.CharField(max_length=256, unique=True)
type_of_unique_id = models.CharField(max_length=64)
class ProductInStore(models.Model):
product = models.ForeignKey('Product', on_delete=models.CASCADE)
store = models.ForeignKey('Store', on_delete=models.CASCADE)
price = models.DecimalField(max_digits=6, decimal_places=2, default=0, help_text='Cena w sklepie')
currency = models.CharField(max_length=4)
url = models.CharField(max_length=200)
created_at = models.DateTimeField(editable=False, default=timezone.now)
updated_at = models.DateTimeField(default=timezone.now)
# ADD TO DATABASE
try:
db_product = Product.objects.create(name=name, slug=slug, product_producer_id=3, unique_id=id,
description=description)
db_product.product_category.set(parsed_categories)
except:
print('product already in database')
ProductInStore.objects.create(product=,store=1,price=price,currency='PLN',url=url)
You can simply do this:
ProductInStore.objects.create(product_id=id, store=1, price=price, currency='PLN', url=url)
You don't need to pass the object if you have the ID of it, simply append _id to the field name and you can reference the foreign key that way.
I have a method that creates orders from the user's cart. For the courier to take an order from different restaurants, the order is divided into several. But at the moment I'm splitting the order just by the dish in the cart. How to make an order split by restaurants? that is, if a user orders 5 dishes from two different restaurants, then 2 orders were formed.
views.py
#action(methods=['PUT'], detail=False, url_path='current_customer_cart/add_to_order')
def add_cart_to_order(self, *args, **kwargs):
cart = Cart.objects.get(owner=self.request.user.customer)
cart_meals = CartMeal.objects.filter(cart=cart)
data = self.request.data
for cart_meal in cart_meals:
order = Order.objects.create(customer=self.request.user.customer,
cart_meal=cart_meal,
first_name=data['first_name'],
last_name=data['last_name'],
phone=data['phone'],
address=data.get('address', self.request.user.customer.home_address),
restaurant_address=cart_meal.meal.restaurant.address,
)
order.save()
return response.Response({"detail": "Order is created", "added": True})
models.py
class Order(models.Model):
"""User's order"""
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='related_orders')
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
phone = models.CharField(max_length=20)
cart_meal = models.ForeignKey(CartMeal, on_delete=models.CASCADE, null=True, blank=True)
restaurant_address = models.CharField(max_length=1024, null=True)
address = models.CharField(max_length=1024)
status = models.CharField(max_length=100, choices=STATUS_CHOICES, default=STATUS_NEW)
created_at = models.DateTimeField(auto_now=True)
delivered_at = models.DateTimeField(null=True, blank=True)
courier = models.OneToOneField('Courier', on_delete=models.SET_NULL, null=True, blank=True)
class CartMeal(models.Model):
"""Cart Meal"""
user = models.ForeignKey('Customer', on_delete=models.CASCADE)
cart = models.ForeignKey('Cart', verbose_name='Cart', on_delete=models.CASCADE, related_name='related_meals')
meal = models.ForeignKey(Meal, verbose_name='Meal', on_delete=models.CASCADE)
qty = models.IntegerField(default=1)
final_price = models.DecimalField(max_digits=9, decimal_places=2)
class Meal(models.Model):
"""Meal"""
title = models.CharField(max_length=255)
description = models.TextField(default='The description will be later')
price = models.DecimalField(max_digits=9, decimal_places=2)
discount = models.IntegerField(default=0)
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE, null=True)
slug = models.SlugField(unique=True)
class Restaurant(models.Model):
"""Restaurant"""
name = models.CharField(max_length=255)
slug = models.SlugField(unique=True)
address = models.CharField(max_length=1024)
owner = models.ForeignKey('Restaurateur', on_delete=models.CASCADE, null=True)
meals = models.ManyToManyField('Meal', related_name='related_restaurant', blank=True)
How can I do this, please help me
You can group your meals with respect to resturants.
import itertools
from core.models import CartMeal, Order
for restaurant, cart_meals in itertools.groupby(CartMeal.objects.order_by('meal__restaurant'), lambda s: s.meal.restaurant):
order = Order.objects.create(
customer=self.request.user.customer,
first_name=data['first_name'],
last_name=data['last_name'],
phone=data['phone'],
address=data.get('address', self.request.user.customer.home_address),
restaurant_address=cart_meal.meal.restaurant.address,
)
order.cart_meal.set([cart_meal for cart_meal in cart_meals])
Ref: The answer is formulated by taking help from following answer.
https://stackoverflow.com/a/57897654/14005534
I need to log the transaction of the item movement in a warehouse. I've 3 tables as shown in the below image. However Django response error:
ERRORS:
chemstore.ItemTransaction: (models.E007) Field 'outbin' has column name 'bin_code_id' that is used by another field.
which is complaining of multiple uses of the same foreign key. Is my table design problem? or is it not allowed under Django? How can I achieve this under Django? thankyou
DB design
[Models]
class BinLocation(models.Model):
bin_code = models.CharField(max_length=10, unique=True)
desc = models.CharField(max_length=50)
def __str__(self):
return f"{self.bin_code}"
class Meta:
indexes = [models.Index(fields=['bin_code'])]
class ItemMaster(models.Model):
item_code = models.CharField(max_length=20, unique=True)
desc = models.CharField(max_length=50)
long_desc = models.CharField(max_length=150, blank=True)
helper_qty = models.DecimalField(max_digits=10, decimal_places=4)
unit = models.CharField(max_length=10, blank=False)
def __str__(self):
return f"{self.item_code}"
class Meta:
verbose_name = "Item"
verbose_name_plural = "Items"
indexes = [models.Index(fields=['item_code'])]
class ItemTransaction(models.Model):
trace_code = models.CharField(max_length=20, unique=False)
item_code = models.ForeignKey(
ItemMaster, related_name='trans', on_delete=models.CASCADE, null=False)
datetime = models.DateTimeField(auto_now=False, auto_now_add=False)
qty = models.DecimalField(max_digits=10, decimal_places=4)
unit = models.CharField(max_length=10, blank=False)
action = models.CharField(
max_length=1, choices=ACTION, blank=False, null=False)
in_bin = models.ForeignKey(
BinLocation, related_name='in_logs', db_column='bin_code_id', on_delete=models.CASCADE, null=False)
out_bin = models.ForeignKey(
BinLocation, related_name='out_logs', db_column='bin_code_id', on_delete=models.CASCADE, null=False)
remarks = models.TextField(blank=True)
def __str__(self):
return f"{self.trace_code} {self.datetime} {self.item_code} {dict(ACTION)[self.action]} {self.qty} {self.unit} {self.in_bin} {self.out_bin}"
you have same db_column in two fields so change it
in_bin = models.ForeignKey(
BinLocation, related_name='in_logs', db_column='bin_code_id', on_delete=models.CASCADE, null=False)
out_bin = models.ForeignKey(
BinLocation, related_name='out_logs', db_column='other_bin_code', on_delete=models.CASCADE, null=False) /*change db_column whatever you want but it should be unique*/
If are linked to the same model name, You should use different related_name for each foreign_key filed . here is the exemple :
address1 = models.ForeignKey(Address, verbose_name=_("Address1"),related_name="Address1", null=True, blank=True,on_delete=models.SET_NULL)
address2 = models.ForeignKey(Address, verbose_name=_("Address2"),related_name="Address2", null=True, blank=True,on_delete=models.SET_NULL)
thank you for everyone helped. According to Aleksei and Tabaane, it is my DB design issue (broken the RDBMS rule) rather than Django issue. I searched online and find something similar: ONE-TO-MANY DB design pattern
In my case, I should store in bin and out bin as separated transaction instead of both in and out in a single transaction. This is my solution. thankyou.
p.s. alternative solution: I keep in bin and out bin as single transaction, but I don't use foreign key for bins, query both in bin and out bin for the bin selection by client application.
I would like to perform this query in django:
For each sensor, select it's latest message
In SQL it would be something like
SELECT * FROM (SELECT * FROM messages order by date_entered DESC) as order GROUP BY sensor_id
Models are defined like this
class BaseModel(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
date_entered = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
deleted = models.IntegerField(default=0)
class Meta:
abstract = True
ordering = ['date_modified']
class Device(BaseModel):
user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True)
name = models.CharField(max_length=255, blank=False)
identifier = models.CharField(max_length=31, blank=False)
imei = models.CharField(max_length=31, blank=False)
status = models.CharField(max_length=31, blank=False)
settings = models.TextField()
class DeviceMessage(BaseModel):
device = models.ForeignKey(Device, on_delete=models.SET_NULL, blank=True, null=True)
user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True)
latitude = models.DecimalField(max_digits=9, decimal_places=6, blank=True, null=True)
longitude = models.DecimalField(max_digits=9, decimal_places=6, blank=True, null=True)
altitude = models.DecimalField(max_digits=7, decimal_places=2, blank=True, null=True)
signal_strength = models.DecimalField(max_digits=5, decimal_places=2, blank=True, null=True)
battery = models.DecimalField(max_digits=5, decimal_places=2, blank=True, null=True)
satellites = models.IntegerField(blank=True, null=True)
size = models.IntegerField(blank=True, null=True)
raw = models.TextField(blank=True, null=True)
Is it possible to achieve this in django?
Basically, it is this problem Using ORDER BY and GROUP BY together
Since Django 1.11 there is the Subquery feature that you can use to annotate data:
from django.db.models import OuterRef, Subquery
latest = DeviceMessage.objects.filter(device_id=OuterRef('pk')).order_by('-date_entered')
devices = Device.objects.annotate(latest_message_id=Subquery(latest.values('pk')[:1]))
message_ids = [d.latest_message_id for d in devices]
for message in DeviceMessage.objects.filter(pk__in=message_ids).select_related('device'):
print(device.name, message)
......
"For each sensor, select it's latest message"
I assume by "sensor" you are referring to the Device model. In that case (since you've now clarified that BaseModel was an abstract base class), it is as simple as this (with sensor being the particular Device instance you're querying about):
sensor.devicemessage_set.order_by("-date_entered")[0]
If you want to find this information for multiple sensors at once, you might be better off using an annotation, for example:
from django.db.models import Max
...
Device.objects.all().annotate(latest_msg=Max("devicemssage_set__date_entered"))
This gives each Device object a (temporary) property called latest_msg which holds the information you want.