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

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'))

Related

django filter empty array in prefetch_related

I used prefetch_related and Prefetch to create a list of areas ( main category), needs (subcategory), and product categories (subsubcategory) for my products.
filter_needs = Area.objects.filter(Exists(Need.objects.filter(category_need=OuterRef('pk'))))
products = Area.objects.prefetch_related(Prefetch('need_area',
queryset=filter_needs, to_attr='need_area__category_need__product'))
.filter(need_area__category_need__product__isnull=False).distinct()
models.py
class Area(models.Model):
title = models.CharField(max_length=75, blank=False)
body = models.CharField(max_length=150, default='-', blank=False)
publish = models.DateTimeField('publish', default=timezone.now)
class Need(models.Model):
title = models.CharField(max_length=75, blank=False, null=False, help_text='max 75 characters')
body = models.CharField(max_length=150, default='-', blank=False)
publish = models.DateTimeField(default=timezone.now)
need_area = models.ForeignKey(Area, on_delete=models.CASCADE, related_name='need_area')
class ProductCategory(models.Model):
title = models.CharField(max_length=400, blank=False, null=False, help_text='max 400 characters')
body = models.TextField(default='-')
publish = models.DateTimeField('publish', default=timezone.now)
category_area = models.ForeignKey(Area, on_delete=models.CASCADE, related_name='category_area', null=True)
category_need = models.ForeignKey(Need, on_delete=models.CASCADE, related_name='category_need', null=True)
class Product(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=400, blank=False)
category = models.ForeignKey(ProductCategory, on_delete = models.CASCADE, blank=True, related_name='product')
Right now query renders all areas that contain the product but with all needs (even if there is no product in need). How can exclude all needs that are empty?
Try this:
from django.db.models import Prefetch
Area.objects.prefetch_related(
Prefetch(
"need_area"
queryset=Need.objects.filter(
category_need__products__isnull=False,
category_need__products__status=1,
).distinct(),
)
).filter(
need_area__category_need__products__isnull=False,
need_area__category_need__products__status=1,
).distinct()
You have to specify the same filter on the custom prefetch as with your outer filter.

Export a list of customers who completed registration but haven't performed any action (no invoice, expense, withdrawal)

