I am using Django 3.2
models.py
class AwardCategory(models.Model)
name = models.CharField(max_length=16)
slug = models.SlugField(max_length=16, unique=True, db_index=True)
class Award(models.Model):
name = models.CharField(max_length=16)
category = models.ForeignField(AwardCategory, on_delete=models.CASCADE)
class CeremonyAward(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="awards", on_delete=models.CASCADE )
award = models.ForeignKey(Award, related_name="ceremonies", on_delete=models.CASCADE )
I want to write a query that returns for a specified user (or list of user_ids):
award_category:
name
number_of_awards_in_category
I know I have to use annotations, but I'm quite new to annotations.
This is what I have so far:
user_ids = (someuser.id,)
CeremonyAward.objects.filter(user_id__in=user_ids).\
annotate(Count('award__category__slug', distinct=True)).\
prefetch_related('award','award_category')
How do I create a query that groups by award__category and then counts the awards in each distinct category
Related
I have 3 django tables connected like this:
Is there anyway to make a query for table Table that will get id_equip from table equip?
models.py
class Vendor(models.Model):
vendor_name = models.CharField(max_length=50)
def __str__(self):
return self.vendor_name
class Equipment(models.Model):
equipment_name = models.CharField(max_length=50)
id_vendor = models.ForeignKey(Vendor, on_delete=models.CASCADE, default=None)
def __str__(self):
return self.equipment_name
class Table(models.Model):
table_name = models.CharField(max_length=100)
id_vend = models.ForeignKey(Vendor, on_delete=models.CASCADE, default=None)
id_equip = models.ManyToManyField(Equipment)
This part of the django docs is relevant and helpful, I definitely recommend your review at least that section and ideally the whole page.
Your models are already denormalized as evidenced by Table.id_equip which relates to Equipment so you could do:
table = Table.objects.get(SOME_FILTER)
equipment_ids = list(table.id_equip.all().values_list('id', flat=True))
If you wanted to go through the vendor I'd suggest:
table = Table.objects.get(SOME_FILTER)
equipment_ids = list(Equipment.objects.filter(vendor_set__table_set=table).values_list('id', flat=True))
I would recommend that you don't name your relationship fields with id_. With an ORM, these fields should represent the instances of the Model they are mapping to. For example:
class Table(models.Model):
name = models.CharField(max_length=100)
vendor = models.ForeignKey(Vendor, on_delete=models.CASCADE, default=None)
equipment = models.ManyToManyField(Equipment)
If you're trying to create the model on top of an existing table, you can make use of the db_column parameter when defining the field.
vendor = models.ForeignKey(Vendor, on_delete=models.CASCADE, default=None, db_column="id_vend")
Each time i want to add an invoice, i want to have a unique invoice_id which is an increment number (+1), but the problem is that i have a multiple users app, so i get the error that this invoice_id already exist. how can i customize the ids so each user can have its ids following the latest of same user.
class Company(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=64)
class Invoice(models.Model):
company = models.ForeignKey('Company', on_delete=models.CASCADE)
invoice_id = models.CharField(max_length=20, unique=True)
name = models.CharField(max_length=256)
add an last_invoice field in your company record. Then let it do the work for you by adding a function that generates new invoice:
class Company(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=64)
last_invoice = models.CharField(max_length=20)
def get_invoice(self):
l_newNum = self.last_invoice + '1' #your number here
self.last_invoice = l_newNum
self.save()
return l_newNum
class Invoice(models.Model):
company = models.ForeignKey('Company', on_delete=models.CASCADE)
#you no longer need unique as it will create a mess between companies
invoice_id = models.CharField(max_length=20)
name = models.CharField(max_length=256)
def save(self):
self.invoice_id = self.company.get_invoice()
super(Invoice,self).save()
You need to fill in the details here and there, but this should work for you. IDeally I would suggest that the get_invoice is actually used to automatically create Invoice entry for the company, but this would depend on the concrete case you are building.
Good afternoon,
I am really struggling with getting a sum using Annotate in DJango.
I am using User object and the following models:
class Depts(models.Model):
dept_name = models.CharField(max_length=55)
dept_description = models.CharField(max_length=255)
isBranch = models.BooleanField(default=False)
def __str__(self):
return "{}".format(self.dept_name)
class UserProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='profile')
title = models.CharField(max_length=75)
dept = models.ForeignKey(Depts, on_delete=models.CASCADE, related_name="dept", null=True)
class ActivityLog(models.Model):
activity_datetime = models.DateTimeField(default=timezone.now)
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, related_name='activity_user')
activity_category = models.ForeignKey(ActivityCategory, on_delete=models.CASCADE, null=True, related_name='activity_cat')
activity_description = models.CharField(max_length=100, default="Misc Activity")
class ActivityCategory(models.Model):
activity_name = models.CharField(max_length=40)
activity_description = models.CharField(max_length=150)
pts = models.IntegerField()
def __str__(self):
return '%s' % (self.activity_name)
What I need to do is get a group of departments with aggregating the sum of the pts earned by all the users activitylogs.
So a user is part of department, they do activities, each activity is of a type activity_category and has associated points. How can I query using the ORM to get a sum of points for everyone in each department?
Thank you, I cannot seem to wrap my mind around it.
You annotate the departments with the sum:
from django.db.models import Sum
Depts.objects.annotate(
total_pts=Sum('dept__user__activity_user__activity_category__pts')
)
Note: The related_name=… parameter [Django-doc]
is the name of the relation in reverse, so from the Depts model to the UserProfile
model in this case. Therefore it (often) makes not much sense to name it the
same as the forward relation. You thus might want to consider renaming the dept relation to userprofiles.
After setting the related_name='userprofiles', the query is:
from django.db.models import Sum
Depts.objects.annotate(
total_pts=Sum('userprofiles__user__activity_user__activity_category__pts')
)
I have a class Summary:
class Summary(models.Model):
title = models.CharField(max_length=128)
category = models.ForeignKey(Category)
subcategory = models.ForeignKey(Subcategory)
content = RichTextField(null=True, blank=True)
users_rated_positive = models.ManyToManyField(
User, blank=True, related_name='summaries_rated_positive')
users_rated_negative = models.ManyToManyField(
User, blank=True, related_name='summaries_rated_negative')
author = models.ForeignKey(User, related_name='summaries_authored')
and a class UserProfile:
class UserProfile(models.Model):
user = models.OneToOneField(User, primary_key=True, related_name='profile')
karma = models.IntegerField(default=0)
rank = models.IntegerField(null=True,blank=True)
I want the karma to be calculated as the positive ratings on all the users summaries minus the negative ratings on all the users summaries
I figured I could add a property like this instead of a field:
#property
def karma(self):
summaries_list = self.user.summaries_authored.all()
positive_karma = sum(
[summary.users_rated_positive.count() for summary in summaries_list])
negative_karma = sum(
[summary.users_rated_negative.count() for summary in summaries_list])
return positive_karma - negative_karma
Is this the right way to do this? I feel like I should be using aggregate, or annotate but to be honest I'm new to Django and not entirely sure how they work for complex situations.
from django.db.models import Count
positive_karma = Summary.objects.filter(author=self.user).aggregate(pos_count=Count('users_rated_positive'))['pos_count']
negative_karma = Summary.objects.filter(author=self.user).aggregate(neg_count=Count('users_rated_negative'))['neg_count']
aggregate returns a dict so the actual value must be retrieved by the key
You would use annotate if you wanted to get the count of pos/neg ratings for every user.
Guys,
Is there an easy way to return different fields names from different models by chaining joins?
My model:
Class Item(models.Model):
item_code = models.CharField(max_length=10)
name = models.CharField(max_length=255)
...
Class Stock(models.Model):
item_code = models.ForeignKey( Item )
userid = models.ForeignKey( User )
qty = models.IntegerField()
...
I want to select " Item.Item_code, Item.name, Stock.qty where Stock.userid=2 and Item.item_code = Stock.Item_Code"
How do i do this in Django?
Gath
I want to select " Item.Item_code, Item.name, Stock.qty where Stock.userid=2 and Item.item_code = Stock.Item_Code"
You can pick these specific fields only using one SQL, provided you start from the Stock model. For instance
q = Stock.objects.select_related('userid', 'item_code').filter(
userid__id = 2).values('item_code__item_code', 'item_code__name', 'qty')
This will help if you want to limit the data and then number of queries. If you are not concerned with this then do:
q = Stock.objects.filter(userid__id = 2)
for stock in q:
print stock.item_code.item_code
print stock.item_code.name
print stock.qty
This will return a queryset with only those fields you have chose using values. You can then iterate through it.
PS: Re: your models.
Class Stock(models.Model):
item_code = models.ForeignKey( Item )
userid = models.ForeignKey( User )
qty = models.IntegerField()
...
It is a good idea to use the model name in lower case for FK relationships. For e.g. you ought to write:
Class Stock(models.Model):
item = models.ForeignKey( Item ) # Changed
user = models.ForeignKey( User ) # Changed
qty = models.IntegerField()
...
You can also use this:
Stock.objects.filter(user=2).values('item__item_code', 'item__name')
First of all change fileds names
Read this very carefuly http://docs.djangoproject.com/en/dev/ref/models/querysets/
Class Item(models.Model):
item_code = models.CharField(max_length=10)
name = models.CharField(max_length=255)
...
Class Stock(models.Model):
item = models.ForeignKey( Item )
user = models.ForeignKey( User )
qty = models.IntegerField()
...
#view
stocks = Stock.objects.filter(user=2)
for stock in stocks:
print stock.item.item_code, stock.item.name
#one query version