Django calculation based on calculated value - python

I need your help once more. I am working on a project for my wargaming group. It is a simple ranking site. So we have players, they participate in tournaments and get points. I got to the point where I am able to assign players to tournaments, assign place they took at the tournament to their name.
Now I have to calculate points. Algorithm is simple, but I have problems passing a value from Tournament model to Ranking. Each Tournament has a calculated rating (based on other things, mostly bigger tournament, bigger rating) and in other models, I was unable to use it and need your help with it. On top of that, it would be awesome if changing a rating value in Tournament would force an update of all dependent calculations.
So we have models like that:
class Player(models.Model):
class Meta:
name = models.CharField(max_length=50)
nicname = models.CharField(max_length=50,blank=True)
army = models.CharField(max_length=50)
def __unicode__(self):
return self.name
def __str__(self):
return self.name
class Tournament(models.Model):
class Meta:
name = models.CharField(max_length=100)
date = models.DateTimeField('date')
player_num = models.IntegerField
points = models.FloatField(default=1000.00)
def __unicode__(self):
return self.name
def __str__(self):
return self.name
And then I have a ranking model of this kind:
class TournamentStandings(models.Model):
tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE)
player = models.ForeignKey(Player, on_delete=models.CASCADE)
player_place = models.FloatField
In admin.py I do calculations for TournamentAdmin:
fields = ['name', 'date', 'player_num', 'points', 'get_rating']
def get_rating(self, obj):
return obj.points / 100.00
And now I would like to make calculation for TournamentStandingsAdmin:
def player_points(self, obj):
return (obj.tournament.player_num/obj.player_place)* obj.tournament.get_rating
But the result is an error
'Tournament' object has no attribute 'get_rating'
So my guess is my calculated get_rating is not a true model field but how to work around that?

as #Daniel suggested you need to add get_rating method to your Tournament model.
class Tournament(models.Model):
name = models.CharField(max_length=100)
date = models.DateTimeField('date')
player_num = models.IntegerField
points = models.FloatField(default=1000.00)
....
def get_rating(self):
return obj.points / 100.00
After that you can call the method with a Tournament object as follows:
obj.tournament.get_rating

Related

What is Football game statistics Django Model logic?

I am creating an app that visualizes football game statistics on Django. I take data from https://fbref.com/ as a CSV file. I have a python script that cleans data and create data frame with ['Player', 'Shots', 'SCA', 'Touches', 'Pass', 'Carries', 'Press', 'Tackled', 'Interceptions', 'Blocks'] columns (I can add Team name, Game Date, Home/Away or whatever).
And now I need to create models to store this data. I don't know what models do I need.
Option 1 (only one model):
class GameStats(models.Model):
date = models.DateField()
team_name = models.CharField(max_length=20)
home_away = models.CharField(max_length=20, choices=HOME_AWAY)
name = models.CharField(max_length=20)
number = models.IntegerField()
age = models.IntegerField()
shots = models.IntegerField()
SCA = models.IntegerField()
touches = models.IntegerField()
passes = models.IntegerField()
But it will give one Row of data. Technically, I can group rows by Team_name and Date.
Option 2:
from django.db import models
class Team(models.Model):
name = models.CharField(max_length=200)
league = models.CharField(max_length=200)
def __str__(self):
return self.name
class Player(models.Model):
name = models.CharField(max_length=200)
number = models.IntegerField()
age = models.IntegerField()
team = models.ForeignKey(Team, related_name='team', on_delete=models.CASCADE)
def __str__(self):
return self.name
class HomeTeam(models.Model):
team = models.ForeignKey(Team, related_name='home_team', on_delete=models.CASCADE)
players = models.ManyToManyField(Player)
def __str__(self):
return self.team
class AwayTeam(models.Model):
team = models.ForeignKey(Team, related_name='away_team', on_delete=models.CASCADE)
players = models.ManyToManyField(Player)
def __str__(self):
return self.team
class Game(models.Model):
title = models.CharField(max_length=200)
homeTeam = models.OneToOneField(HomeTeam, on_delete=models.CASCADE)
awayTeam = models.OneToOneField(AwayTeam, on_delete=models.CASCADE)
def __str__(self):
return self.title
In Option 2 I can understand where to put Game statistics and a list of the players.
Would be great if someone could write proper models. I really stack!
There is no one way to do it, so every answer you get for this question is going to be opinionated. That being said, for my personal preference, I would prefer the first modal approach as it is simple and has all the data in one place. I don't see a point of separating the columns into different database tables unless you know these different subset of columns are going to be used in some other models later down the line. Even then, you can always link the whole model with a ForeignKey to the new model and only use the data that you need.
Another reason I prefer the first approach is that the data does not have many columns, so dividing it into many smaller parts is making it complicated for no reason. You will be querying the database more in second approach, and later down the line, it may become difficult to keep track of things. But that is my opinion, so take it with a grain of salt.

How do you use a Custom Model Manager method for a related Many-to-Many model field?

