【问题标题】:Django 2.2.9 - unexpected results from timedelta()Django 2.2.9 - timedelta() 的意外结果
【发布时间】:2020-06-16 10:28:06
【问题描述】:

如果有人问过这个问题,请原谅我
我正在网上学习一门课程,虽然已经 3 岁了,所以从那以后有些东西已经更新了
但是,据我所见,日期时间似乎大致相同。

当我们使用 timedelta 过滤上个月的所有销售额时出现问题
我在几周内添加了一些测试销售,当我尝试将其过滤到过去 48 小时时,什么都没有
经过一些测试,我回到了 12 天(从这个问题开始,2 月 20 日售出的物品清单出现了,但没有更早。
我进入管理员并更新了其中一个并刷新了页面。它完全消失了
任何人都可以提供任何关于我缺少什么的见解吗?
如果您需要更多信息,请说:

模型.py

from decimal import Decimal
import datetime
from django.conf import settings
from django.db import models
from django.db.models import Count, Sum, Avg
from django.db.models.signals import pre_save, post_save
from django.urls import reverse
from django.utils import timezone

from addresses.models import Address
from billing.models import BillingProfile
from carts.models import Cart
from ecommerce.utils import unique_order_id_generator
from products.models import Product

ORDER_STATUS_CHOICES = (
    ('created', 'Created'),
    ('paid', 'Paid'),
    ('shipped', 'Shipped'),
    ('refunded', 'Refunded'),
)

class OrderManagerQuerySet(models.query.QuerySet):
    def recent(self):
        return self.order_by("-updated", "-timestamp")

    def by_date(self):
        now = timezone.now() - datetime.timedelta(days=12)
        return self.filter(updated__day__gte=now.day)

    def totals_data(self):
        return self.aggregate(Sum("total"), Avg("total"))

    def cart_data(self):
        return self.aggregate(Sum("cart__products__price"), Avg("cart__products__price"), Count("cart__products"))

    def by_status(self, status="shipped"):
        return self.filter(status=status)

    def not_refunded(self):
        return self.exclude(status='refunded')

    def by_request(self, request):
        billing_profile, created = BillingProfile.objects.new_or_get(request)
        return self.filter(billing_profile=billing_profile,)

    def not_created(self):
        return self.exclude(status='created')

class OrderManager(models.Manager):

    def get_queryset(self):
        return OrderManagerQuerySet(self.model, using=self._db)

    def by_request(self, request):
        return self.get_queryset().by_request(request)

    def new_or_get(self, billing_profile, cart_obj):
        created = False
        qs = self.get_queryset().filter(
            billing_profile=billing_profile,
            cart=cart_obj,
            active=True,
            status='created'
        )

        if qs.count() == 1:
            obj = qs.first()
        else:
            obj = self.model.objects.create(
                billing_profile=billing_profile,
                cart=cart_obj
            )
            created = True
        return  obj, created

class Order(models.Model):
    billing_profile     = models.ForeignKey(BillingProfile, null=True, blank=True, on_delete=models.CASCADE)
    order_id            = models.CharField(max_length=120, blank=True)
    shipping_address    = models.ForeignKey(Address, related_name="shipping_address", null=True, blank=True, on_delete=models.CASCADE)
    billing_address     = models.ForeignKey(Address, related_name="billing_address", null=True, blank=True, on_delete=models.CASCADE)
    cart                = models.ForeignKey(Cart, on_delete=models.CASCADE)
    status              = models.CharField(max_length=120, default='created', choices=ORDER_STATUS_CHOICES)
    shipping_total      = models.DecimalField(default=5.99, max_digits=100, decimal_places=2)
    total               = models.DecimalField(default=0.00, max_digits=100, decimal_places=2)
    active              = models.BooleanField(default=True)
    updated             = models.DateTimeField(auto_now=True)
    timestamp           = models.DateTimeField(auto_now_add=True)


    def __str__(self):
        return self.order_id


    objects = OrderManager()

    class Meta:
        ordering = ['-timestamp', '-updated']

    def get_absolute_url(self):
        return reverse("orders:detail", kwargs={'order_id': self.order_id})

    def get_status(self):
        if self.status == "refunded":
            return "Refunded order"
        elif self.status == "shipped":
            return "Shipped"
        return "Shipping Soon"

    def update_total(self):
        cart_total = self.cart.total
        shipping_total = self.shipping_total
        new_total = Decimal(cart_total)+Decimal(shipping_total)
        formatted_total = format(new_total, '.2f')
        print(type(new_total))
        self.total = formatted_total
        self.save()
        return new_total


    def check_done(self):
        shipping_address_required = not self.cart.is_digital
        shipping_done = False

        if shipping_address_required and self.shipping_address:
            shipping_done = True
        elif shipping_address_required and not self.shipping_address:
            shipping_done = False
        else:
            shipping_done = True


        billing_profile = self.billing_profile
        billing_address = self.billing_address
        total = self.total
        if billing_profile and shipping_done and billing_address and total > 0:
            return True
        return False

    def update_purchases(self):
        for p in self.cart.products.all():
            obj, create = ProductPurchase.objects.get_or_create(
                order_id = self.order_id,
                product = p,
                billing_profile= self.billing_profile,
            )
        return ProductPurchase.objects.filter(order_id = self.order_id).count()

    def mark_paid(self):
        if self.status != 'paid':            
            if self.check_done():
                self.status = "paid"
                self.save()
                self.update_purchases()
        return self.status


def pre_save_create_order_id(sender, instance, *args, **kwargs):
    if not instance.order_id:
        instance.order_id = unique_order_id_generator(instance)

    qs= Order.objects.filter(cart=instance.cart).exclude(billing_profile=instance.billing_profile)
    if qs.exists():
        qs.update(active=False)

pre_save.connect(pre_save_create_order_id, sender=Order)


def post_save_cart_total(sender, instance, created, *args, **kwargs):
    if not created:
        cart_obj = instance
        cart_total = cart_obj.total
        cart_id = cart_obj.id
        qs = Order.objects.filter(cart__id=cart_id)
        if qs.count() ==1:
            order_obj = qs.first()
            order_obj.update_total()

post_save.connect(post_save_cart_total,sender=Cart)

def post_save_order(sender, instance, created, *args, **kwargs):
    print("running")
    if created:
        print("updating...first")
        instance.update_total()

post_save.connect(post_save_order,sender=Order)

class ProductPurchaseQuerySet(models.query.QuerySet):
    def active(self):
        return self.filter(refunded=False)

    def digital(self):
        return self.filter(product__is_digital=True)

    def by_request(self, request):
        billing_profile, created = BillingProfile.objects.new_or_get(request)
        return self.filter(billing_profile=billing_profile,)


class ProductPurchaseManager(models.Manager):
    def get_queryset(self):
        return ProductPurchaseQuerySet(self.model, using=self._db)

    def all(self):
        return self.get_queryset().active()

    def digital(self):
        return self.get_queryset().active().digital()       

    def by_request(self, request):
        return self.get_queryset().by_request(request)

    def products_by_id(self, request):
        qs = self.by_request(request).digital()
        ids_ = [x.product.id for x in qs]
        return ids_

    def products_by_request(self, request):
        ids_ = self.products_by_id(request)
        product_qs = Product.objects.filter(id__in=ids_).distinct()
        return product_qs

class ProductPurchase(models.Model):
    order_id        = models.CharField(max_length=120)
    billing_profile = models.ForeignKey(BillingProfile, on_delete=models.CASCADE) # billingprofile.productpurchase_set.all()
    product         = models.ForeignKey(Product, on_delete=models.CASCADE) # product.productpurchase_set.count()
    refunded        = models.BooleanField(default=False)
    updated         = models.DateTimeField(auto_now=True)
    timestamp       = models.DateTimeField(auto_now_add=True)

    objects = ProductPurchaseManager()
    def __str__(self):
        return self.product.title

Views.py

from django.contrib.auth.mixins import LoginRequiredMixin
from django.db.models import Count, Sum, Avg
from django.http import HttpResponse
from django.views.generic import TemplateView
from django.shortcuts import render

from orders.models import Order

class SalesView(LoginRequiredMixin, TemplateView):
    template_name = 'analytics/sales.html'

    def dispatch(self, *args, **kwargs):
        user = self.request.user
        if not user.is_staff:
            return render(self.request, "401.html", {})
        return super(SalesView, self).dispatch(*args, **kwargs)

    def get_context_data(self, *args, **kwargs):
        context = super(SalesView, self).get_context_data(*args, **kwargs)
        qs = Order.objects.all().by_date()
        context['orders'] = qs
        context['recent_orders'] = qs.recent().not_refunded()
        context['recent_orders_data'] = context['recent_orders'].totals_data()

        context['recent_orders_cart_data'] = context['recent_orders'].cart_data()

        context['shipped_orders'] = qs.recent().not_refunded().by_status("shipped")
        context['shipped_orders_data'] = context['shipped_orders'].totals_data()

        context['paid_orders'] = qs.recent().not_refunded().by_status("paid")
        context['paid_orders_total'] = context['paid_orders'].totals_data()
        return context

Sales.html

{% extends "base.html" %}

{% block content%}

  <div class="row">
    <div class="col-12">
      <h1>Sales Data</h1>
    </div>
  </div>

  <div class="row">    
    <div class="col">
      <p>Recent Toal: £{% if recent_orders_data.total__sum %} {{ recent_orders_data.total__sum }}{% else %}0 {% endif %}</p>
      <ol>
        {% for order in recent_orders %}
        <li>{{ order.order_id}}
        {{ order.total}}
        {{ order.updated}} </li>    
        {% endfor %}
      </ol>
    </div>
    
    <div class="col">
      <p>Shipped Total: £{% if shipped_orders_data.total__sum %}{{ shipped_orders_data.total__sum }}{% else %}0{% endif %}</p>
      <ol>
        {% for order in shipped_orders %}
        <li>{{ order.order_id}}
        {{ order.total}}
        {{ order.updated}}</li>
        {% endfor %}
    </ol>
    </div>
    
    <div class="col">
      <p>Paid Totals: £{% if shipped_orders_data.total__sum %}{{ paid_orders_total.total__sum }}{% else %}0{% endif %}</p>
      <ol>
        {% for order in paid_orders %}
        <li>{{ order.order_id}}
        {{ order.total}}
        {{ order.updated}}</li>
        {% endfor %}
    </ol>
    </div>
  </div>
{% endblock %}

【问题讨论】:

    标签: django python-3.x datetime


    【解决方案1】:

    在您的 filter by_date 方法中,您使用的是当天而不是日期的过滤器。这无济于事,因为它将返回该月的某一天。因此,如果日期是 2020-03-11,则 now.day 将返回 29。如果您将 .day 更改为 .date() 并将 updated__day__gte 更改为 updated__date__gte 应该没问题

    def by_date(self):
            now = timezone.now() - datetime.timedelta(days=12)
            return self.filter(updated__date__gte=now.date())
    

    【讨论】:

    • 谢谢你,但是我收到一个错误:预期的字符串或类似字节的对象
    • 很抱歉,我将 .date 中的括号作为一种方法。你想要这个 ``` return self.filter(updated__date__gte=now.date())``` 我已经更新了我的回复,让它更清楚。
    【解决方案2】:

    以下应该可以正常工作:

    now = timezone.now().date() - datetime.timedelta(days=12)
    return self.filter(updated__date__gte=now)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-10
      • 1970-01-01
      • 2012-09-10
      • 1970-01-01
      • 2011-09-15
      • 2012-04-02
      相关资源
      最近更新 更多