This is the full task :
Export a list of customers who completed registration but haven't performed any action (no invoice, expense, withdrawal) last week (3-9 May)
I need to create this type of SQL, but I don't know how to check for actions, what I did for now is
SELECT user FROM users_user
WHERE completed_registration=False
AND date_joined BETWEEN '2021-05-03 00:00:00' AND '2021-05-29 00:00:00'
UNION
SELECT user FROM invoice_invoice;
Check for users who had completed the registration, check for the date, and then check the invoice. But as I check for invoice_invoice itself it's an empty table, why do I get one user when I launch this query? The completed_registration and the date fields which are in queryset right now are only for test.
Only users
This is when I check only for invoices
This is the structure:
Expense model:
class Merchant(BaseModel):
company = models.ForeignKey(Company, on_delete=models.PROTECT, related_name='merchants')
name = models.CharField(max_length=255)
company_code = models.CharField(max_length=255, default='', blank=True)
def __str__(self):
return f'{self.company} {self.name}'
class Expense(Operation):
category = models.CharField(choices=ExpenseCategories.get_choices(), default=ExpenseCategories.GENERAL.name,
db_index=True, blank=True, max_length=255)
merchant = models.ForeignKey(Merchant, on_delete=models.PROTECT, related_name='expenses', blank=True, null=True)
amount = models.PositiveIntegerField(default=0, blank=True, help_text='Only taxable amount. In cents')
full_amount = models.PositiveIntegerField(
default=0,
blank=True,
help_text='Full amount. Most of the time same as amount or bigger. In cents'
)
currency = models.ForeignKey(Currency, on_delete=models.PROTECT, related_name='expenses',
default=settings.DEFAULT_CURRENCY_CODE)
description = models.TextField(default='', blank=True)
is_taxable = models.BooleanField(blank=True, default=True)
from_date = models.DateField(null=True, blank=True, help_text='Start date in case of aggregated bulk creation.')
to_date = models.DateField(null=True, blank=True, help_text='End date in case of aggregated bulk creation.')
receipt_number = models.CharField(blank=True, default='', max_length=255, help_text='Number from receipt.')
Invoice model:
class Invoice(Operation):
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='invoices')
number = models.CharField(max_length=255)
notes = models.TextField(default='', blank=True)
payment_due = models.DateField()
total = models.PositiveIntegerField(help_text='In cents', default=0)
payment_status = models.CharField(choices=InvoiceStatuses.get_choices(), default=InvoiceStatuses.UNPAID,
max_length=20)
pdf = models.FileField(null=True, blank=True, upload_to='invoices/pdf', max_length=255)
is_sent = models.BooleanField(default=False, help_text="Is pdf invoice sent")
User model:
class User(AbstractUser):
username = None
email = models.EmailField('email address', blank=True)
phone = PhoneNumberField(unique=True)
is_verified = models.BooleanField(default=False)
language = models.ForeignKey(
Language,
default=settings.DEFAULT_LANGUAGE,
on_delete=models.SET_DEFAULT,
)
avatar = models.ImageField(upload_to='users/avatars', null=True, blank=True)
companies = models.ManyToManyField(Company, related_name='users')
active_company = models.OneToOneField(Company, null=True, related_name='active_user', on_delete=models.SET_NULL)
agreement_text = models.TextField(default='', blank=True)
agreement_date = models.DateField(null=True, blank=True)
personal_no = models.CharField(max_length=100, default='', blank=True)
full_name = models.CharField(max_length=255, help_text='Field holds first and last names.', default='', blank=True)
completed_registration = models.BooleanField(default=False)
work_hours_from = models.TimeField(default=settings.DEFAULT_WORK_HOURS_FROM, null=True, blank=True)
work_hours_until = models.TimeField(default=settings.DEFAULT_WORK_HOURS_UNTIL, null=True, blank=True)
You seem to want not exists. I would expect logic like this:
SELECT u.*
FROM users_user u
WHERE u.completed_registration AND
NOT EXISTS (SELECT 1
FROM invoice_invoice i
WHERE i.user = u.user AND
i.invoice_date >= '2021-05-03' AND
i.invoice_date < '2021-05-10'
);
You would repeat this logic for each table where you want to check an action. Also, it is not clear what date you want to use within the invoice table, so I made one up.

How would i replicate this SQL query in Django Rest Framework using generics.ListAPIView and serializers.ModelSerializer

