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.
Related
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
I'm working on my first django project which is a sport betting game.
My models are:
class Game(models.Model):
home_team = models.CharField(max_length=200)
away_team = models.CharField(max_length=200)
home_goals = models.IntegerField(default=None)
away_goals = models.IntegerField(default=None)
class Bet(models.Model):
gameid = models.ForeignKey(Game, on_delete=models.CASCADE)
userid = models.ForeignKey(User, on_delete=models.CASCADE)
home_goals = models.IntegerField()
away_goals = models.IntegerField()
score = models.IntegerField(default=None, null=True)
logic for calculating scores is:
WHEN polls_bet.home_goals = polls_game.home_goals AND polls_bet.away_goals = polls_game.away_goals THEN 2
WHEN polls_game.home_goals > polls_game.away_goals AND polls_game.home_goals > polls_game.away_goals THEN 1
WHEN polls_bet.home_goals < polls_bet.away_goals AND polls_game.home_goals < polls_game.away_goals THEN 1
ELSE 0
I was able to solve it easily using database view that combines all data in one table but it seems that it does not play well with django migrations..
So I was thinking about sql trigger after update of game goals, but then I don't know how to pass conditions like user's id.
2nd idea is to create additional 'scores' table with:
gameid,
userid,
betid,
score
But then again I don't know how to calculate scores.
Please advice how this should be done properly, without using sql view. I appreciate all answers!
You can define a 'score' property on the Bet model to solve this easily. Please refer to the documentation here.
Your property implementation will be something like:
#property
def score(self):
if (self.home_goals == self.game__home_goals and
self.away_goals == self.game__away_goals):
return 2
if (self.game__home_goals > self.game__away_goals):
return 1
if (self.home_goals < self.away_goals and
self.game__home_goals < self.home_goals):
return 1
return 0
On a side note, the normal naming convension for a foreignkey relation is the model name in lowercase. So it becomes 'game' and 'user' instead of 'gameid' and 'userid'. Also I believe you have some typos on the second condition.
I have this two models.
class City(models.Model):
city = models.CharField(max_length=200)
country = models.CharField(max_length=200)
class CityTranslation(models.Model):
city = models.ForeignKey(City)
name = models.CharField(max_length=200)
lang = models.CharField(max_length=2)
prio = models.IntegerField()
Every city can have multiple translated names within one language.
So I want to get all City's with country="Poland". If a corresponding City has one or more CityTranslations with lang=name. I want to get only the first ordered by prio.
I am doing something like that now.
City.objects.filter(country="Poland", citytranslation__land="pl").annotate(transname=F("alt_names__name"))
But this is not working, because:
If there is a City without a CityTranslation it want be listed
If there are multiple CityTranslation's they all will be shown. But I just want the first. (... .ordered_by('prio').first())
Any idea?
EDIT:
Solved it by using a #property field, which is ordering my CityTranslation by prio and picks the first one:
#propert
def transcity(self):
return self.citytranslation.filter(lang="pl").order_by('-prio').first()
def magic(passed_country="Poland", passed_lang="pl")
# I want to get all City's with country="Poland".
cities = City.objects.filter(country=passed_country)
# If a corresponding City has one or more CityTranslations with lang=name. I want to get only the first ordered by prio.
suitable_cities = cities.filter(citytranslation__lang=passed_lang)
if suitable_cities.exists()
first_matching_city = suitable_cities.orderby('prio').first()
else:
first_matching_city = cities.orderby('prio').first()
return first_matching_city
May need to set up a relatedname on citytranslation.
May not need orderby if you plan on ordering by ID anyways.
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
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.