Суммирование внутри модели корзины промежуточного итога каждой записи TabularInline в Django Admin
Не уверен, что это возможно, но в моем подробном представлении корзины (класс модели) в Django Admin я хотел бы суммировать все промежуточные итоги класса модели TabularInline Entry и сохранить это значение в атрибуте total_price моей корзины. Есть ли способ сделать это или как-то использовать фильтр или виджет формы, чтобы добавить все промежуточные итоги вместе для той же цели? Заранее благодарю за любую помощь!
Ниже приведен пример того, что я хотел бы сделать. Вы можете видеть в поле итоговой цены, которое я вручную ввел 130 (итого: 90 + 20 + 20 = 130). Я хотел бы, чтобы это вычислялось автоматически каждый раз, когда запись добавляется из инвентаря, и ее количество редактируется.
До сих пор в моем admin.py у меня был административный класс TabularInline, который возвращает промежуточный итог для каждой записи, умножая ее количество на соответствующую цену. Затем мой класс CartAdmin отображает эти встроенные отношения в подробном представлении модели корзины.
admin.py
class EntryInline(admin.TabularInline):
model = Entry
extra = 0
readonly_fields = ['subtotal']
exclude = ['price']
def subtotal(self, obj):
return "$" + str(obj.quantity * obj.inventory.price)
class CartAdmin(admin.ModelAdmin):
inlines = [EntryInline]
fieldsets = (
(None, {
'fields':('user', 'total_price')
}),
)
models.py
class Inventory(models.Model):
quantity = models.IntegerField()
price = models.DecimalField(max_digits=5, decimal_places=2)
class Cart(models.Model):
user = models.OneToOneField(User)
total_price = models.DecimalField(max_digits=4, decimal_places=2, blank=True, null=True)
class Entry(models.Model):
cart = models.ForeignKey(Cart, related_name="entries")
inventory = models.ForeignKey(Inventory, related_name="entries")
quantity = models.PositiveSmallIntegerField(default=1)
price = models.DecimalField(max_digits=4, decimal_places=2, blank=True, null=True)
3 ответа
Вы можете попробовать получить total_price в админке корзины и заполнить поле следующим образом:
class CartAdmin(admin.ModelAdmin):
inlines = [EntryInline]
fieldsets = (
(None, {
'fields':('user', 'total_price')
}),
)
def get_form(self, request, obj=None, **kwargs):
form = super().get_form(request, obj, **kwargs)
# check if the cart object exists
if obj:
try:
_price = Entry.objects.filter(cart=obj).aggregate(sum=Sum(F('quantity')*F('inventory__price'), output_field=FloatField()))
total = _price['sum']
obj.total_price = total
except:
pass
return form
Что касается вашей ошибки импорта, импортируйте F и Sum как:
from django.db.models import Sum, F
Или, если вы хотите более динамичный контроль, чтобы всякий раз, когда пользователь редактировал количество во встроенной записи, total_price обновлялся автоматически, вы можете написать собственный javascript для него.
Надеюсь, поможет.
Ниже приведено то, что я использовал, чтобы обновить страницу после сохранения в админке, чтобы обновить общее количество:
cart.js
if (!$) {
$ = django.jQuery;
}
function addSecs(d, s) {
return new Date(d.valueOf() + s * 1000);
}
function doRun() {
document.getElementById("msg").innerHTML = "Processing JS...";
setTimeout(function() {
start = new Date();
end = addSecs(start, 5);
do {
start = new Date();
} while (end - start > 0);
document.getElementById("msg").innerHTML = "Finished Processing";
}, 10);
}
$(function() {
$(".change_form_save").click(doRun);
if (window.localStorage) {
if (!localStorage.getItem("firstLoad")) {
localStorage["firstLoad"] = true;
window.location.reload();
} else localStorage.removeItem("firstLoad");
}
});
Затем в моем admin.py под моим классом для CartAdmin(admin.ModelAdmin):
class Media:
js = ('js/cart.js',)
Хорошо, я все еще работаю над тем, как обновить браузер, отправляющий AJAX-запрос, когда я редактирую количество в подробном административном представлении, чтобы увидеть обновленные изменения в общей цене корзины на стороне сервера. Не уверен, сколько времени это займет, но я обновлю этот ответ, как только выясню.
А пока вот как я смог получить промежуточные итоги и итоги:
В models.py я добавил поле "промежуточный итог " в мою модель Entry:
subtotal = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
В admin.py:
class EntryInline(admin.TabularInline):
model = Entry
extra = 0
# calls my helper method
readonly_fields = ['get_subtotal']
# exclude redundant field being replaced by helper method
exclude = ['subtotal']
# gets subtotal of each entry
def get_subtotal(self, obj):
# for a given entry, find that id
entry = Entry.objects.get(id=obj.id)
# subtotal the entry quantity * its related price
entry_subtotal = obj.quantity * obj.inventory.price
# check if entry obj has a subtotal and try to save to db
if entry.subtotal != entry_subtotal:
try:
entry.subtotal = entry_subtotal
entry.save()
except DecimalException as e:
print(e)
# return a str representation of entry subtotal
return "$" + str(entry_subtotal)
# set helper method's description display
get_subtotal.short_description = 'subtotal'
# initialization of variable
total_price = 0
class CartAdmin(admin.ModelAdmin):
model = Cart
inlines = [EntryInline]
# calls get_total helper method below
readonly_fields = ['get_total']
exclude = ['total_price']
def get_total(self, obj):
# extend scope of variable
global total_price
# get all entries for the given cart
entries = Entry.objects.filter(cart=Cart.objects.get(id=obj.id))
# iterate through entries
for entry in entries:
# if entry obj has a subtotal add it to total_price var
if entry.subtotal:
total_price += entry.subtotal
print(obj.total_price)
# assign cart obj's total_price field to total_price var
obj.total_price = total_price
# save to db
obj.save()
# reset total_price var
total_price = 0
# return cart's total price to be displayed
return obj.total_price
# give the helper method a description to be displayed
get_total.short_description = 'total'
** Следует отметить, что промежуточные итоги загружаются динамически, когда я редактирую количество и сохраняю его, потому что он использует вспомогательный метод. Мне все еще нужно обновить браузер еще раз, чтобы сохранить в базе данных, однако дисплей все еще там. Я не уверен, почему get_total() не работает так же; Там нет дисплея И я должен обновить браузер, чтобы сохранить в базе данных. Логика кажется противоречивой...