Calculate a number in a Django model from another model's data - python

I want to take data (amount_spent) from the field of each user and add those numbers up and display them in another field (total_revenue) from a different model (RevenueInfo).
from __future__ import unicode_literals
from django.contrib.auth.models import User
from django.db import models
from django import forms, views
# Create your models here.
#LoginInfo is being used, LoginForms in forms.py is
class LoginInfo(models.Model):
username = models.CharField('', max_length=10)
password = models.CharField('', max_length=15)
class ExtendedProfile(models.Model):
user = models.OneToOneField(User)
amount_spent = models.DecimalField(max_digits=6, decimal_places=2)
class RevenueInfo(models.Model):
total_amount_spent = models.DecimalField(max_digits=6, decimal_places=2, default=0)
total_revenue = models.ForeignKey(ExtendedProfile, null=True)
class Product(models.Model):
category = models.CharField(max_length=100)
name = models.CharField(max_length=100)
description = models.TextField()
#photo = models.ImageField()
price_CAD = models.DecimalField(max_digits=6, decimal_places=2)
quantity = models.DecimalField(max_digits=2, decimal_places=0, null=True)
How could I go about this? Would I iterate of each Usermodel and find User.amount_spent then add that to RevenueInfo.total_revenue? I'm not sure how to put that into code. Also I'm pretty sure I don't need both total_amount_spent and total_revenue but I feel like I need a ForeignKey

You could add a classmethod to the ExtendedProfile model to aggregate the amount_spent value for each User (which bypasses the need for a separate RevenueInfo model):
from django.db.models import Sum
class ExtendedProfile(models.Model):
....
#classmethod
def total_user_spend(cls):
return cls.objects.aggregate(total=Sum('amount_spent'))
Then you can get the total spend using ExtendedProfile.total_user_spend():
>>> ExtendedProfile.total_user_spend()
{'total': Decimal('1234.00')}

Yes, you can write a method for that in your model. There are 2 ways for it.
1) Writing a method that calculates the values and sets it to a instance value.
2) Writing a method that calculates the value and directly returns it.
For example purpose, here is the code for 2nd type.
# models.py
def total_amount_spent(self):
temp_values = [int(user.amount_spent) for user in ExtendedProfile.objects.all()]
return sum(temp_values)
And for using that value in views , but remeber it would be an integer by default
#views.py
value = RevenueInfo.total_amount_spent()

Avoid iterating over database entities in python (it can get really slow). Look into aggregation, it allows you to efficiently get sum (average, max, min, etc...) of values in a database:
>>> from django.db.models import Sum
>>> ExtendedProfile.objects.all().aggregate(Sum('amount_spent'))
{'amount_spent__sum': Decimal('1234.56')}
>>> # ... do whatever you want with the return value

Related

Django querying data out of models and get sum of field

I'm just beginning with Django and have the following question:
I have set up a model looking like
class Automation_Apps(models.Model):
app_name = models.CharField(max_length=50)
app_description = models.TextField(blank=True)
view_function_name = models.CharField(max_length=50)
time_saver_min = models.IntegerField()
implementation_date = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.app_name
class Automation_Usage(models.Model):
used_app = models.ForeignKey(Automation_Apps, on_delete=models.CASCADE)
used_by_user = models.ForeignKey(User, on_delete=models.CASCADE)
used_on_date = models.DateTimeField(auto_now_add=True)
I would like to query it like:
Select Sum(time_saver_min)
from Automation_Apps, Automation_Usage
where Automation_Usage.used_app = Automation_Apps.app_name
The goal of this is the following:
Automation_App will have the Apps and a field, how much time will be saved by using this app
Automation_Usage will have the records, when a user is utilizing the app.
With my query I would like to pull how often the apps where used and how much time was saved by using it (which comes from the Automation_App).
I would like to do this in my view.
To get sum of time utilized over all apps by each user:
from django.db.models import Sum
Automation_Usage.objects.values('used_by_user').annotate(time=Sum('used_app__time_saver_min'))
To get number of users and sum of time utilized for each app:
from django.db.models import Sum, Count
Automation_Usage.objects.values('used_app').annotate(users=Count('used_by_user'), time=Sum('used_app__time_saver_min'))
You can use the Model Sum to Sum the value of your model.
Do it this way;
from django.db.models import Sum
Then
data = Model.objects.aggregate(Sum('time_saver_min'))['time_saver_min__sum'] print(data)
You can use annotate in Django ORM.
My solution:
Sum(time_saver_min) based on Automation_Usage.used_app = Automation_Apps.app_name
=> queryset = Automation_Usage.objects.values('used_app').annotate(Coalesce(Sum('used_app__time_saver_min'), Value(0)))
=> print(queryset['used_app__time_saver_min__sum'])

Django how to auto generate a unique number/string for a model attribute

