Is there a way to query reverse in django models? - python

I know this question has been answered in different ways but i'm still unable to see the clear picture.
I have the following tables with following relationships:
class Category(models.Model):
name = models.CharField(max_length=100, null=False, blank=False)
def __str__(self):
return self.name
class SubCategory(models.Model):
sub_name = models.CharField(max_length=100, null=False, blank=True, default='None')
category = models.ManyToManyField(Category, default=1)
class Product(models.Model):
name = models.CharField(max_length=150, null=False, blank=False)
brand = models.CharField(max_length=100, null=False, blank=False)
price = models.FloatField(null=False, blank=False)
weight = models.CharField(max_length=100,null=False, blank=False)
sub_category = models.ForeignKey(SubCategory, on_delete=models.SET_DEFAULT, default=13)
category = models.ForeignKey(Category, on_delete= models.CASCADE)
I am trying to solve two queries as follows:
Fetch all the category and subcategories to a specific category where the brand is given. Display structure that i'm making is Brand(Men->Shirts,Pants etc. Women->Shirts,Pants etc).
NOTE: Each brand can sell products of multiple categories and subcategories.
Fetch all the subcategories where the category name must be taken out from the result of Category.objects.all(). Display structure that i'm making here is Men(its sub categories) , Women(its sub categories)

Let us take it step by step
Get all products for a specific brand
Product.objects.filter(brand=brand)
Now we want to list down the categories for this match. We'll get the ids of categories instead
Product.objects.filter(brand=brand).values_list("category_id", flat=True)
Now let us get the corresponding category objects
queryset = Product.objects.filter(brand=brand).values_list("category_id", flat=True)
categories = Category.objects.filter(id__in=queryset)
Note: If you just want to fetch the category names, you can do
Product.objects.filter(brand=brand).values_list("category__name", flat=True).distinct()

Related

What's the most efficient way to retrieve django queryset with the hightest number of posts for a related name?

I'm currently working on a website where advertisements will be posted to display vehicles for sale and rent. I would like to retrieve a queryset that highlights only one car brand (i.e. Audi) which has the highest number of posts for the respective model. Example:
Displaying the Audi brand because it has the highest number of related posts.
My question is, what's the most efficient way of doing this? I've done some work here but I'm pretty sure this is not the most efficient way. What I have is the following:
# Algorithm that is currently retrieving the name of the brand and the number of related posts it has.
def top_brand_ads():
queryset = Advertisement.objects.filter(status__iexact="Published", owner__payment_made="True").order_by('-publish', 'name')
result = {}
for ad in queryset:
# Try to update an existing key-value pair
try:
count = result[ad.brand.name.title()]
result[ad.brand.name.title()] = count + 1
except KeyError:
# If the key doesn't exist then create it
result[ad.brand.name.title()] = 1
# Getting the brand with the highest number of posts from the result dictionary
top_brand = max(result, key=lambda x: result[x]) # Returns for i.e. (Mercedes Benz)
context = {
top_brand: result[top_brand] # Retrieving the value for the top_brand from the result dict.
}
print(context) # {'Mercedes Benz': 7} -> Mercedes Benz has seven (7) related posts.
return context
Is there a way I could return a queryset instead without doing what I did here or could this be way more efficient?
If the related models are needed, please see below:
models.py
# Brand
class Brand(models.Model):
name = models.CharField(max_length=255, unique=True)
image = models.ImageField(upload_to='brand_logos/', null=True, blank=True)
slug = models.SlugField(max_length=250, unique=True)
...
# Methods
# Owner
class Owner(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
telephone = models.CharField(max_length=30, blank=True, null=True)
alternate_telephone = models.CharField(max_length=30, blank=True, null=True)
user_type = models.CharField(max_length=50, blank=True, null=True)
payment_made = models.BooleanField(default=False)
expiring = models.DateTimeField(default=timezone.now)
...
# Methods
# Advertisement (Post)
class Advertisement(models.Model):
STATUS_CHOICES = (
('Draft', 'Draft'),
('Published', 'Published'),
)
owner = models.ForeignKey(Owner, on_delete=models.CASCADE, blank=True, null=True)
name = models.CharField(max_length=150, blank=True, null=True)
brand = models.ForeignKey(Brand, on_delete=models.CASCADE, blank=True, null=True)
publish = models.DateTimeField(default=timezone.now)
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='Draft')
...
# Other fields & methods
Any help would be greatly appreciated.
Since you need brands, let's query on Brand model:
Brand.objects.filter(advertisement__status__iexact="Published").\
filter(advertisement__owner__payment_made=True).\
annotate(published_ads=Count('advertisement__id')).\
order_by('-published_ads')
However, even in your proposed solution, you can improve a little bit:
Remove the order_by method from your queryset. It doesn't affect the final result but adds some overhead, especially if your Advertisement model is not indexed on those fields.
Every time you call ad.brand you are hitting the database. This is called the N+1 problem. You are in a loop of n, you make n extra db access. You can use select_related to avoid such problems. In your case: Advertisement.objects.select_related('brand')...
Did you try the count method?
from django.db.models import Count
Car.objects.annotate(num_views=Count('car_posts_related_name')).order_by('num_views')

