Django COUNT with JOIN and GROUP BY SQL query - python

What would be the correct Django view and HTML for this SQL query?:
SELECT
hood.`hood`,
COUNT(business.`id`) AS TOTAL
FROM
`hood`
JOIN business
ON hood.`id` = business.`hood_id`
WHERE business.`city_id` = 8
GROUP BY hood.`id`
ORDER BY TOTAL DESC
LIMIT 5 ;
My models are:
class Hood(models.Model):
name = models.CharField(max_length=50, db_column='hood')
slugname = models.SlugField(max_length=50, blank=True)
city = models.ForeignKey('City', related_name='hoods')
location = models.ForeignKey('Location', related_name='hoods')
switch = models.SmallIntegerField(null=True, blank=True, default='1')
class Meta:
db_table = 'hood'
class Business(models.Model):
name = models.CharField(max_length=50, db_column='name', blank=True)
slugname = models.SlugField(max_length=50, blank=True)
city = models.ForeignKey('City', related_name="business")
hood = models.ForeignKey('Hood', null=True, blank=True, related_name="business")
....
And the HTML template?
Thank you!

Check out the docs on aggregration:
https://docs.djangoproject.com/en/1.6/topics/db/aggregation/
You should be able to write a view that returns a queryset with counts similar to this:
from django.db.models import Count
Hood.objects.filter(business__city_id=8).annotate(bus_count=Count('business__id'))
As for the HTML, that's entirely up to you. If you provide that queryset, though, you'd be able to get the count with {{ object.bus_count }}.

Related

How do I filter django models based on product fields?

I am trying to run a filter command, using related fields; and am unsure how to go about it:
class Listing(models.Model):
name = models.CharField(max_length=150)
slug = models.SlugField()
description = models.TextField()
catchphrase = models.TextField(null=True, blank=True)
picture_0 = models.ImageField(upload_to = "mainimages")
picture_1 = models.ImageField(null=True, blank=True, upload_to = "./product/static/product/imgs")
picture_2 = models.ImageField(null=True, blank=True, upload_to = "./product/static/product/imgs")
short_term_price = models.IntegerField(default=0)
long_term_price = models.IntegerField(default=0)
tax = models.IntegerField(default=0)
tag = models.ForeignKey('Tag', on_delete=models.PROTECT)
def __str__(self):
return self.name
class Order(models.Model):
listing = models.ForeignKey(Listing, on_delete=models.PROTECT)
customer = models.ForeignKey(Customer, on_delete=models.PROTECT)
lease_date = models.DateField()
clear_date = models.DateField()
price = models.IntegerField(default=0) #cents
def __str__(self):
return self.listing.name
def get_display_price(self):
return "{0:.2f}".format(self.price / 100)
The general idea is that the user provides a start date and an end date and Django only returns the listings that aren't already in an order in that timeframe. I am unsure how to go about the view function:
def search_products(request, start_date, end_date):
listing = Listing.objects.select_related('order').all()
I will provide an answer as if you are using the lease_date to do the filtering. There is a couple of ways to achieve this. One is:
listing_qs = Listing.objects.filter(
pk__in=Order.objects.exclude(lease_date__range(start_date,end_date)).select_related('listing').values_list('listing__pk')
)
Code breakdown:
retrieve the orders by excluding those whose lease date is in between the provided timeframe
selecting the listing's pk (via values('listing__pk')) you can select any other attribute you want
using the result of the 2 previous instructions to get the Listing objects since we have the list of pk.
Another way:
Just exclude all the Listing objects whose lease date is in between the provided timeframe
Listing.objects.exclude(order_set__lease_date__range=(start_date,end_date))
I hope this helps.

How multiply and sum two columns in different tables in django

I have an application for calculating diets based on the nutrients of each meal. In admin of this application I want to price of each meal to the Meal table, which I have managed to do by calculating the price when displaying it in admin:
# admin.py
class AdminMeal(admin.ModelAdmin):
list_display = ['name', 'meal_type_names', 'price']
#admin.display(description='Price')
def price(self, obj):
unit_prices = np.asarray(obj.mealingredient_set.order_by('id').values_list('ingredient__unit_price'))
amounts = np.asarray(obj.mealingredient_set.order_by('id').values_list('amount'))
to_return = float(np.matmul(np.transpose(unit_prices), amounts) / 1000)
return mark_safe(to_return)
Now my main question: I need to allow ordering of Meal table based on Price which I don't know how.
based on my search it seems I should use annotate instead of my current way of calculating the price to be able to sort my table, I found a solution in here
# admin.py
class AdminMeal(admin.ModelAdmin):
list_display = ['name', 'meal_type_names', 'price']
def get_queryset(self, request):
queryset = super().get_queryset(request)
queryset = queryset.annotate(_price=Sum('mealingredient__amount * mealingredient__ingredient__unit_price'))
But sadly it throws this error (I think it's because i'm trying to SUM over different tables):
Unsupported lookup 'amount * mealingredient' for AutoField or join on the field not permitted. Any help is appreciated, forgive me if I have missed something obvious, I'm a beginner in django.
Some of the relevant Models for Meal table:
# models.py
class Meal(models.Model):
id = models.AutoField(primary_key=True)
meal_type = models.ForeignKey(MealType, on_delete=models.CASCADE, null=True, blank=True, related_name='meal_type')
meal_types = models.ManyToManyField(MealType, blank=True)
name = models.CharField(max_length=255, null=True, blank=True)
class MealIngredient(models.Model):
id = models.AutoField(primary_key=True)
meal = models.ForeignKey(Meal, on_delete=models.CASCADE, null=True, blank=True)
user_meal = models.ForeignKey(UserMeal, on_delete=models.CASCADE, null=True, blank=True)
ingredient = models.ForeignKey(Ingredient, on_delete=models.SET_NULL, null=True, blank=True)
amount = models.DecimalField(max_digits=14, decimal_places=4, default=0)
class Ingredient(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255, null=True, blank=True)
state = models.CharField(max_length=255, null=True, blank=True)
unit_price = models.IntegerField(null=True, blank=True)
You need to use ExpressionWrapper:
from django.db.models import DecimalField, ExpressionWrapper, F, Sum
queryset = queryset.annotate(
_price=Sum(
ExpressionWrapper(
F('mealingredient__amount') *
F('mealingredient__ingredient__unit_price'),
output_field=DecimalField()
)
)
)

