Understanding django annotate - django

My models:
class Ward(models.Model):
id = models.AutoField(primary_key=True, unique=True)
clinic = models.ForeignKey(Clinic, on_delete=models.CASCADE)
name = models.CharField(max_length=500, default='', blank=True)
description = models.CharField(max_length=2000, default='', blank=True)
bedcapacity = models.IntegerField(default=1)
class Bed(models.Model):
id = models.AutoField(primary_key=True, unique=True)
name = models.CharField(max_length=200, default='',
blank=True, unique=True)
clinic = models.ForeignKey(Clinic, on_delete=models.CASCADE)
ward = models.ForeignKey(Ward, on_delete=models.CASCADE)
occupied = models.BooleanField(default=False)
I'm writing to convert the following pseudocode to django:
from django.db.models import F, Q, When
clinic = Clinic.objects.get(pk=10)
wards = Ward.objects.filter(clinic=clinic)
ward_set = []
for ward in wards:
occupied = len(Bed.objects.filter(clinic = clinic, ward = ward, occupied = True))
total = len(Bed.objects.filter(clinic = clinic, ward = ward))
ward['occupied'] = occupied # The next two lines are pseudocode
ward['total']=total
ward_set.append(ward)
return render(request, 'file.html',
{
'wards': ward_set
})
I believe I should be using annotate, but I'm finding it difficult to understand annotate from the docs.

What about this ?
from django.db.models import Q, Count
ward_set = Ward.objects.filter(clinic=10).annotate(
occupied=Count('bed', filter=Q(bed__occupied=True)),
total=Count('bed')
)
You could see some examples for conditional aggregation here

Related

I am working on a django project that involves three models as indicated below Client,Loan,Payment

I am getting alot of duplicates in my template when i try to call the calculated loan payments in templates.
My models:
class Client(models.Model):
full_name = models.CharField(max_length=200,blank=True) staff=models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.SET_NULL,null=True,blank=True,related_name="client")
date = models.DateTimeField(default=timezone.now)
class Loan(models.Model):
ref = ShortUUIDField(length=6,max_length=6,alphabet="ABCDZXFQFHKRKL0123456789",unique=True)
loan_amount = models.IntegerField(blank=True,null=True)
staff=models.ForeignKey(settings.AUTH_USER_MODEL,on_delete=models.SET_NULL,null=True,blank=True,related_name="loans")
search_client=models.ForeignKey(Client,on_delete=models.SET_NULL,null=True,blank=True)
#cached_property
def loan_repayments(self):
myfilter = Loan.objects.filter(ref=self.ref,payment__payment_reason='loan repayment')
result=myfilter.aggregate(total=Sum(F('payment__amount')))
total = result['total']
if total is None:
return 0
return total
class Payment(models.Model):
ref = ShortUUIDField(length=6,max_length=6,alphabet="ABCDZXFQFHKRKL0123456789",unique=True)
payment_reason = models.CharField(max_length=200, null=True, blank=True,choices=PAYMENT_REASON,default='loan repayment',db_index=True)
amount = models.IntegerField(blank=True, null=True)
lender = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL,
blank=True, null=True, related_name="payments")
loan = models.ForeignKey(Loan, on_delete=models.CASCADE,
blank=True, null=True)
my view:
class Loan(LoginRequiredMixin,ListView):
query_set =Loan.objects.filter(status="active",action="creating loan").select_related('staff','search_client')
context_object_name = 'transactions'
paginate_by = 15
my template:
duplicates am getting:
duplicates in the toolbar

Django models - how to assign as ForeignKey

