Here is the related App and Classes. Trying to get the sum of amount of all the components belonging to an invoice and save it in 'sub_total' field of Invoice. An invoice sub_total = sum of amount of all components of the invoice instance. What will be way around? Thanks in advance.
from stock.models import Part
class Invoice(models.Model):
invoice_number = models.CharField(max_length=250)
sub_total = models.DecimalField(max_digits=8, decimal_places=2)
class Component(models.Model):
invoice = models.ForeignKey('Invoice',)
stock = models.ForeignKey('stock.Part',)
qty = models.SmallPositiveIntegerField()
unit_price = models.DecimalField(max_digits=8, decimal_places=2)
amount = models.DecimalField(max_digits=8, decimal_places=2)
After hours of trying things out, I finally got a clue and things are working fine now. I did not want complicated things in views or in the templates.
Below link has a fine example, and learnt from below and further testing seems to be working fine.
https://github.com/mwolff44/django-simple-invoice/blob/master/invoice/models.py
Around line number 194, something like below is shown, which tells us the clue as to what to do.
Below is similar to my "Invoice" Class
....
def total(self):
total = Decimal('0.00')
for item in self.items.all():
total = total + item.total()
return total
Then in Item (Component, in my case) Class. Around line number 281, something like below is shown. Although this has been taken care of earlier, thought others might find this useful.
...
def total(self):
total = Decimal(str(self.unit_price * self.quantity))
return total.quantize(Decimal('0.01'))
Again, big thanks to Mathias WOLFF, https://github.com/mwolff44
Related
Model:
class Vote(models.Model):
thumbs_up = models.ManyToManyField(
settings.AUTH_USER_MODEL, blank=True, related_name='thumbs_up')
thumbs_down = models.ManyToManyField(
settings.AUTH_USER_MODEL, blank=True, related_name='thumbs_down')
View:
qs = Vote.objects.all()
percent_min = request.GET.get('min-rating')
percent_max = request.GET.get('max-rating')
qs = qs.annotate(percent=(Count('thumbs_up')/(Count('thumbs_down')+Count('thumbs_up')))
* 100).filter(percent__gte=percent_min)
qs = qs.annotate(percent=(Count('thumbs_up')/(Count('thumbs_down')+Count('thumbs_up')))
* 100).filter(percent__lte=percent_max)
I also tried this which also didn't work.
qs = qs.annotate(up=Count('thumbs_up', distinct=True), combined=Count('thumbs_up', distinct=True) +
Count('thumbs_down', distinct=True), result=(F('up')/F('combined'))*100).filter(result__gte=percent_min)
I'm attempting to filter by min and max percentages based on user votes (up and down) but I can't seem to get it to work properly.
Using the current code if I, for example, put a maximum percentage of 74% in then it filters out everything rated 100% and leaves the remaining. The opposite happens if I enter 74% as a minimum percentage, it filters everything except those rated 100%.
Currently no 0 rated entries as I have to tackle the divide by 0 issue next.
Any insights would be greatly appreciated.
So I came up with this which seems to be working:
qs = qs.annotate(meh=Count('thumbs_meh', distinct=True), up=Count('thumbs_up', distinct=True), combined=Count('thumbs_up', distinct=True) +
Count('thumbs_down', distinct=True) + Count('thumbs_meh', distinct=True), result=Case(When(combined=0, then=0), default=((F('up')+(F('meh')/2))/(1.0*F('combined')))*100)).filter(result__gte=rating_min)
I added another model field for 'meh' votes hence the addition to the query.
You can do something similar to
Vote.objects.filter(percentage__range=(min_perct, max_perct))
Although you need to generate a separate field for this method to work. Try adding:
class Vote
# ...
percentage = models.DecimalField(default=0, max_digits=5, decimal_places=2)
# Remember to update this field after every update!
v = Vote()
v.percentage = (thumbs_up/(thumbs_up + thumbs_down))
Pardon the bad drawing.
Notations:
I - Interview
R1,R2,R3,R4 - Rounds
RT - Rating Sheet
A0, A1, A2, A4 - Aspect(Text Fields) under Rating sheet.
Models:
class Interview(models.Model):
date = ............
class Round(models.Model):
interview = models.ForeignKey(Interview)
class RatingSheet(models.Model):
name = ............
class Aspects(models.Model):
sheet= models.ForeignKey(RatingSheet)
I need a rating sheet to be filled for each interview. That sheet will hold ratings (1-10) for each aspect for each Round.
What have I tried:
I have thought of creating a form dynamically for an Interview with N rounds and accept ratings as drop down .
And I collect all via request.POST. And create a dictionary like:
rating_interview_1 = {'interview_pk':
{
'Round1': {'ASP1': 5, 'ASP2':10},
'Round2': {'ASP1': 5, 'ASP2':10},
'Round3': {'ASP1': 5, 'ASP2':9},
}
}
And then save it to database somehow. I think this can be difficult to edit or maintain. Can I get some suggestions on the correct way to design this ?
I solved it myself. Took 2 weeks of thinking. This is too complex to answer.
But one thing I learned is...there is always a way to organise you models to store data . Other wise retrieval of the same will be a real pain in the future.
Solution:
We need more models .
class InterviewRatingSheet(models.Model):
name = models.CharField(max_length=200, default='MySheet')
interview = models.ForeignKey(Interview, null=True)
round_name = models.OneToOneField(Round, null=True)
def __str__(self): # __unicode__ on Python 2
return self.name
class RatingAspect(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(null=True, default='', blank=True)
interview_rating_sheet = models.ForeignKey(InterviewRatingSheet)
points = models.PositiveIntegerField(default=0)
def __str__(self): # __unicode__ on Python 2
return self.name
So for a "Round", there will be only one "InterviewRatingSheet" and each sheet will have "RatingAspect"s same as "Aspects".
You can see the whole code in this link:
https://github.com/arindam31/django-interview-manager
Let's say I have following simplified model:
class CurrentInvoices(models.Manager):
def get_queryset(self):
qs = super(CurrentInvoices, self).get_queryset()
current_invoices = qs.order_by('person', '-created_on').distinct('person').values('pk')
return qs.annotate(invoice_count=models.Count('number')).filter(id__in=current_invoices).order_by('person__last_name')
class Invoice(models.Model):
created_on = models.DateField()
person = models.ForeignKey(Person)
total_amount = models.DecimalField()
number = models.PositiveSmallIntegerField()
objects = models.Manager()
current_invoices = CurrentEDCInvoices()
A Person can have an Invoice with the same number if for some reason the previously generated invoice was wrong. The latest one (highest created_on) is the one that counts.
The trick with .filter(id__in) in the manager is needed to get the results listed by persons last name; this cannot be removed.
Now I'd like to annotate the total count of number.
My try annotate(invoice_count=models.Count('number')) always returns 1 even though there are multiple.
What am I doing wrong? Any pointers on how to properly achieve this without hacking around too much and without hitting the DB for every invoice?
Seems your problem in distinct('person'), which removes duplicates by person field.
Update
To complete your task you should
current_invoices = qs.order_by('person', '-created_on').distinct('person').values('number')
return qs.annotate(invoice_count=models.Count('number')).filter(number__in=current_invoices).order_by('person__last_name')
Here are models in my project:
class Tournament(models.Model):
title = models.CharField(max_length=150)
start_date = models.DateTimeField(verbose_name="Start date")
end_date = models.DateTimeField(verbose_name="End date")
participants = models.ManyToManyField(Participant)
def __unicode__(self):
return u"%s" % self.title
class Game(models.Model):
tournament = models.ForeignKey(Tournament)
number_of_tour = models.PositiveSmallIntegerField()
opponent_white = models.ForeignKey(Participant, related_name='opponent_white', blank=True)
opponent_black = models.ForeignKey(Participant, related_name='opponent_black', blank=True)
winner = models.CharField(max_length=150, choices=(('White', 'White'), ('Black', 'Black'), ('Draw', 'Draw')), blank=True)
elo_gained_white = models.FloatField(default=0)
elo_gained_black = models.FloatField(default=0)
My problem is that i want to know number of tours in each tournament. I'm trying to do it like this:
num_of_tours = Game.objects.filter(tournament=tournament_id).annotate(Count('number_of_tour', distinct=True)).count()
But this still returns number of Game objects related to a tournament with tournament_id. Could you show me the way to do it right?
Thanks.
I think I have the solution:
What he is looking for is the distinct count of number_of_tours per tournament.
Each game has a tour value, but those values can be double. so number_of_tours work like some kind of id.
Nervosa you can get the distinct value like so:
num_of_tours = Game.objects.filter(tournament=tournament_id).values('number_of_tour').distinct().count()
This should give you just that: the distinct number_of_tours per tournament
So you want to Sum the number_of_tour column for all games in a specific tournament? I think you may need aggregate instead of annotate and Sum instead of Count:
num_of_tours = Game.objects.filter(tournament=tournament_id).aggregate(total_num=Sum('number_of_tour', distinct=True))['total_num']
EDIT: Daniel Roseman pointed out that number_of_tour is probably an index. That makes much more sense. My bad. You will need to do a distinct on the whole query, then count that query. Like this:
num_of_tours = Game.objects.filter(tournament=tournament_id).distinct('number_of_tour').count()
That should count the number of Game objects that have a different number_of_tour. I believe that is what you want. Sorry for the confusion.
What I want is to be able to get this weeks/this months/this years etc. hotest products. So I have a model named ProductStatistics that will log each hit and each purchase on a day-to-day basis. This is the models I have got to work with:
class Product(models.Model):
name = models.CharField(_("Name"), max_length=200)
slug = models.SlugField()
description = models.TextField(_("Description"))
picture = models.ImageField(upload_to=product_upload_path, blank=True)
category = models.ForeignKey(ProductCategory)
prices = models.ManyToManyField(Store, through='Pricing')
objects = ProductManager()
class Meta:
ordering = ('name', )
def __unicode__(self):
return self.name
class ProductStatistic(models.Model):
# There is only 1 `date` each day. `date` is
# set by datetime.today().date()
date = models.DateTimeField(default=datetime.now)
hits = models.PositiveIntegerField(default=0)
purchases = models.PositiveIntegerField(default=0)
product = models.ForeignKey(Product)
class Meta:
ordering = ('product', 'date', 'purchases', 'hits', )
def __unicode__(self):
return u'%s: %s - %s hits, %s purchases' % (self.product.name, str(self.date).split(' ')[0], self.hits, self.purchases)
How would you go about sorting the Products after say (hits+(purchases*2)) the latest week?
This structure isn't set in stone either, so if you would structure the models in any other way, please tell!
first idea:
in the view you could query for today's ProductStatistic, than loop over the the queryset and add a variable ranking to every object and add that object to a list. Then just sort after ranking and pass the list to ur template.
second idea:
create a filed ranking (hidden for admin) and write the solution of ur formula each time the object is saved to the database by using a pre_save-signal. Now you can do ProductStatistic.objects.filter(date=today()).order_by('ranking')
Both ideas have pros&cons, but I like second idea more
edit as response to the comment
Use Idea 2
Write a view, where you filter like this: ProductStatistic.objects.filter(product= aProductObject, date__gte=startdate, date__lte=enddate)
loop over the queryset and do somthing like aProductObject.ranking+= qs_obj.ranking
pass a sorted list of the queryset to the template
Basically a combination of both ideas
edit to your own answer
Your solution isn't far away from what I suggested — but in sql-space.
But another solution:
Make a Hit-Model:
class Hit(models.Model):
date = models.DateTimeFiles(auto_now=True)
product = models.ForeignKey(Product)
purchased= models.BooleanField(default=False)
session = models.CharField(max_length=40)
in your view for displaying a product you check, if there is a Hit-object with the session, and object. if not, you save it
Hit(product=product,
date=datetime.datetime.now(),
session=request.session.session_key).save()
in your purchase view you get the Hit-object and set purchased=True
Now in your templates/DB-Tools you can do real statistics.
Of course it can generate a lot of DB-Objects over the time, so you should think about a good deletion-strategy (like sum the data after 3 month into another model MonthlyHitArchive)
If you think, that displaying this statistics would generate to much DB-Traffic, you should consider using some caching.
I solved this the way I didn't want to solve it. I added week_rank, month_rank and overall_rank to Product and then I just added the following to my ProductStatistic model.
def calculate_rank(self, days_ago=7, overall=False):
if overall:
return self._default_manager.all().extra(
select = {'rank': 'SUM(hits + (clicks * 2))'}
).values()[0]['rank']
else:
return self._default_manager.filter(
date__gte = datetime.today()-timedelta(days_ago),
date__lte = datetime.today()
).extra(
select = {'rank': 'SUM(hits + (clicks * 2))'}
).values()[0]['rank']
def save(self, *args, **kwargs):
super(ProductStatistic, self).save(*args, **kwargs)
t = Product.objects.get(pk=self.product.id)
t.week_rank = self.calculate_rank()
t.month_rank = self.calculate_rank(30)
t.overall_rank = self.calculate_rank(overall=True)
t.save()
I'll leave it unsolved if there is a better solution.