I am working on a small project, mostly for learning, and I am running into an issue with accessing expected custom model manager methods on some related M2M type models. The application I am trying to implement is a travel calculator, that can determine total fuel requirements and weights for all vehicles in a particular trip.
models.py
from django.db import models
from django.db.models import Sum
# Create your models here.
class TripManager(models.Manager):
def get_vehicle_weight(self):
return self.get_queryset().all().aggregate(total_weight=Sum('vehicle__weight'))
def get_vehicle_fuel(self):
return self.get_queryset().all().aggregate(total_fuel=Sum('vehicle__fuel'))
class Vehicle(models.Model):
"""A vehicle may belong to multiple businesses and multiple trips at once."""
name = models.CharField(max_length=50, help_text="The common name of the vehicle.")
fuel_capacity = models.IntegerField(default=0, help_text="The total fuel capacity in gallons.")
burn_rate = models.FloatField(default=0, help_text="The burn rate of fuel in gal/h.")
weight = models.FloatField(default=0, help_text="The weight of the vehicle in pounds.")
def __str__(self):
return self.name
class Business(models.Model):
""""""
name = models.CharField(max_length=50, help_text="The name of the business.")
def __str__(self):
return self.name
class EmployeeType(models.Model):
"""Employee types can belong to many businesses."""
name = models.CharField(max_length=50, help_text="The title/role of a type of employee.")
def __str__(self):
return self.name
class Trip(models.Model):
"""A trip will be the primary object, composed of other objects that are associated with the trip."""
name = models.CharField(max_length=128)
vehicles = models.ManyToManyField(Vehicle, through="TripVehicle", through_fields=('trip', 'vehicle'),)
employee_types = models.ManyToManyField(EmployeeType, through="TripEmployeeType", through_fields=('trip', 'employee_types'),)
businesses = models.ManyToManyField(Business)
objects = TripManager()
class Meta:
base_manager_name = 'objects'
def __str__(self):
return self.name
class TripVehicle(models.Model):
trip = models.ForeignKey(Trip, on_delete=models.CASCADE)
business = models.ForeignKey(Business, on_delete=models.CASCADE)
vehicle = models.ForeignKey(Vehicle, on_delete=models.CASCADE)
quantity = models.IntegerField()
class TripEmployeeType(models.Model):
trip = models.ForeignKey(Trip, on_delete=models.CASCADE)
business = models.ForeignKey(Business, on_delete=models.CASCADE)
employee_types = models.ForeignKey(EmployeeType, on_delete=models.CASCADE)
quantity = models.IntegerField()
views.py
def home(request):
trip = Trip.objects.all()
context = {
'trip' : trip,
}
return render(request, 'home.html', context=context)
The above 'home' page is just for my testing. I've pre-populated the DB with some basic vehicles, businesses, and employee types. However, when navigating to the 'home' url, I get the below error:
'QuerySet' object has no attribute 'get_vehicle_weight'
I am expecting to be able to create a 'trip' instance, then determine the sums of every vehicle attribute * the quantity of that type of vehicle for each trip. However, accessing the fields through 'trip.vehicles.get_vehicle_weight()' does not work.
What I expect to see:
# 3 cars with a weight of 1500 lbs each
trip.vehicles.get_vehicle_weight()
{'total_weight' : 4500}
What am I messing up with my implementation?

How do I create a method in django models that returns the sum of a field in that same model?

