Django product count per category - django

Have below models
class TreatmentCategory(models.Model):
name = models.CharField(max_length=255, unique=True)
def __str__(self):
return self.name
class Treatment(models.Model):
unique_id = models.UUIDField('Unique Record ID', default=uuid.uuid4, editable=False)
name = models.CharField('Treatment Name', max_length=255, db_index=True, unique=True)
category = models.ForeignKey(TreatmentCategory, on_delete=models.PROTECT)
duration = models.PositiveSmallIntegerField('Treatment Duration (min)')
price = models.DecimalField('Treatment Cost', max_digits=9, decimal_places=2)
description = models.TextField(null=True, blank=True)
active = models.BooleanField(default=True)
def __str__(self):
return self.name
class Therapist(models.Model):
day_options = [
("Off Day", "Off Day"),
("On Leave", "On Leave"),
("On Duty", "On Duty"),
]
unique_id = models.UUIDField('Unique Record ID', default=uuid.uuid4, editable=False)
name = models.CharField('Therapist Name', max_length=255)
contact = models.CharField('Phone Number', max_length=12, blank=True, null=True)
day_status = models.CharField(
'Duty Status',
max_length=255,
choices=day_options,
default='On Duty'
)
status = models.BooleanField(
'Status of Therapist',
default=True,
)
treatments = models.ManyToManyField(Treatment, related_name='therapists')
def __str__(self):
return self.name
Would like to output all therapists and a total count of treatments per category they have records i.e each therapist may have different treatments per category thus they wont have same number of treatments
therapist1 | treatmentcategory1 (number of treatments)
| treatmentcategory2 (number of treatments)
| treatmentcategory3 (number of treatments)
therapist2 | treatmentcategory1 (number of treatments)
My current code as below, its displaying therapists however, instead of treatmentcategories, it showing treatments
category = (TreatmentCategory.objects.filter(treatment__active=True)
.prefetch_related('treatment_set')
.annotate(treatment_count=Count('treatment__name'))
)
queryset = (Therapist.objects.filter(status=True)
.prefetch_related(
Prefetch('mytreatments', queryset=category, to_attr='treatment_details')
)
)
Testing category alone gives the list of treatmentcategories, treatment count and all treatments in each treatmentcategory.
Queryset returns the therapists and treatments only, no categories nor treatment count per category.
Kindly assist in refining/rewriting the queries to extract the data as per sample above.
Thanks in advance.

Related

django objects.create method is too slow How to make faster?