So i been trying to generate 10 digit unique random number in django model without making it my primary key. My model is
Class Transaction(models.Model):
Referrence_Number = models.Charfield(max_lenght = 10, blank=True, editable=False, unique=True)
I know that Django has special feature inside it to generate random string that i came to know after reading documentation.
from django.utils.crypto import get_random_string
get_random_string(10).lower()
But my problem is how can i incorporate this get_random_string function inside my django transaction model in my Transcation_number Instance. I m actually new to django and cant able to figure it that out.
I normally use uuid, the uuid string will set to the field when create object
the_field = models.CharField(max_length=36, default=uuid.uuid4)
You can set a default on your attribute to a function that generates whatever Referrence_Number you'd like. Just don't set a Referrence_Number on record creation
For example, your model attribute would look like this:
from utils import create_new_ref_number
Class Transaction(models.Model):
Referrence_Number = models.Charfield(
max_length = 10,
blank=True,
editable=False,
unique=True,
default=create_new_ref_number
)
And in your utils.py file, you'd have something like this:
import random
def create_new_ref_number():
return str(random.randint(1000000000, 9999999999))
While you could simply throw the randint function in the default parameter, you should abstract it away so you can perform extra niceties like verify no record exists with that same id, etc.
This will automatically create a unique Referrence_Number
import random
class Transaction(models.Model):
Referrence_Number = models.Charfield(
max_length = 10,
blank=True,
editable=False,
unique=True,
default=create_new_ref_number
)
def create_new_ref_number():
not_unique = True
while not_unique:
unique_ref = random.randint(1000000000, 9999999999)
if not Transaction.objects.filter(Referrence_Number=unique_ref):
not_unique = False
return str(unique_ref)
Perhaps you could override the save() method on the Model. Something like this:
def save(self,*args, **kwargs):
self.Reference_Number = get_random_string(10).lower()
super(Transaction, self).save(*args, **kwargs)

How to reduce quantity of an item in main table when it is being used in another table - django

I am creating my model in Django and I have a many to many relationship between supplies and van kits. The idea is that an "item" can belong to many "van kits" and a "van kit" can have many " items. I created an intermediary model that will hold the relationship, but I am struggling to figure out a way to relate the quantity in the van kit table to the quantity in the main supplies table. For example, if I wanted to mark an item in the van kit as damaged and reduce the quantity of that supply in the van kit, I would also want to reduce the total count of that supply in the main "supplies" table until it has been replenished. I am thinking that maybe I'll have to create a function in my views file to carry out that logic, but I wanted to know if it could be implemented in my model design instead to minimize chances of error. Here's my code:
class supplies(models.Model):
class Meta:
verbose_name_plural = "supplies"
# limit the user to selecting a pre-set category
choices = (
('CREW-GEAR','CREW-GEAR'),
('CONSUMABLE','CONSUMABLE'),
('BACK-COUNTRY','BACK-COUNTRY')
)
supplyName = models.CharField(max_length=30, blank=False) # if they go over the max length, we'll get a 500 error
category = models.CharField(max_length=20, choices = choices, blank=False)
quantity = models.PositiveSmallIntegerField(blank=False) # set up default
price = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True) # inputting price is optional
def __str__(self):
return self.supplyName
class van_kit(models.Model):
supply_name = models.ManyToManyField(supplies, through='KitSupplies',through_fields=('vanKit','supplyName'), related_name="supplies")
van_kit_name = models.CharField(max_length=100)
vanName = models.ForeignKey(vans, on_delete=models.CASCADE)
def __str__(self):
return self.van_kit_name
class KitSupplies(models.Model):
supplyName = models.ForeignKey(supplies, on_delete=models.CASCADE)
vanKit = models.ForeignKey(van_kit, on_delete=models.CASCADE)
quantity = models.PositiveSmallIntegerField(blank=False)
def __str__(self):
return str(self.supplyName)
class Meta:
verbose_name_plural = 'Kit Supplies'
I am fairly new to django, I have to learn it for a class project so if my logic is flawed or if a better way to do it is obvious, please respectfully let me know. I'm open to new ways of doing it. Also, I've read through the documentation on using "through" and "through_fields" to work with the junction table, but I'm worried I may not be using it correctly. Thanks in advance.
One option would be to drop/remove the field quantity from your supplies model and just use a query to get the total quantity.
This would be a bit more expensive, as the query would need to be run each time you want to know the number, but on the other hand it simplifies your design as you don't need any update logic for the field supplies.quantity.
The query could look as simple as this:
>>> from django.db.models import Sum
>>> supplies_instance.kitsupplies_set.aggregate(Sum('quantity'))
{'quantity__sum': 1234}
You could even make it a property on the model for easy access:
class supplies(models.Model):
...
#property
def quantity(self):
data = self.kitsupplies_set.aggregate(Sum('quantity'))
return data['quantity__sum']

Django-Tables2 add extra columns from dictionary