My lab has a models.py as below:
class Book(models.Model):
isbn = models.CharField(max_length=10, unique=True)
name = models.CharField(max_length=100)
published_year = models.IntegerField()
total_qty = models.IntegerField()
current_qty = models.IntegerField()
max_duration = models.IntegerField()
author = models.ForeignKey(Author, on_delete=models.PROTECT)
category = models.ForeignKey(Category, on_delete=models.PROTECT)
def __str__(self):
return self.name
class BookCopy(models.Model):
class Status:
AVAILABLE = 1
BORROW =2
LOST = 3
barcode = models.CharField(max_length=30, unique=True)
buy_date = models.DateField(null=True, blank=True)
status = models.IntegerField()
book = models.ForeignKey(Book, on_delete=models.PROTECT)
def __str__(self):
return self.barcode
class User(models.Model):
username = models.CharField(max_length=30, unique=True)
fullname = models.CharField(max_length=100, null=True)
phone = models.CharField(max_length=10, null=True)
def __str__(self):
return self.fullname
class BookBorrow(models.Model):
class Status:
BORROWING = 1
RETURNED = 2
borrow_date = models.DateField()
deadline = models.DateField()
return_date = models.DateField(null=True)
status = models.IntegerField()
book_copy = models.ForeignKey(BookCopy, on_delete=models.PROTECT)
book_name = models.ForeignKey(Book, on_delete=models.PROTECT)
user = models.ForeignKey(User, on_delete=models.PROTECT)
And i wrote the api for borrow_book function like below:
#csrf_exempt
def muon_sach(request):
body = request.POST
username = body.get('username')
barcode = body.get('barcode')
user = User.objects.filter(username=username).first()
bookcopy = BookCopy.objects.filter(barcode = barcode).first()
if not user:
return HttpResponse(json.dumps({
'error':"Nguoi dung khong ton tai"
}))
if not bookcopy:
return HttpResponse(json.dumps({
'error':"ma sach khong ton tai"
}))
book_borrow = BookBorrow()
# resp = []
book_borrow.user = user
book_borrow.book_copy = bookcopy
book_borrow.borrow_date = datetime.now()
book_borrow.deadline = datetime.now() + timedelta(days=bookcopy.book.max_duration)
book_borrow.status = BookBorrow.Status.BORROWING
book_borrow.book_name = bookcopy.book.name
book_borrow.save()
bookcopy.status = BookCopy.Status.BORROW
bookcopy.save()
bookcopy.book.current_qty -=1
bookcopy.book.save()
return HttpResponse(json.dumps({'success':True}))
however when i test with postman (give username and barcode), it gets the error
xxx "BookBorrow.book_name" must be a "Book" instance."
Could you please advise where incorrect and assist me correct it ? Appreciate for any assist
You have to do the following:
#csrf_exempt
def muon_sach(request):
# ... more code here
bookcopy = BookCopy.objects.filter(barcode = barcode).first()
book_borrow.book_name = bookcopy.book
book_borrow.save()
# ... more code here
return HttpResponse(json.dumps({'success':True}))
So in the definition of your model you can see that book_name has the following structure:
class BookBorrow(models.Model):
# ... More code here
book_name = models.ForeignKey(Book, on_delete=models.PROTECT)
user = models.ForeignKey(User, on_delete=models.PROTECT)
It is clear that BookBorrow.book_name must accept a Book instance. So when you pass in you code book_borrow.book_copy = bookcopy it is passing a BookCopy instance so that's the error.
borrow_copy.book is the appropiate.
You have specified book_name to be a Foreign Key to Book, and you try to assign to it the book.name value.
Either you need to set this field as a CharField or you need to rename the field from book_name to book and use book_borrow.book = bookcopy.book

How to aggregate on a foreign key and a specific field at the same time?