I'm new to Django and I have two models set up like this currently. I want to calculate, in the Climber model, that returns the total points that climber has earned by accessing the climbs_completed field. How can I go about doing this? In other words, how do I sum up the points for each Climb in climbs_completed? Is there a better way to do this besides writing a method? Thank you in advance!
class Climb(models.Model):
name = models.CharField(max_length=20, default='')
grades = [('v'+str(i),'v'+str(i))for i in range(0,13)]
grade = models.CharField(max_length=3, choices=grades, default='v-1')
weeks = [(i,i)for i in range(1,13)]
week = models.IntegerField(choices=weeks, default=0)
points = models.IntegerField(default=0)
def __str__(self):
return self.name
class Climber(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
grades = [('v'+str(i),'v'+str(i))for i in range(0,13)]
highest_grade = models.CharField(max_length=3, choices=grades, default='v0')
team = models.ForeignKey(Team, null=True, on_delete=models.SET_NULL)
climbs_completed = models.ManyToManyField(Climb, blank=True)
def __str__(self):
return self.user.username
# for each climbs_completed, sum up the points
def total_score(self):
pass
You can use Sum and add the total climb points as annotated value like this:
from django.db.models import Sum
climbers = Climber.objects.annotate(total_score=Sum('climbs_completed__points'))
print(climbers.values('pk', 'total_score'))
Or you can use aggregation in the total_score method, like this:
class Climber(models.Model):
...
def total_score(self):
return self.climbs_completed.aggregate(total_score=Sum('points'))['total_score']
First method is more efficient if you want to get values from bunch of climbers and do it in one database hit.

schema design for ecommerce product

I am exploring models in django where I am trying to create a model for e-commerce product. The schema that I have designed is follow for now
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=80)
def __str__(self):
return self.name
class Product(models.Model):
name = models.CharField(max_length=100)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
total_stock = models.PositiveIntegerField(default=0)
def __str__(self):
return self.name
class Attribute(models.Model):
'''
attribute can be like color, material, size and many more
'''
name = models.CharField(max_length=80)
def __str__(self):
return self.name
class AttributeValue(models.Model):
'''
Values for the selected attribute like for size attr
the values can be Large, Medium, Small and etc
'''
name = models.CharField(max_length=100)
attribute = models.ForeignKey(Attribute, on_delete=models.CASCADE)
price = models.DecimalField(decimal_places=2, max_digits=10)
discount = models.DecimalField(decimal_places=2, max_digits=10)
stock = models.PositiveIntegerField(default=0)
def __str__(self):
return self.name
class ProductAttribute(models.Model):
'''
Associate Particular attribute to Particular product
'''
product = models.ForeignKey(Product, on_delete=models.CASCADE)
attribute = models.ForeignKey(Attribute, on_delete=models.CASCADE)
def __str__(self):
return self.product.name
class ProductImage(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
image = models.ImageField(upload_to = 'pic_folder/')
def __str__(self):
return self.product.name
My question is when I researched for scalable e-commerce product design (scalable in terms of better table relation and covering most of the factors in e-commerce), I saw various tables like
ProductVariant, ProductVariantImage, ProductOptions and etc. So I got confused on those terminology. Can anyone help me to make me understand that with example and how can i adjust those table in my models.py?
Here is the link
https://i.imgur.com/qGDBz29.png
I think you just want to understand the terms and how they relate to each other, correct? And once you understand, you can decide how to adjust the schema & models.
ProductVariant: A "version" of the Product. From an e-commerce point of view, this can mean something that doesn't neatly fit into the Attribute or AttributeValue models. For example, a product can have a variant:
size
country of origin
language
men only, women only, unisex
different price point (high-end vs. low-end, public vs. private)
I think you can do without a ProductVariant model, and just get things to work using attributes. It may be meaningful to use ProductVariant as a reference to a pre-existing Product (foreign key constraint on Product.id). See here and here.
ProductVariantImage: A version of the ProductImage.
ProductOptions: The options for the Product. You can just use Attributes instead. This table/model doesn't seem to be any different than what Attributes & AttributeValues already have.

Issue with Django foreign key assignment

I have a series of related models. Country -> League -> Team -> Player. The model works fine relating country to league and league to team, but the team id is different as teams play in more than one competition. To deal with this I've added a ref column with an id for each team. I would like to use this ref column as the Foreign Key in my player model but I'm getting errors when I try to parse the data to the Postgres database.
I've tried using to_field and unique=True but still end up with an error. I've taken a look around but haven't found a solution yet.
Here is my models code:
from django.conf import settings
from django.db import models
from django.utils import timezone
import datetime
class Country(models.Model):
objects = models.Manager()
name = models.CharField(max_length=50,default="TBA")
id = models.IntegerField(primary_key=True,default=0)
def __str__(self):
return self.name
def __unicode__(self):
return u'%s' % self.name
class League(models.Model):
objects = models.Manager()
name = models.CharField(max_length=100,default="TBA")
id = models.IntegerField(primary_key=True,default=0)
country = models.ForeignKey(Country,on_delete=models.CASCADE)
def __str__(self):
return self.name
def __unicode__(self):
return u'%s' % self.name
class Team(models.Model):
objects = models.Manager()
name = models.CharField(max_length=100,default="TBA")
id = models.IntegerField(primary_key=True,default=0)
league = models.ForeignKey(League,on_delete=models.CASCADE)
ref = models.IntegerField(default=0)
def __str__(self):
return self.name
def __unicode__(self):
return u'%s' % self.name
class Player(models.Model):
objects = models.Manager()
name = models.CharField(max_length=64)
id = models.IntegerField(primary_key=True)
first_name = models.CharField(max_length=64,default="Unknown")
last_name = models.CharField(max_length=64,default="Unknown")
nationality = models.CharField(max_length=64,default="Unknown")
date_of_birth = models.DateField(default = datetime.date.today)
position = models.CharField(max_length=64, default="Unknown")
team_ref =models.ForeignKey(Team,to_field="ref",on_delete=models.CASCADE)
def __str__(self):
return self.name
IMHO, I think you model design is bit wrong. You should make Team to League relation as ManyToMany Field. So that, a Team can be assigned to multiple Leagues. If you want to maintain different Squads for different Tournaments, then you should create a new Model Named Squads and use it as through, and make a many to many relation to that Squads Model from Player. For example:
class Team(models.Model):
# other fields
league = models.ManyToManyField(League, through="Squad")
class Squad(models.Model):
team = models.ForeignKey(Team)
league = models.ForeignKey(League)
class Player(models.Model):
squad = models.ManyToManyField(Squad)

Categories

Resources