I apologize if this question has been asked before but I couldn't find my specific use case answered.
I have a table that displays basic product information. Product details such as price, number of sales, and number of sellers are scraped periodically and stored in a separate database table. Now I want to display both the basic product information and scraped details in one table on the frontend using tables2. To do this, I wrote a function in my Product model to fetch the latest details and return them as a dictionary this way I can use a single Accessor call.
# models.py
class Product(models.Model):
created_at = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=256)
brand = models.ForeignKey(Brand)
category = models.CharField(max_length=128, choices=CATEGORY_CHOICES)
def __unicode__(self):
return self.name
def currentState(self):
currentDetailState = ProductDetailsState.objects.filter(
product=self
).latest('created_at')
# return current details as a dictionary
return {
price: currentDetailState.price,
num_sellers: currentDetailState.num_sellers,
num_sales: currentDetailState.num_sales
}
class ProductDetailsState(models.Model):
product = models.ForeignKey(Product)
created_at = models.DateTimeField(auto_now_add=True)
price = models.DecimalField(max_digits=6, decimal_places=2, null=True)
num_sellers = models.IntegerField(null=True)
num_sales = models.IntegerField(null=True)
def __unicode__(self):
return self.created_at
# tables.py
class ProductTable(tables.Table):
productBrand = tables.Column(
accessor=Accessor('brand.name'),
verbose_name='Brand'
)
currentRank = tables.Column(
accessor=Accessor('currentRank')
)
class Meta:
model = Product
...
How do I now use this returned dictionary and split it into columns in my Product table? Is there another way to use an Accessor than how I am doing it?
You can use the Accessor to traverse into the dict, so something like this should work:
class ProductTable(tables.Table):
# brand is the name of the model field, if you use that as the column name,
# and you have the __unicode__ you have now, the __unicode__ will get called,
# so you can get away with jus this:
brand = tables.Column(verbose_name='Brand')
currentRank = tables.Column()
# ordering on the value of a dict key is not possible, so better to disable it.
price = tables.Column(accessor=tables.A('currentState.price'), orderable=False)
num_sellers = tables.Column(accessor=tables.A('currentState.num_sellers'), orderable=False)
num_sales = tables.Column(accessor=tables.A('currentState.num_sales'), orderable=False)
class Meta:
model = Product
While this works, sorting is also nice to have. In order to do that, your 'currentState' method is a bit in the way, you should change the QuerySet you pass to the table. This view shows how that could work:
from django.db.models import F, Max
from django.shortcuts import render
from django_tables2 import RequestConfig
from .models import Product, ProductDetailsState
from .tables import ProductTable
def table(request):
# first, we make a list of the post recent ProductDetailState instances
# for each Product.
# This assumes the id's increase with the values of created_at,
# which probably is a fair assumption in most cases.
# If not, this query should be altered a bit.
current_state_ids = Product.objects.annotate(current_id=Max('productdetailsstate__id')) \
.values_list('current_id', flat=True)
data = Product.objects.filter(productdetailsstate__pk__in=current_state_ids)
# add annotations to make the table definition cleaner.
data = data.annotate(
price=F('productdetailsstate__price'),
num_sellers=F('productdetailsstate__num_sellers'),
num_sales=F('productdetailsstate__num_sales')
)
table = ProductTable(data)
RequestConfig(request).configure(table)
return render(request, 'table.html', {'table': table})
This simplifies the table definition, using the annotations created above:
class ProductTable(tables.Table):
brand = tables.Column(verbose_name='Brand')
currentRank = tables.Column()
price = tables.Column()
num_sellers = tables.Column()
num_sales = tables.Column()
class Meta:
model = Product
You can find the complete working django project at github

How to create custom choice field in Django?

I'm trying to create a form in which user fill in language,description and level of the language (intermediate, advanced...).
The language model:
class Language(models.Model):
shortcut = models.CharField(max_length=6)
name = models.CharField(max_length=50)
first_level_price = models.FloatField() # Basic
second_level_price = models.FloatField() # Intermediate
third_level_price = models.FloatField() # Advanced
def __str__(self):
return self.shortcut+': '+self.name
For each language there exist three levels and prices (first_level_price,second_level_price,third_level_price).
Now, I want user to fill the form. One of the forms should be the level. User just would chose either 'Basic','Intermediate' or 'Advanced'. According to this choice, there would be the price counted.
So if user have chosen 'Basic', there would be price according to first_level_price of the language.
I've tried many ways but no one worked.
level = forms.ChoiceInput(choices=('Basic','Intermediate','Advanced'))
You can update your model like this:
class Language(models.Model):
PRICE_CHOICES = (
('first_level_price', 'Basic'),
('second_level_price', 'Intermediate'),
('third_level_price', 'Advanced'),
)
first_level_price = models.FloatField() # Basic
second_level_price = models.FloatField() # Intermediate
third_level_price = models.FloatField() # Advanced
shortcut = models.CharField(max_length=6)
name = models.CharField(max_length=50)
level = models.CharField(max_length=50, choices=PRICE_CHOICES)
def __str__(self):
return self.shortcut+': '+self.name
Now you can just create a ModelForm using this Model and it should work the way you expect.
When an user selects first_level_price aka Basic membership, you can now grab the price from the first_level_price field.
The choices should a tuple of pairs. The first value in the pair is the data you get into your form instance, the second value of the pair is the displayed value in your template:
level = forms.ChoiceField(choices=(('basic', 'Basic'),
('intermediate', 'Intermediate'),
('advanced', 'Advanced')))
Check django doc details about the choices.

Categories

Resources