My table named Value has a one to many relationship with the table Country and the table Output_outcome_impact. I have a query that is working fine and gets what I want but then I need to do an average of the value field, but this average needs to be done for each unique id_output_outcome_impact and not the whole query.
class Country(models.Model):
country_name = models.CharField(max_length=255, primary_key=True)
CONTINENTCHOICE = (
('Africa', 'Africa'),
('America', 'America'),
('Asia', 'Asia'),
('Europe', 'Europe'),
('Oceania', 'Oceania')
)
region = models.CharField(max_length=255)
continent = models.CharField(max_length=255, choices=CONTINENTCHOICE)
GDP_per_capita = models.IntegerField(null=True)
unemployment_rate = models.FloatField(null=True)
female_unemployment_rate = models.FloatField(null=True)
litteracy_rate = models.FloatField(null=True)
def __str__(self):
return self.country_name
class OutputOutcomeImpact(models.Model):
output_outcome_impact_name = models.CharField(max_length=255, primary_key=True)
TYPECHOICE = (
('Output', 'Output'),
('Outcome', 'Outcome'),
('Impact', 'Impact'),
)
type = models.CharField(max_length=255, choices=TYPECHOICE)
description = models.TextField()
TARGETGROUP = (
('Standard', 'Standard'),
('Investors', 'Investors'),
('Local authorities and NGOs', 'Local authorities and NGOs'),
)
target_group = models.CharField(max_length=255,choices=TARGETGROUP)
question = models.TextField(null=True, blank=True)
parent_name = models.ForeignKey('self', on_delete=models.PROTECT, null=True, blank=True)
indicator = models.ForeignKey(Indicator, on_delete=models.PROTECT)
def __str__(self):
return self.output_outcome_impact_name
class Activity(models.Model):
activity_name = models.CharField(max_length=255, primary_key=True)
description = models.TextField()
product_service = models.TextField()
output_outcome = models.TextField()
outcome_impact = models.TextField()
output_outcome_impacts = models.ManyToManyField('OutputOutcomeImpact')
countries = models.ManyToManyField('Country')
sectors = models.ManyToManyField('Sector')
def __str__(self):
return self.activity_name
class Value(models.Model):
value_name = models.CharField(max_length=255, primary_key=True)
country = models.ForeignKey(Country, on_delete=models.PROTECT)
id_output_outcome_impact = models.ForeignKey(OutputOutcomeImpact, on_delete=models.PROTECT)
value_has_source = models.ManyToManyField('Source')
value = models.FloatField()
function_name = models.CharField(max_length=255, default = "multiply")
def __str__(self):
return self.value_name
region_values = Value.objects.filter(id_output_outcome_impact__output_outcome_impact_name__in = output_pks, country_id__region = region).exclude(country_id__country_name = country).values()
So the result of the query is available below, and what I would like to achieve is to set the value field to an average of every object that has the same id_output_outcome_impact_id, here Dioxins and furans emissions reduction appears twice so I would like to get the 2 values set as their average.
<QuerySet [{'value_name': 'Waste_to_dioxins', 'country_id': 'Malawi', 'id_output_outcome_impact_id': 'Dioxins and furans emissions reduction', 'value': 0.0003, 'function_name': 'multiply'}, {'value_name': 'Waste_to_dioxins_south_africa', 'country_id': 'South Africa', 'id_output_outcome_impact_id': 'Dioxins and furans emissions reduction', 'value': 150.0, 'function_name': 'multiply'}, {'value_name': 'Households getting electricity per kWh', 'country_id': 'Malawi', 'id_output_outcome_impact_id': 'Households that get electricity', 'value': 0.0012, 'function_name': 'multiply'}, {'value_name': 'Dioxin to disease', 'country_id': 'Malawi', 'id_output_outcome_impact_id': 'Reduction of air pollution related diseases', 'value': 0.31, 'function_name': 'multiply'}]>
I am wondering if django models allow such modification (I went through the doc and saw the annotate function with the average but couldn't make it work for my specific case), that would be nice. Thanks.
region_values = Value.objects.filter(id_output_outcome_impact__output_outcome_impact_name__in = output_pks, country_id__region = region).exclude(country_id__country_name = country).values('id_output_outcome_impact__output_outcome_impact_name').annotate(Avg('value'))

business generated has to come based on dcr(daily call report)

I have DCR & SalesMIS model. I want to get the business generated count. And if count is it should return the business_genrated else saleMIS.amount
I wrote a method in DCR model i.e. get_business_generated(self) and apply filter on SaleMIS model. Then trying to get the count of business_generated
ERROR:D:\Projects\Python\Django\kingllp\venv\lib\site-packages\django\db\models\base.py", line 95, in new
"INSTALLED_APPS." % (module, name)
RuntimeError: Model class builtins.DCR doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.
This is DCR model
class DCR(models.Model):
STATUSES = (
('1N', 'Need Analysis'),
('2P', 'Proposal Stage'),
('3C', 'Competitive Selling'),
('4D', 'Decision Stage'),
)
prospect = models.ForeignKey(Prospect, on_delete=models.CASCADE, related_name='dcrs')
date = models.DateField(blank=True)
status = models.CharField(choices=STATUSES, max_length=2, default='1N')
discussion_points = models.CharField(max_length=2047, blank=True)
business_generated = models.IntegerField(default=0)
is_new_business = models.BooleanField(default=False)
def get_business_generated(self):
date = self.date
client = self.prospect
sale = SalesMIS.objects.filter(date=date,client = Prospect)
salecount = sale.count()
if salecount==0:
return DCR.business_generated
else:
return SalesMIS.amount
This is SaleMIS model
class SalesMIS(models.Model):
class Meta:
verbose_name_plural = _("Sale MIS")
date = models.DateField()
fls = models.ForeignKey(Employee, blank=True, null=True, on_delete=models.SET_NULL, related_name='sales')
amount = models.DecimalField(max_digits=20, decimal_places=2)
po_number = models.CharField(max_length=255, null=True, blank=True)
products = models.CharField(max_length=255, null=True, blank=True)
client = models.ForeignKey(Client, blank=True, null=True, on_delete=models.SET_NULL, related_name='client_mis')
def __str__(self):
return str(self.date) + ":" + self.fls.full_name()
Business share has to come based on DCR/MIS.

Add extra column to tables in django_tables2

I want to add extra-column which is not in my model. And I apply one of the solutions at this site to my project. But it doesn't work properly.
model.py
class Companies(models.Model):
legal_name = models.CharField(max_length=120, blank=True)
co_name = models.CharField(max_length=120, blank=True)
client = models.ForeignKey(Clients, models.SET_NULL, blank=True, null=True)
tel_no = models.CharField(max_length=120, blank=True)
email = models.EmailField(null=True, blank=True)
address = models.TextField(null=True, blank=True)
contact = models.CharField(max_length=250, blank=True)
con_tel_no = models.CharField(max_length=120, blank=True)
entity = models.CharField(max_length=2, null=True)
yearend = models.DateField(null=True, blank=True)
bn = models.CharField(max_length=9)
memo = models.TextField(null=True, blank=True)
slug = models.SlugField(null=True, blank=True)
def t2_due_date(self):
now_year = datetime.date.today().year
if self.entity == 'CO':
yearend_ = DateWidget.decompress(self, self.yearend)
if yearend_[1] > 6:
yearend_[2] = now_year + 1
yearend_[1] -= 6
else:
yearend_[2] = now_year
yearend_[1] += 6
t2_due = DateWidget.compress(self, yearend_)
return t2_due
tables.py
class ScheduleTable(tables.Table):
due_date_col = tables.Column(accessor='t2_due_date', verbose_name='T2 Due Date')
class Meta:
attrs = {"class": "paleblue", "width":"100%"}
fields = ['client','legal_name', 'co_name', 'entity', 'yearend', 'due_date_col']
model = Companies
When I run this program 'due_date_col' is always blank. It seems that the function('t2_due_date) does not go through. Do you have any clue to clear this problem?
As far as I know accessor points to related objects, rather than model's properties, methods etc.
What you can try is to make use of Table.render_{column} as so:
class ScheduleTable(tables.Table):
def render_due_date_col(self, record):
return record.t2_due_date()
See djanog tables official doc for more info.