Django: how to sum up numbers in django - python

i want to calculate the total sales and display the total price, i dont know how to write the function to do this. I have tried writing a little function to do it in models.py but it working as expected. This is what i want, i have a model named UserCourse which stored all the purchased courses, now i want to calculate and sum up all the price of a single course that was sold.
models.py
class Course(models.Model):
course_title = models.CharField(max_length=10000)
slug = models.SlugField(unique=True)
price = models.IntegerField(default=0)
class UserCourse(models.Model):
user = models.ForeignKey(User , null = False , on_delete=models.CASCADE)
course = models.ForeignKey(Course , null = False , on_delete=models.CASCADE)
date = models.DateTimeField(auto_now_add=True)
and also how do i filter this models in views.py so i can display the total price of courses a particular creator have sold.

Try this approach using aggregate where you filter all UserCourse objects from a particular course, then run an aggregate on the price for that course:
my_course = Course.objects.first()
total_amount_sold = UserCourse.objects.filter(
course=my_course,
).aggregate(total_amount_sold=Sum("course__price"))["total_amount_sold"]

You can use the aggregate functionality in your view:
from django.db.models import Sum
price_total = UserCourse.objects.filter(
course__title="a title"
).aggregate(
aggregate_price = Sum("course__price")
)["aggregate_price"]
To break it down:
First we get filter all the UserCourse objects where the course has a title of "a title"
Then we aggregate them, finding the sum of the course prices for all those courses picked up by the filter
By naming a variable in our Sum expression, the queryset will now have a named value that we can get by the name ['aggregate_price'] and assign to price_total
Now you can pass price_total via context to use in your template.
You can specify other filters to get price totals for different querysets. So if your creator is the user field in your UserCourse model, you can repeat the process with user__username = "my_username" as the filter

Related

Django 4.0. Updating field once every day