Cant actually find anything like what i am trying to do with wanting to use case statements and left joins using DRF Django Rest Framework, yes this could be done on the front end of the project i am working on but id rather not have to let the front end potentially sending 100s of requests when loading a product list for example.
Nothing i can really add to this but i have tried many different ways of doing the below
SELECT
p.itemno,
CASE
WHEN cp.price IS NULL THEN p.HighSell
ELSE cp.price
END AS price
FROM
api_product AS p
LEFT JOIN
api_customerprices AS cp ON p.itemno = cp.itemno
AND cp.customerno = 'Examplecust'
WHERE
p.FreeStock > 0
or restockDate > '1900-01-01'
Here Are my models:
class Product(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
itemno = models.CharField(max_length=100)
description = models.TextField(null=True)
colour = models.CharField(max_length=100, null=True)
manufacturerCode = models.CharField(max_length = 100, null=True)
RRP = models.DecimalField(max_digits=6, decimal_places=2, null=True)
SSP = models.DecimalField(max_digits=6, decimal_places=2,null=True)
FreeStock = models.IntegerField(null=True)
ItemSpec1 = models.CharField(max_length=100, null=True)
ItemSpec2 = models.CharField(max_length=100, null=True)
ItemSpec3 = models.CharField(max_length=100, null=True)
ItemSpec4 = models.CharField(max_length=100, null=True)
ItemSpec5 = models.CharField(max_length=100, null=True)
ItemSpec6 = models.CharField(max_length=100, null=True)
ItemSpec7 = models.CharField(max_length=100, null=True)
ItemSpec8 = models.CharField(max_length=100, null=True)
ItemSpec9 = models.CharField(max_length=100, null=True)
ItemSpec10 = models.CharField(max_length=100, null=True)
TI = models.IntegerField(null=True)
HI = models.IntegerField(null=True)
Item_Height = models.DecimalField(max_digits=6, decimal_places=2, null=True)
Item_Length = models.DecimalField(max_digits=6, decimal_places=2, null=True)
Item_Width = models.DecimalField(max_digits=6, decimal_places=2, null=True)
ProductPaging_Height = models.DecimalField(max_digits=6, decimal_places=2, null=True)
ProductPaging_Length = models.DecimalField(max_digits=6, decimal_places=2, null=True)
ProductPaging_Width = models.DecimalField(max_digits=6, decimal_places=2, null=True)
CartonHeight = models.DecimalField(max_digits=6, decimal_places=2, null=True)
CartonLength = models.DecimalField(max_digits=6, decimal_places=2, null=True)
CartonWidth = models.DecimalField(max_digits=6, decimal_places=2, null=True)
palletQty = models.IntegerField(null=True)
cartonQty = models.IntegerField(null=True)
restockDate = models.DateField(null=True)
IPG = models.CharField(max_length=100, null=True)
CatalogueTheme = models.CharField(max_length=100, null=True)
Analysis2 = models.CharField(max_length=100, null=True)
Electrical_or_Housewares = models.CharField(max_length=100, null=True)
HighSell = models.DecimalField(max_digits=6, decimal_places=2, null=True)
Analysis1 = models.CharField(max_length=100, null=True)
Image = models.TextField(null=True, blank=True)
MarketingText = models.TextField(null=True, blank=True)
SearchTerms = models.TextField(null=True, blank=True)
ItemVariant = models.CharField(max_length=100)
Categories = models.CharField(max_length=249, null=True)
def __str__(self):
return self.itemno
class CustomerPrices(models.Model):
customerNo = models.CharField(max_length=20)
itemno = models.CharField(max_length=20)
price = models.DecimalField(max_digits=6, decimal_places=2)
startDate = models.DateField()
endDate = models.DateField()
def __str__(self):
return self.customerNo
Here are my Serializers
class OauthProdListSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = (
'id',
'itemno',
'description',
'colour',
'RRP',
'SSP',
'manufacturerCode',
'FreeStock',
'restockDate',
'Image',
'HighSell',
'ItemVariant',
'Categories'
)
class OCustomerPricesSerializer(serializers.ModelSerializer):
class Meta:
model = CustomerPrices
fields = (
'id',
'customerNo',
'itemno',
'price'
)
Found a way of performing what i wanted, turned out i was missing knowledge on the SerializerMethodField() method, once i found out that i got it pretty quickly.
class ProdListSerializer(ModelSerializer):
price = SerializerMethodField()
class Meta:
model = Product
fields = [
'id',
'itemno',
'description',
'colour',
'RRP',
'SSP',
'manufacturerCode',
'FreeStock',
'restockDate',
'Image',
'ItemVariant',
'Categories',
'price'
]
def get_price(self, obj):
itemno = obj.itemno
customerNo = self._context['view'].request.query_params.get('customerNo', '')
if customerNo:
customerPrice = CustomerPrices.objects.filter(
Q(customerNo=customerNo) &
Q(itemno=itemno)
).values('price').first()
if customerPrice:
return customerPrice
else:
return Product.objects.filter(itemno=itemno).values('HighSell').first()
else:
return Product.objects.filter(itemno=itemno).values('HighSell').first()
I am just writing this off the top of my head so it will take some effort on your part to get it working.
You do not have a relationship between your Product and CustomerPrices. You will either need to add a Foreignkey between the two tables directly or, you will need to add an intermediate table with foreignkeys between them.
Your query will be something like:
Q is a django query object - see Complex lookups with Q objects
results = CustomerPrices.objects.filter(
Q(itemno__FreeStock__gt=0) | Q(itemno__restockDate__gt='1900-0-01'),
customerno='Examplecust'
)
# Your case statement can be handled in your code logic:
def case_statement(result):
if not result.price:
return result.HighSell
return result.price
I am not exactly sure what you mean, but you can get the SQL for all the tables in an app by running this command:
python manage.py sqlall your_app
or you want to convert sql to django use :
Person.objects.raw('your_sql_query')

Order by formula

I'm filtering for stores. And I need to sort the stores by rating but the rating is considered by a complex form, I can sort them by one field but how to count for each I do not know
that is current version order by rate
shops = shops.order_by('rate')
But you need to put the formula instead of the field rate, how is this done?
class Shop(models.Model):
name = models.CharField(max_length=255, verbose_name=u'Название')
time_begin = models.TimeField(max_length=255,
verbose_name=u'Время начала работы')
time_end = models.TimeField(max_length=255,
verbose_name=u'Время окончания работы')
phone = models.CharField(max_length=255, verbose_name=u'Телефон')
preview = models.FileField(upload_to='files/shop/preview')
delivery_price = models.IntegerField(verbose_name=u'Стоимость доставки')
min_price = models.IntegerField(verbose_name=u'Мин доставка', null=True)
min_order = models.IntegerField(verbose_name=u'Минимальный заказ')
del_wait_time = models.IntegerField(verbose_name=u'Время доставки минут')
is_always = models.BooleanField(verbose_name=u'Круглосуточно?',
default=True, blank=True)
is_cash = models.BooleanField(verbose_name=u'Наличными',
default=True, blank=True)
is_card = models.BooleanField(verbose_name=u'Картой курьеру',
default=True, blank=True)
is_online = models.BooleanField(verbose_name=u'Онлайн-оплата',
default=True, blank=True)
is_points = models.BooleanField(verbose_name=u'Еда за баллы',
default=True, blank=True)
is_sale = models.BooleanField(verbose_name=u'Акция',
default=True, blank=True)
is_new = models.BooleanField(verbose_name=u'Новый магазин',
default=True, blank=True)
notification = models.CharField(max_length=255,
verbose_name=u'Важное уведомление',
blank=True, null=True)
email = models.CharField(max_length=255, verbose_name=u'E-mail')
review_email = models.CharField(max_length=255,
verbose_name=u'Review E-mail',
default='null')
rate = models.FloatField(max_length=255, verbose_name=u'Рейтинг')
destination = models.CharField(max_length=255,
verbose_name=u'Местоположение')
specific = models.CharField(max_length=255, default='',
verbose_name=u'Специализация ресторана')
description = models.TextField(max_length=3000,
verbose_name=u'Описание')
cook = models.CharField(max_length=255, verbose_name=u'Имя повара',
blank=True, null=True)
image_cook = models.FileField(upload_to='files/shop/cook',
blank=True, null=True)
desc_cook = models.TextField(max_length=3000,
verbose_name=u'Описание повара',
blank=True, null=True)
shoptype = models.ForeignKey(ShopType, verbose_name=u'Тип заведения',
related_name="shop")
slider = models.ManyToManyField(Slider, verbose_name=u'Слайдер',
related_name="slider",
blank=True)
kitchen = models.ManyToManyField(Kitchen)
Lacking a more detailed statement of the problem, I will give a vague answer. (to be filled in if more details show up)
You could do this by annotating the queryset with a calculated value. See the documentation here
What this means is that you're going to ask the database to figure out the "rate", for example by dividing each shop's distance by its time, and add that value to the data returned for each shop. You could then do an order_by based on that field.

Pk fields always ends up in resulting query GROUP BY clause

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.

Categories