multiple tables are mapped and, when I create post request,
it takes about 2~3 seconds. Is there any ways to fix it?
I guess it takes a long time on:
objects.create
for loop
product.objects.get
however, I am not able to find the better ways..
models:
#product, Order, OrderItems, ShippingAddress are mapped
class Order(models.Model):
user = models.ForeignKey(User, on_delete= models.CASCADE)
order_date = models.DateTimeField(auto_now=True)
is_paid = models.BooleanField(default=False)
paid_at = models.DateTimeField(auto_now=False, null=True, blank=True)
delivery_code = models.CharField(max_length=255, null=True, blank=True)
is_delivered = models.BooleanField(default=False)
delivered_date = models.DateTimeField(auto_now=False, null=True, blank=True)
total_price = models.DecimalField(max_digits=7, decimal_places=2, null=True)
shipping_price = models.DecimalField(max_digits=7, decimal_places=2, null=True)
payment_method = models.CharField(max_length=255,null=True)
def __str__(self):
return str(self.user)
class OrderItem(models.Model):
order = models.ForeignKey(Order, on_delete= models.CASCADE, null=True, blank=True)
product = models.ForeignKey(Product, on_delete= models.CASCADE)
name = models.CharField(max_length=200, null=True)
image = models.CharField(max_length=255, null=True)
qty = models.IntegerField(default=0, null=True)
price = models.DecimalField(max_digits=7, decimal_places=2, null=True)
def image_preview(self):
if self.image:
return mark_safe('<img src="{0}" width="55" height="55" />'.format(self.image))
else:
return '(No image)'
def __str__(self):
return str(self.product)
class ShippingAddress(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
order = models.OneToOneField(Order, on_delete=models.CASCADE, null=True, blank=True)
address = models.CharField(max_length=255, null=False)
city = models.CharField(max_length=255, null=False)
postal_code = models.CharField(max_length=255, null=False)
country = models.CharField(max_length=255, null=False)
def __str__(self):
return str(self.user)
view:
#permission_classes(IsAuthenticated)
#api_view(['POST'])
def OrderCreate(request):
data = request.data
user = request.user
order_items = data['orderItems']
#1.create order
order = Order.objects.create(
user = user,
total_price = data['totalPrice'],
shipping_price = data['shippingPrice'],
payment_method = data['paymentMethod']
)
#2.create orderItems
for i in order_items:
product = Product.objects.get(id=i['id'])
order_item = OrderItem.objects.create(
order = order,
product = product,
name = i['name'],
qty = i['qty'],
price = i['price'],
image = i['image']
)
#3. update stock
product.stock -= i['qty']
product.save()
#4.create shipping address
shipping_address = ShippingAddress.objects.create(
user = user,
order = order,
address = data['shippingAddress']['address'],
city = data['shippingAddress']['city'],
postal_code = data['shippingAddress']['postalCode'],
country = data['shippingAddress']['country'],
)
#5.serializing and save
serializer = OrderSerializer(order, many=False)
return Response(serializer.data)
You can instantiate the order_items without ever fetching the product, provided you have sufficient trust for the product ids in i['id']
for i in order_items:
# product = Product.objects.get(id=i['id'])
order_item = OrderItem.objects.create(
order = order,
product_id = i['id'], # set the id (magic suffix) without fetching product
name = i['name'],
qty = i['qty'],
price = i['price'],
image = i['image']
)
Instead of using .create you might instantiate these order_items as a list of unsaved instances and create them using OrderItem.bulk_create Read the bulk_create documentation; it has a number of caveats.
You could then run a loop updating the product stock field using an F expression to subtract from the current value in the product row without actually fetching a product object from the DB
for i in order_items:
product_id = i['id']
Product.objects.filter(
pk = product_id
).update(
stock = F('stock') - i['qty']
)
If you do fetch all the product instances into a list with updated stock values, there's also bulk_update which would let you apply all the updated stock values in a single DB operation. This might be better than doing them one by one with an F expression. You can also fetch them in bulk using
Product.objects.filter( pk__in=[ i['id'] for i in order_items ] )
(Warning, I don't think that there's any guarantee that the queryset contains the objects in the same order that you supply the i['id'] values )
Treat this as brainstorming. I'm not entirely certain that this is correct and I really don't know whether it will speed things up a lot, a little, or at all. I'd be interested to know, if you try it.

how to make relationship in django model

models.py
So,here i want to make Invoicemgmt model in which i can have multiple entries for Invoice table having
customer,project and Invoice_amount.
Basically,requirement is that whenever i see 'view_Invoice' of some id,first i will see all data of that
specific id on that page and then i want to have small table below for invoice_mgmt,where i can add amount received for that specific id invoice.
*so,i want to know what fields should i add in invoice_mgmt model for relationship "
class Invoice(models.Model):
company_choice = (
('VT_India', 'VT_India'),
('VT_USA', 'VT_USA'),
)
company = models.CharField(
max_length=30, blank=True, null=True, choices=company_choice)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
project = models.ForeignKey(Allproject, on_delete=models.CASCADE)
invoice_title = models.CharField(max_length=15)
invoice_id = models.IntegerField(primary_key=True)
invoice_amount = models.IntegerField()
invoice_date = models.DateField(
blank=True, null=True)
invoice_duedate = models.DateField(
blank=True, null=True)
invoice_description = models.TextField()
def __str__(self):
return self.invoice_title
class Paymentmethod(models.Model):
paymentmethod_id = models.IntegerField(primary_key=True)
paymentmethod_name = models.CharField(max_length=15)
def __str__(self):
return self.paymentmethod_name
class Invoicemgmt(models.Model):
invoicemanagement_id = models.IntegerField(primary_key=True)
invoice_received = models.IntegerField()
date = models.DateField(
blank=True, null=True)
payment_method = models.ForeignKey(Paymentmethod, on_delete=models.CASCADE)
"So, basically i want to have multiple entries in invoice mgmt table for one specific invoice table id(one specific data)"

Django Queryset getting a sequence of objects instead of result of a Sum

I'm trying to perform a Sum of different rows of the Model according to specific column values, in this case I want to Sum according to a week and an specific car, so for example:
Car Week Payment
1 2020-W06 $500
1 2020-W06 $300
2 2020-W06 $200
1 2020-W05 $500
So I pass to the query the car & the week and it should get the sum of the payments according to those values
I pass Car = 1 and Week = 2020-W06 and Payment Sum = $800
this is my queryset:
payed = Pagos.objects.filter(carro_id=1, semana=semana).annotate(total=Sum('pago'))
and thi is the result I'm getting:
<Pagos: Pagos object (6)>, <Pagos: Pagos object (12)>]
I don't understand why I don't get the Sum
models.py
class Pagos(models.Model):
carro = models.ForeignKey(
Carros, on_delete=models.CASCADE, blank=False, null=False)
pago = models.DecimalField(max_digits=6, decimal_places=2)
fecha = models.DateField(
auto_now=False, auto_now_add=False, blank=True, null=True)
semana = models.CharField(max_length=20)
startweek = models.DateField(
auto_now=False, auto_now_add=False, blank=True, null=True)
endweek = models.DateField(
auto_now=False, auto_now_add=False, blank=True, null=True)
renta = models.ForeignKey(
Renta, on_delete=models.PROTECT, blank=False, null=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name_plural = "Pagos"
def get_absolute_url(self):
return reverse('pagos')
class Carros(models.Model):
nombre = models.CharField(max_length=20, blank=True, null=True)
marca = models.CharField(max_length=25)
modelo = models.CharField(max_length=25)
year = models.IntegerField()
placa = models.CharField(max_length=10, unique=True)
color = models.CharField(max_length=10)
conductor = models.ForeignKey(
Conductores, on_delete=models.SET_NULL, blank=True, null=True)
propietario = models.ForeignKey(Propietarios, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
verbose_name_plural = "VehĂ­culos"
def __str__(self):
return self.nombre
def get_absolute_url(self):
return reverse('carros')
According to docs annotate adds a filed to each of your model objects, so you get the sum and you can access it like this(docs):
payed[0].total
If you want to get total value not on just one field you need to use aggregate(docs), it will be like:
payed = Pagos.objects.filter(carro_id=1, semana=semana).aggregate(total=Sum('pago'))
print(payed)
# {total: x}
About the part you said you want different sums based on fields you can use conditional clauses in your annotate and aggregation.

Showing data related to a logged in user in Django

I am building this simple system for a school, where students can log in to see their results at the end of every semester. I designed a model for exams with a manytomany relationship to a user. My problems is in my template am finding it hard to show a exams results related to a logged in user.
models.py
class StudentProfile(models.Model):
SEX_CHOICES = (
('Male', 'Male'),
('Female', 'Female')
)
user = models.OneToOneField(User, on_delete=models.CASCADE)
other_name = models.CharField(max_length=30, null=True, blank=True)
birth_of_date = models.DateField(null=True, blank=True)
birth_Of_admission = models.DateField(null=True, blank=True)
nationality = models.CharField(max_length=120)
phone_number = models.CharField(max_length=15, validators=[MinLengthValidator(10)])
gender = models.CharField(max_length=120, choices=SEX_CHOICES)
home_address = models.CharField(max_length=250, null=True, blank=True, )
passport_picture = models.ImageField(upload_to='passport_picture', null=True, blank=True,
help_text='Upload the passport picture here')
def __str__(self):
return "%s %s" % (self.user.first_name, self.user.last_name)
#receiver(post_save, sender=User)
def create_or_update_user_profile(sender, instance, created, **kwargs):
if created:
StudentProfile.objects.create(user=instance)
instance.studentprofile.save()
class Subject(models.Model):
subject_name = models.CharField(max_length=30, null=True, blank=True)
def __str__(self):
return self.subject_name
class Year(models.Model):
year = models.CharField(max_length=30, null=True, blank=True)
def __str__(self):
return self.year
class Exam(models.Model):
TERM_CHOICES = (
('First Term', 'First Term'),
('Second Term', 'Second Term'),
('Third Term', 'Third Term')
)
level = models.ForeignKey('Level', null=True, blank=True, on_delete=models.CASCADE)
student = models.ForeignKey(Year, null=True, blank=True, on_delete=models.CASCADE)
year = models.ForeignKey(Year, null=True, blank=True, on_delete=models.CASCADE)
subject = models.ForeignKey(Subject, null=True, blank=True, on_delete=models.CASCADE)
term = models.CharField(max_length=120, default="", choices=TERM_CHOICES)
mid_term_score = models.PositiveSmallIntegerField(help_text='the marks scored for mid term exams')
End_of_term_score = models.PositiveSmallIntegerField(help_text='the marks scored for end of term exams')
class_work_score = models.PositiveSmallIntegerField(help_text='the marks scored for class work')
def __str__(self):
return self.subject + "-" + self.term
views.py
class StudentView(LoginRequiredMixin, ListView):
model = Exam
template_name = 'student.html'
context_object_name = 'student'
def get_object(self):
return self.request.user.exam
What I am trying to do is to show logged in student only his results based on the exams he has taken and but is rather showing me all. Even those he has not taken.
you can retrive those exams by set_all look_up_relationship or manually can query on Exam model.
possible way can be like this
def get_object(self):
return self.request.user.exam_set.all()
Also you can try this
def get_object(self):
return Exam.objects.filter(student_id=self.request.user.id)
Edit
If i understand you properly now, then student's current level is an important factor of this Exam filtering. We should add that too than, otherwise all exam will appear. But from your Exam model structure, i haven't see any level entity associated with User model present. I am assuming term and level actually same thing ( most likely which are not )
def get_object(self):
return Exam.objects.filter(student_id=self.request.user.id, term = self.request.user.level )

django admin foreign key default value inline

I'm converting an ancient Client/Server app (Delphi) into a Django app for a small brick and morter bookstore (wife's).
Since most functions are admin, I'm using the Django admin interface with grappelli for some easier lookups.
I have 3 models: Book, Sale and Item.
class Book(TimeStampedModel):
"""
Books are described individually and are related to collections
as many to many. Every book in this system is unique - i.e. there are
not quantity fields. This is optimized for used book stores where book
condition is essential.
"""
STATUS_CHOICES = (
('IN STOCK', 'IN STOCK'),
('SOLD', 'SOLD'),
('ON LOAN', 'ON LOAN'),
('HOLD', 'HOLD'),
)
isbn = models.CharField(max_length=50, blank=True)
title = models.CharField(max_length=200, blank=False, db_index=True)
author = models.CharField(max_length=200, blank=True)
sell_price = models.DecimalField(max_digits=10,decimal_places=2, default=0)
description = models.TextField()
collections = models.ManyToManyField(Collection)
class Meta:
index_together = [
["author", "title"],
["status", "title", "author"],
]
def __unicode__(self):
return "%s [%d] - %s - $%.2f" % (self.title, self.id, self.book_type, self.sell_price)
#staticmethod
def autocomplete_queryset():
instock = Book.objects.filter(status="IN STOCK")
return instock
#staticmethod
def autocomplete_search_fields():
return("id__iexact", "title__istartswith",)
class Sale(TimeStampedModel):
"""
Sales group all sold items which may or may not be books and are sold to contacts.
We use a "generic" contact of "cash" for non named contacts
"""
PAYMENT_TYPE_CHOICES = ( ('Cash', 'Cash'), ('Charge', 'Charge'), ('Check', 'Check'))
contact = models.ForeignKey(Contact, null=True)
sale_date = models.DateField(blank=True,default=datetime.date.today, db_index=True)
payment_type = models.CharField(max_length=50, choices=PAYMENT_TYPE_CHOICES)
taxed = models.BooleanField(default=True)
tax_exempt_no = models.CharField(blank=True, max_length=50)
sales_tax = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
amt_tender = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
pct_discount = models.SmallIntegerField(blank=True, null=True)
amt_credit = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
amt_shipping = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
amt_due = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
tot_sale = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
tot_items = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
ordering = ['-sale_date']
def __unicode__(self):
return str(self.sale_date)
class Item(TimeStampedModel):
"""
Items are usually books sold on a sale. Items can also be entered manually
at time of sale if they are not books from inventory
"""
sale = models.ForeignKey(Sale)
book = models.ForeignKey(Book, null=True, blank=True)
item_desc = models.CharField(blank=True, max_length=200)
cost = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
sell_price = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
def __unicode__(self):
return self.item_desc
For the Sale form, I use an admin form with Tabular inline Items. Items are usually books (via a foreign key lookup), but can also be entered manually for non-inventory items so I have a sell_price both in the book model and in the item model.
class ItemInline(admin.TabularInline):
model = Item
raw_id_fields = ("book",)
autocomplete_lookup_fields = {
'fk': ['book'],
}
extra = 2
What I'd like to do in the foreign key lookup is to return the key of the book AND fill in the Item's sellprice with the sellprice from the book I looked up.
I have the basic lookup working just fine but can't find out how to set the item sellprice to the book's sellprice immediately after the lookup.
Any advice is appreciated! I have tried figuring out the objects to put some JS logic in but the inlines get object ids created dynamically, I think. I'm no JS expert.