How can I join two child tables using Django Querset API? - python-3.x

I have a model with two child tables and one parent table. Here is the sample model classes.
# Stores list of unique category names
class Category(models.Model):
category = models.CharField(max_length=20, unique=True, validators=[MinLengthValidator(limit_value=5)])
name = models.CharField(max_length=20, validators=[MinLengthValidator(limit_value=8)])
# Parent class for the next two child classes
class DailyLog(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
name = models.CharField(max_length=100, validators=[MinLengthValidator(limit_value=8)])
code = models.CharField(max_length=4, validators=[MinLengthValidator(limit_value=3)])
suggested_values = models.CharField(max_length=100, null=True, blank=True)
# First child class defines display order for dailylog items
class DailyLogDisplayOrder(models.Model):
category_item = models.ForeignKey(DailyLog, on_delete=models.CASCADE)
display_order = models.PositiveIntegerField()
# Second Child class publishes dailylog on a daily bases
class DailyLogCheckList(models.Model):
daily_task = models.ForeignKey(DailyLog, on_delete=models.CASCADE)
publish_date = models.DateField(auto_now=True)
daily_task = DailyTaskCategoryManager() # Log manager to get records per category
How do I perform a cartesian product query? The last column comes from the first child table Dailylogdisplayorder. Here is the raw sql.
select daily_task_id, checklist.publish_date, disporder.display_order
from dailylogchecklist checklist, compliance_dailylogdisplayorder disporder
where checklist.daily_task_id = disporder.category_item_id and checklist.publish_date='2020-07-12'
I have tried using cursor.execute() method per Django documentation. However, I am not able to figure out how to return results in QuyerySet. And also looking for a better way to combine child columns using QuerySet. The return queryset is assigned to a formset.
class DailyTaskCategoryManager(models.Manager):
def with_displayorder(self, user):
from django.db import connection
dtrange = datetime.today().date()
with connection.cursor() as cursor:
cursor.execute("select daily_task_id, checklist.publish_date, disporder.display_order
from dailylogchecklist checklist, compliance_dailylogdisplayorder disporder
where checklist.daily_task_id = disporder.category_item_id and
checklist.publish_date=%s", [dtrange])
result_list = []
for row in cursor.fetchall():
p = self.model(id=row[0], daily_task_id=row[1], publish_date=row[2])
p.display_order = row[3]
result_list.append(p)
return result_list

I already answered to a similar question, You can use prefetch_related() to get the related child table data. Check this answer
https://stackoverflow.com/a/71571509/9561654

Related

Django query fetch foreignkey association without N+1 queries in database

I have two models, Product and Price. I have used the ForeignKey association of the Django models to define the association between product and price. the scenario is, one product can have multiple prices according to size. On the home page, I have to fetch all the products with their prices and need to show their price(probably base price).
Following is the code that I tried.
class Product(BaseModel):
name = models.CharField(max_length=50)
category = models.ForeignKey(Category, null=True, on_delete=models.SET_NULL, help_text='Please add new category if not given.')
image = models.ImageField(upload_to='images/')
tag = models.ManyToManyField(Tag, help_text='You can add multiple tags')
slug = models.SlugField(unique=True, null=True)
time = models.TimeField(verbose_name='Time Required')
class Price(BaseModel):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
size = models.FloatField(validators=[MinValueValidator(0)])
amount = models.FloatField(validators=[MinValueValidator(0)])
Then in the view.py file
class ProductListView(ListView):
model = Product
context_object_name = 'products'
paginate_by = 32
def get_context_data(self,*args, **kwargs):
object = super(ProductListView, self).get_context_data(*args, **kwargs)
object['categories'] = Category.objects.order_by('name')
return object
def get_queryset(self):
return Product.objects.order_by('name')
In the template, I am able to get and loop through the categories and products but I am not able to access the related prices of each product.
If I tried something in the get_context_data, will it cause N+1 queries to fetch prices for every product?
In the template I tried to use something like {{ product.price_set }} but it returns order.Price.None.
use {{ product.price_set.all }}.
To avoid N+1 queries in your filter, use prefetch_related so it looks something like.
Product.objects.all().prefetch_related('price_set')
See prefetch_related in the Django documentation.
See also select related vs prefetch related

How to update the value from one model field in to value of another model field in Django?

I want to update Contract_bills_amount field value in Class Contract from bills_mount field value in Class Bills:
#Contract
class Contract(models.Model):
Contract_code = models.CharField(max_length=200)
Contract_rec = models.DateTimeField('contract date ')
Contract_total_amount = models.FloatField('total amount')
Contract_deposit= models.FloatField('deposit')
Contract_remainder_amount=models.FloatField('Contract remainder amount',null=True,blank=True)
Contract_bills_amount=models.FloatField('bills_amount',null=True,blank=True)
Contract_owner = models.ForeignKey(Client, on_delete=models.CASCADE)
Contract_car = models.ForeignKey(Car, on_delete=models.CASCADE)
def save(self, *args,**kwargs):
a=self.Contract_total_amount
b=self.Contract_deposit
self.Contract_remainder_amount=a-b
super(Contract,self).save(*args,**kwargs)
def __str__(self):
return self.Contract_code
#Bills
class bills(models.Model):
bills_Contract = models.ForeignKey(Contract, on_delete=models.CASCADE)
Contract_owner = models.ForeignKey(Client, on_delete=models.CASCADE)
bills_code = models.CharField(max_length=200)
Contract_rec = models.DateTimeField('date')
bills_mount= models.FloatField('amount')
def __str__(self):
return self.bills_code
You already have a foreign key relationship between Contract and Bills as "bills_Contract". What else are you looking for? You only need to reference the value of the related table.
Example:
one_contract = Contract.objects.get(pk=1) #get the first contract
all_bills = one_contract.bills_set.all() #get all the bills in the first contract
desired_bill = all_bills.filter(bills_mount=400) #get bills amounting to 400
So, you may not need the "Contract_bills_amount" field in Contracts at all since you can already access that through backward relationship.
I see you have a reference to the Client in both Contract and Bills (perhaps for convenience but that cyclic reference is not necessary since the Contract Models identifies the owner.)
This relationship (i.e Bill --> Contract --> Client) implies that you also get the Bill from the client, like so: Bill <-- Contract <-- Client.
You need to learn more about referencing related objects in django

how do I get foreighkey of foreighkey without a loop in django?

It is a Django project, I am trying to create a wishlist (many-to-many will not help because I need DateTime of getting that wished item in the wishlist).
class Client(models.Model):
name = models.CharField(max_length=100)
user = models.ForeignKey(User, on_delete=models.CASCADE)
class Product(models.Model):
name = models.CharField(max_length=100)
price = models.DecimalField()
class WishItem(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
client = models.ForeignKey(Client, related_name="wishlist", on_delete=models.CASCADE)
added_at = models.DateTimeField(auto_now_add=True)
What I could do is only this:
wishlist = Client.objects.wishlist.select_related('product').all()
wish_products = [item.product for item in wishlist]
But I need something like this, without a loop but with a single SQL query and single line
wishlist = Client.objects.wishlist.product.all()
When I try to run this code I get an error AttributeError: 'RelatedManager' object has no attribute 'product'
You can .filter(…) [Django-doc] and then .order_by(…) [Django-doc] with:
Product.objects.filter(wishitem__client__user=my_user).order_by('wishitem__added_at')
You can make it more covenient to query by spanning a ManyToManyField with your WishItem:
class Client(models.Model):
# …
wishlist = models.ManyToManyField(
'Product',
through='WishItem'
)
class Product(models.Model):
# …
pass
class WishItem(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
client = models.ForeignKey(Client, related_name='wishitems', on_delete=models.CASCADE)
added_at = models.DateTimeField(auto_now_add=True)
then you can query with:
Product.objects.filter(client__user=my_user).order_by('wishitem__added_at')
It will also make querying for the .wishlist of the Client more covenient, of the Products where the .client_set is a manager that manages the Clients that have that Product on the wishlist.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Many to many relationship will fix the problem you can add extra fields to your WishItem class you can try this :
class Product(models.Model):
name = models.CharField(max_length=100)
price = models.DecimalField()
class Client(models.Model):
name = models.CharField(max_length=100)
user = models.ForeignKey(User, on_delete=models.CASCADE)
WishProducts = models.ManyToManyField(Product,through='WishItem')
class WishItem(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
client = models.ForeignKey(Client, on_delete=models.CASCADE)
added_at = models.DateTimeField(auto_now_add=True)

retrieve data from manytomany field in the sequence of it was saved django

I Have a Invoice system where employee or staff can create invoice and can add multiple product and quantity for the specific customer . as i am using mysql i cant take json data or an array data .so i was taking the quantity and price( after discount and other modificaion) as a string and then when showing or printing the invoice i used regex to find the quantity and price .i added product ids in a manytomay field from where i am getting the product name and selling price. while showing the data on printing page in when i use zip the products are showing as the id of the product so i want to retrive the data the way it is being saved . or could you tell me any way to do it more easier way?
Here is my models.py
class Invoice(models.Model):
customers = models.ForeignKey(Customer, on_delete=models.CASCADE)
products = models.ManyToManyField(Product)
total = models.FloatField()
date = models.DateField(auto_now_add=True)
amounts = models.CharField(max_length=500, default="00")
quantity = models.CharField(max_length=500, blank=True)
def save(self, *args, **kwargs):
if not Invoice.objects.count():
self.id = 20210001
else:
self.id = Invoice.objects.last().id + 1
super(Invoice, self).save(*args, **kwargs)
Here is my views.py of printing page function
def final_billing(request, id=None):
pk = id
obj = Invoice.objects.get(id=pk)
products = obj.products.all()
customer = Customer.objects.get(id=obj.customers.id)
amn = obj.amounts
qt = obj.quantity
list_of_comma = re.findall("[\d+.+\d]+", amn)
amnts = [float(n) for n in list_of_comma]
list_of_quantity = re.findall('[0-9]+', qt)
qty = [int(n) for n in list_of_quantity if n.isdigit()]
products = list(products)
both = zip(products,amnts,qty)
return render(request, 'finalbill.html',{'bills': obj, "due": customer, "both": both})
I want it to be retrieved the product objects in the sequence of it was saved
The query can only be sorted with a specific field, Django cannot guess otherwise, so in your case the best case is to sort your products by the date they were created, for example :
obj.products.all().order_by("created")
This suppose that you have a "created" field that is added each time a product is save in your database.
Another way of doing it is to specify the through option, from the documentation :
you can use the through option to specify the Django model that represents the intermediate table that you want to use.
The most common use for this option is when you want to associate extra data with a many-to-many relationship.
The through table contains an the primary key of the relation, you can use that to retrieve the sequence in which your objects were added.
for example :
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=50)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through='Membership',
through_fields=('group', 'person'),
)
class Membership(models.Model):
group = models.ForeignKey(Group, on_delete=models.CASCADE)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
inviter = models.ForeignKey(
Person,
on_delete=models.CASCADE,
related_name="membership_invites",
)
invite_reason = models.CharField(max_length=64)
Through Field

How to retrieve all many_to_many relations for each objects in a single query

I have four tables :
class Recipe(models.Model):
item_recipe = models.OneToOneField(Item, on_delete=models.CASCADE, related_name='item_recipe')
items = models.ManyToManyField(Item, through='RecipeItem')
class RecipeItem(models.Model):
item = models.ForeignKey(Item, on_delete=models.CASCADE)
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE)
quantity = models.IntegerField(default=1)
class Item(models.Model):
def __str__(self):
return self.name
name = models.CharField(max_length=200, unique=True)
effects = ArrayField(models.CharField(max_length=200), blank=True, default=list)
pods = models.IntegerField(null=True)
level = models.IntegerField(null=True)
type = models.CharField(max_length=200, null=True, blank=True, default="Ressource")
image = models.URLField()
class Prix(models.Model):
def __str__(self):
return self.item.name
prix_x1 = models.IntegerField(null=True)
prix_x10 = models.IntegerField(null=True)
prix_x100 = models.IntegerField(null=True)
saved_at = models.DateTimeField()
item = models.ForeignKey(Item, on_delete=models.CASCADE)
A recipe is composed of 1 to 8 Items with a certain quantity indicated is the RecipeItem table.
I would like a query that gives me the price of every Recipe.
In other word, a query that get all items and its prices for every recipe and sum it.
I don't find a way to do that without a for loop.
EDIT
Here is what i have currently, it's not pretty and not effective ..
items = Recipe.objects.all().select_related('item').annotate(
prix1_ressource=Subquery(
Prix.objects.filter(
item=OuterRef('items')
).values('prix_x1').exclude(prix_x1__isnull=True).order_by('-saved_at')[:1]
),
prix1_item=Subquery(
Prix.objects.filter(
item=OuterRef('item_recipe')
).values('prix_x1').exclude(prix_x1__isnull=True).order_by('-saved_at')[:1]
)
).exclude(prix1_item__isnull=True).values('id', 'item_recipe__name', 'prix1_ressource', 'items__name',
'recipeitem__quantity', 'prix1_item',
'item_recipe__type', 'item_recipe')
for id in np.unique(items.values_list('id', flat=True)):
item = items.filter(id=id)
try:
prix_craft = sum([i['recipeitem__quantity'] * i['prix1_ressource'] for i in item])
gain = item[0]['prix1_item'] - prix_craft
except TypeError:
continue
There is really not a nice SQL way. At some point either you or Django will have to iterate over all the items in a query and sum them. Perhaps you could make it a little more efficient by using an Inner join, as documented here, using it to preload the items. But resolving the item's current Prix might require a separate query each time.

Resources