#python #django #django-models #django-views #django-templates
#python #django #django-модели #django-представления #django-шаблоны
Вопрос:
Я работаю в django, и моя цель — вывести в шаблон таблицу, состоящую из всех моих клиентов и соответствующей суммы, равной общей сумме, которую они потратили.
У меня есть классы / модели Customers
и Transactions
отношения OneToMany. Каждый раз, когда клиент совершает покупку, транзакция записывается как сумма, которую они потратили ( tx_amount
) . У меня есть следующий набор кода, который работает, но я считаю, что он не является оптимальным, поскольку он выполняется за O (x * y) время, т. Е. Выполняется полный цикл Transactions
для каждого клиента.
Q1: Каков оптимальный способ выполнения этой задачи?
Когда я изначально пытался заставить свой шаблон работать, вместо использования local_customer
я использовал setattr(customer[x],"value",tx_amount)
который работал в оболочке django, но не работал в шаблоне. Моим обходным путем было создание локального класса, который я бы использовал для заполнения моей context
переменной.
Q2: При объединении моделей данных для вывода в шаблоне необходимо ли использовать какой-то локальный класс, подобный моей local_customer
реализации ниже, или есть лучший способ?
Псевдокод ниже:
models.py:
class Customers(models.Model):
name = models.CharField(max_length=70)
def __str__(self):
return self.name
class Transactions(models.Model):
customers = models.ForeignKey(Customers, on_delete=models.CASCADE)
tx_amount = models.DecimalField(max_digits=9, decimal_places=2
views.py:
class Local_Customer:
name = ""
total_spent = 0
def __str__(self, name):
self.name = name
def customer_view(request):
customer = Customers.objects.all()
customer_context = [] # list of objects we'll pass to render
for x in range(len(customer)):
local_customer = Local_Customer(customer[x].name)
customer_txs = Transactions.objects.filter(customer__name=customer[x])
for y in customer_txs:
local_customer.total_spent = y.tx_amount
customer_context.append(local_customer)
context = {'customers' : customer_context}
html_template = loader.get_template( load_template )
return HttpResponse(html_template.render(context, request))
template.html:
{% for customer in customers %}
{{customer.name}}
{{customer.total_spent}}
{% endfor %}
Ответ №1:
Вы можете .annotate(…)
[Django-doc] сделать это на стороне базы данных:
from django.db.models import Sum
from django.shortcuts import render
def customer_view(request):
customers = Customers.objects.annotate(
total_spent=Sum('transactions__tx_amount')
)
context = {'customers' : customers}
return render(request, load_template, context)
Таким образом, это сгенерирует запрос, который выглядит как:
SELECT customers.*, SUM(transactions.tx_amount) AS total_spent
FROM customers
LEFT OUTER JOIN transactions ON transactions.customers_id = customers.id
и, таким образом, извлекает все данные в одном запросе.
Если транзакций нет, total_spent
will be None
( NULL
) , not 0
, вы можете использовать Coalesce
[Django-doc] для использования нуля вместо:
from django.db.models import Sum, Value
from django.db.models.functions import Coalesce
from django.shortcuts import render
def customer_view(request):
customers = Customers.objects.annotate(
total_spent=Coalesce(Sum('transactions__tx_amount'), Value(0))
)
context = {'customers' : customers}
return render(request, load_template, context)