How do I call a specific field in a model into another model?

I am new to django. I wanted to know if it was possible to call a specific field in a model into another model.
The goal is to update a the quantity of a specific product in the database and generate a receipt for it.
Here's the example:
models.py:
class Product(models.Model):
name = models.CharField(max_length=300, null=True)
price = models.FloatField(null=True)
quantity = models.IntegerField(default='0', blank=True, null=True)
class UpdateStock(models.Model):
date = models.DateTimeField(default=timezone.now)
wayBill_Number = models.CharField(max_length=300, null=True)
product_Name = models.ForeignKey(Product.name, null=True, on_delete= models.SET_NULL)
quantity = models.ForeignKey(Product.quantity, null=True, on_delete= models.SET_NULL)
I just want to select the product and the quantity to update in the database but I am unable to do so.
A ForeignKey is supposed to refer to another model, not to one of its fields.
So the first thing you have to do it to edit your UpdateStock model this way :
class UpdateStock(models.Model):
date = models.DateTimeField(default=timezone.now)
wayBill_Number = models.CharField(max_length=300, null=True)
product = models.ForeignKey('Product', null=True, on_delete=models.SET_NULL) # Here
quantity = models.IntegerField(default='0', blank=True, null=True) # Here
So you can bound your product to your stock_update when you create it:
stock_update = UpdateStock.objects.create(..., product=your_product_obj, ...)
And you will then be able to access any product's field from this stock_update:
product = stock_update.product
print(product.quantity)
product.quantity = product.quantity + stock_update.quantity
print(product.quantity)
product.save()
But I strongly advise you to read more about many-to-one relationships in the docs.

how can I limit the count of returned objects from a related manager in django

I have two models Category and Product
class Category(models.Model):
name = models.CharField(max_length=200,
db_index=True)
slug = models.SlugField(max_length=200,
unique=True,)
description = models.CharField(max_length=50,
blank=True)
class Product(models.Model):
category = models.ForeignKey(Category,
related_name='products',
on_delete=models.CASCADE)
name = models.CharField(max_length=200,
db_index=True)
description = models.TextField(blank=True)
slug = models.SlugField(max_length=200, db_index=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
available = models.BooleanField(default=True)
featured = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
the wanted behavior is to return all categories with and each category should include 10 products
I tried to return all categories without limiting the products returned objects then I used the slice filter in the templates, but I am not sure if this is efficient for big scale and I am not sure if Django will lazy-load the products.
right now the view is as follows
def product_list(request):
featured_products = Product.objects.filter(featured=True).all()
categories = Category.objects.all()
return render(request,
template_name='home/home.html',
context={'categories': categories,
'featured_products': featured_products})
is the way I am using is efficient or I should limit the products when querying the categories using Category.objects.all()?
Yes, that's an usual approach. Django's querysets are lazy and database won't be accessed until you slice the result in your template and that slicing would go - kind of - to the level of database.

Get category product with minimal price of product in each category (Django querysets)

I want to get from database only 3 categories with lowest price product in each category so i tried to do this
Category.objects.get(id=1).category_product.all().values('price').order_by('price')[0]
Category.objects.get(id=2).category_product.all().values('price').order_by('price')[0]
Category.objects.get(id=3).category_product.all().values('price').order_by('price')[0]
How to simplify this queryset, get three categories with lowest price of product in each category? Get with one request
class Product(models.Model):
name = models.CharField(max_length=255)
category = models.ForeignKey(Category, on_delete=models.PROTECT, null=True, related_name='category_product')
class Category(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(unique=True)
image = models.ImageField(upload_to='images', null=True, blank=True)
Category.objects.filter(id__in=[1,2,3]).category_product.order_by('price').values('id', 'price')[:3]

How to get count items in foreign key Django?

I have two classes of models and I need to get the number of products from a single vendor
class Vendor(models.Model):
name = models.CharField(max_length=50, unique=True)
seo_name = models.SlugField()
product_count = models.IntegerField(default=0)
class Product(models.Model):
vendor = models.ForeignKey(Vendor, unique=False, blank=True, default=None, null=True, on_delete=models.SET_NULL)
How to calculate the count of products in the Vendor ?
Take a look on Django recipe: Add an auto-count field to your model
tutorial. It totally covers your question.
Good Luck !

Categories

Resources