I have a Book model that has a integer field count which is the number of users that have this book in their readlist. Users can add a book to their ReadList model (many to many field). I want to update the count in the book model once a day...how should I go about doing this?
Will be using this to displaying trending books and book rank based on user count.
Book Model:
class Book(models.Model):
name = models.CharField(max_length=150, unique=True)
description = models.TextField()
user_count = models.IntegerField()
pages = models.IntegerField()
genres = models.ManyToManyField(Genre)
def __str__(self):
return self.name
ReadList Model:
class ReadList(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
readlist = models.ManyToManyField(Book, related_name='readlist', blank=True)
def __str__(self):
return self.user.username
Django unfortunately doesn't do scheduling very well. You can actually generate this information already, via the related_name, with the advantage this will be realtime!
Let's start with a queryset for all books. Imagine you put this in one of your views.
books = Book.objects.all()
Now you have all your books, but no data on booklist numbers. So we can annotate that information via the query
from django.db.models import Count
books = Book.objects.all().annotate(user_count = Count('readlist')
Useful, but in no particular order, lets arrange it from highest to lowest.
books = Book.objects.all().annotate(user_count = Count('readlist') .order_by('-user_count')
But do we want it for every book in the store? Let's limit to the top 10.
books = Book.objects.all().annotate(user_count = Count('readlist') .order_by('-user_count')[:10]
The books don't necessarily know their own place in the heirarchy, though. Lets cycle through them and give them another impromptu field (Querysets aren't really indexed, so we can do this ourselves)
for index, book in enumerate(books):
book.placing = index
Pass context['books'] = books to your template and you should be able to do the following:
{% for book in books %}
Title:{{book.title}} <br>
Position:{{book.placing}} <br>
In :{{book.user_lists}} reading lists<br>
And there you have it, an up to the minute list of the top 10 books sorted by user_count without having to use external scheduling. Make sure you add the palcing last, as any further DB queries on the queryset will cause the query to be remade.

Show Last month progress of user on base of actions on website django

I have designed my database like this
user = models.ForeignKey(Profile, unique=True, on_delete=models.CASCADE)
signup = models.FloatField(default=10)
signup_from_user_link = models.FloatField(default=0)
post_review = models.FloatField(default=0)
total = models.FloatField(default=0)
I am calculating overall progress of user by adding all these values . But Now I have to make a change and I want to show progress of last month. Like in last month how much user added in every filed . What will be easiest way to get value of last month addition into these filed.
Any help would be highly appreciated. thanks
You can create a model that stores the time of creation and a "value" for an event (like submitting a review), every time a user performs the action you create an instance of this model
class Action(models.Model):
user = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name='actions')
timestamp = models.DateTimeField(auto_now_add=True)
value = models.FloatField()
You can then use aggregation to sum all the values for the event in the last month for a user with this query
total_for_last_month = user.actions.filter(
timestamp__gt=datetime.datetime.now() - dateutil.relativedelta(months=1)
).aggregate(
total=Sum('value')
)['total']
You would probably want to rename the model as "Review" or something and might want to add more fields
If you wish to calculate the total for a queryset of profiles then you can annotate each one
users = Profile.objects.filter(
actions__timestamp__gt=datetime.datetime.now() - dateutil.relativedelta(months=1)
).annotate(
total=Sum('actions__value')
)

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']

Updating Many-to-Many relation

I have 3 models (simplified):
class Product(models.Model):
category = models.ForeignKey('Category', related_name='products', to_field='category_name')
brand = models.ForeignKey('Brand', related_name='products', to_field='brand_name')
class Brand(models.Model):
brand_name = models.CharField(max_length=50)
categories = models.ManyToManyField('Category', related_name='categories')
class Category(models.Model):
category_name = models.CharField(max_length=128)
I want to change a Category in admin to a bunch of products, i have a custom admin function written for that. After that I need to update Brand-Categories Many-to-Many relation to check if that Category is still available for a specific Brand. I have written this function:
def brand_refresh():
brands = Brand.objects.all().prefetch_related('shops', 'categories')
products = Product.objects.select_related('shop', 'brand', 'category')
for brand in list(brands):
for category in brand.categories.all():
if not products.filter(category=category).exists():
brand.categories.remove(category)
for product in list(products.filter(brand=brand).distinct('category')):
if product.category not in [None, category]:
brand.categories.add(product.category)
Seems to me this monstro is working, but it takes 2 hours to loop over all cycles (i have ~220k products, 4k+ brands, and ~500 categories). I there any better way to update M2M relation here? I think .prefetch_related() should help here, but what I have now seems have no effect.
Here's a solution for the first part of your loop:
You should try this on a disposable local copy of your database and check that everything works well before running these in production:
from django.db.models import Count
# get a list of all categories which have no products
empty_categories = Category.objects.annotate(product_count=Count('products')).filter(product_count=0).values_list('id', flat=True)
# delete association of empty categories in all brands
Brand.categories.through.objects.filter(category_id__in=list(empty_categories)).delete()
For the second part, perhaps you can do something like this, though I'm not convinced if it's any faster (or even correct as it is):
for brand in Brand.objects.all():
# get a list of categories of all products in the brand
brand_product_categories = brand.products.all().value_list('category__id', flat=True).distinct()
# get the brand's categories
brand_categories = Category.objects.filter(category__brand=brand).value_list('id', flat=True)
# get elements from a not in b
categories_to_add = set(brand_product_categories) - set(brand_categories)
for category_id in categories_to_add:
brand.categories.add(category_id)

admin django. Add the total price in admin

hello I have to add a line or for example total_price = 123312 in admin
models.py
class Exit(models.Model):
description= models.CharField(max_length=50)
data_uscita = models.DateField('data uscita')
price = models.DecimalField(decimal_places=2, null=True,blank=True)
admin.py
class ExitAdmin(admin.ModelAdmin):
list_display =['description','price','total_exit']
def total_exit(self, request):
total = Exit.objects.all().aggregate(tot=Sum('price'))['tot']
return total
but is not ok because j have a columns with the total_exit that are repeated. I want total write only just once
I'm using Python 2.7.11
If what you need it's a function that returns the total of all outcomes (exits in your case), this is how the function goes:
from django.db.models import Sum
#other code you may need
def get_total(self, request):
return Exit.objects.aggregate(total=Sum('price'))
But, as you only got one price by outcome it doesn't make any sense to make a aggregate function because you should need multiple prices by outcome and a cost model perse.

Categories

Resources