How to get table data (including child table and sub child data) based on id which obtains from another table data? Django

views
company = Company.objects.get(id = company_id) # getting input from django urls (<int:company_id>)
vehicles = CompanyContainVehicles.objects.filter(company_id=company.id) # Give all rows having same id (company.id)
all_vehicles = Vehicle.objects.filter(companies=company) # Gives all row with id provide by company
all_vehicles_parts = VehiclePart.objects.filter(__________) # Not Working
models
class Company(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(blank=True, null=True, unique=True)
description = models.TextField()
class Vehicle(models.Model):
vehicle_number = models.IntegerField()
name = models.CharField(max_length=255)
slug = models.SlugField(blank=True, null=True, unique=True)
companies = models.ManyToManyField(
Company,
through='CompanyVehicle',
related_name='companies'
)
class CompanyVehicle(models.Model):
company = models.ForeignKey(Company, on_delete=models.CASCADE)
vehicle = models.ForeignKey(Vehicle, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
class VehiclePart(models.Model):
id = models.AutoField(primary_key=True)
vehicle = models.ForeignKey(Vehicle, on_delete=models.CASCADE)
type = models.ForeignKey(PartType, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True, blank=True)
How do I get VehiclePart's with their Vehicle? (I think I will give all the data in a variable and we should divide it and add it with their Vehicle). Also, what can we do to access data if VehiclePart contains a child class named VehiclePartDetail?
I think I will give all the data in a variable and we should divide it and add with their Vehicle.
You don't have to. Django can read ForeignKey relations in reverse. You can query with:
qs = Vehicle.objects.prefetch_related('vehiclepart_set')
then you can enumerate over the queryset, and for each Vehicle object, access this with .vehiclepart_set.all(). For example:
for item in qs:
print(vehicle_name)
for part in item.vehiclepart_set.all():
print(part.id)

How to join tables with ManyToManyField type in Django?

I have models like this:
class Education(models.Model):
title = models.CharField(default=None, max_length=100)
content = models.TextField(default=None)
price = models.ManyToManyField(Price)
class Price(models.Model):
cost = models.CharField(default=None, max_length=20)
created_at = models.DateTimeField(auto_now=True, null=True, blank=True)
And i want to inner join between two tables and access to all fields of both.
We can achieve like this,
Education.objects.filter(price__in=Price.objects.all()).select_related('Price').values_list('title', 'content', 'price__cost', 'price__created_at')

How to get first N rows from every category in Django

I have following models, with many to many table, for which I would like to get first 20 news from every category in single response.
class Category(models.Model):
code = models.CharField(primary_key=True, max_length=45)
name = models.CharField(max_length=200, blank=True, null=True)
is_active = models.TextField(blank=True, null=True) # This field type is a guess.
class Meta:
managed = False
db_table = 'category'
verbose_name_plural = 'Categories'
class News(models.Model):
source_code = models.CharField(max_length=45, blank=True, null=True)
title = models.CharField(max_length=1000, blank=True, null=True)
image = models.CharField(max_length=2000, blank=True, null=True)
link = models.CharField(max_length=1000, blank=True, null=True)
published_at = models.DateTimeField(blank=True, null=True)
scraped_at = models.DateTimeField(blank=True, null=True)
is_active = models.TextField(blank=True, null=True) # This field type is a guess.
categories = models.ManyToManyField('Category', through='NewsCategory')
class Meta:
managed = False
db_table = 'news'
verbose_name_plural = 'News'
class NewsCategory(models.Model):
news_id = models.ForeignKey(News, db_column='news_id', on_delete=models.CASCADE)
category_code = models.ForeignKey(Category, db_column='category_code', on_delete=models.CASCADE)
class Meta:
managed = False
db_table = 'news_category'
unique_together = (('news_id', 'category_code'),)
verbose_name_plural = 'NewsCategory'
My view class looks like this, and here I would like to add some logic to return 20 rows for each category, for example if I have 5 categories it should return 100 news in single request.
class NewsViewSet(viewsets.ModelViewSet):
http_method_names = ['get']
serializer_class = NewsSerializer
def get_queryset(self):
queryset = News.objects.all().order_by('-published_at')
sources = self.request.query_params.getlist('sources')
if len(sources) > 0:
queryset = queryset.filter(source_code__in=sources)
return queryset
The typical way to do this is to use a window function. Django has support for them but I don't think they allow filtering on the output of them. I think this is further complicated by the m2m field. Given it's not too complex and doesn't seem to involve user input, you might just want to use a raw query.
Here's what it might look like:
SELECT *
FROM (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY c.code ORDER BY n.published_at DESC) AS row_num
FROM appname_news n
JOIN appname_newscategory nc
ON n.id = c.news_id
JOIN appname_category c
ON nc.category_code = c.code
) sub
WHERE
row_num <= 20
And see here for Django's guide on how to actually implement this in a view:
https://docs.djangoproject.com/en/3.2/topics/db/sql/#executing-custom-sql-directly

Categories

Resources