0

在我的 Django 项目中,我试图创建一个自定义模型字段,该字段依赖于比较两个反向外键时间戳的值。

在这个玩具示例中,我经营一些汽车经销商。汽车是出租给人们的,但每次再次出租之前都需要“维修”。我正在尝试将“currently_serviced”字段添加到我的汽车模型中。如果一辆汽车在最近的“HireEvent”之后有一个相关的“ServiceEvent”,则该汽车被认为正在维修。

我一直在关注自定义模型字段的文档,但这并不能帮助我了解反向外键的复杂性并识别最新的(在当前时间之前,因为我想忽略未来的招聘)时间戳。

用法

然后我可以在我的视图中使用这个字段“car.currently_serviced”,例如通过一个简单的个人汽车页面,显示汽车的名称 {{ car​​.car_name }} 以及它的服务状态 {{ car .currently_serviced }} 或更复杂的页面,例如在经销商页面上输出“经销商服务的汽车数量”。

现在的进展

我一直试图在模型中声明这个字段,请参阅我在 models.py 中声明的函数“currently_serviced”中的努力。我已经尝试编写模型管理器类并使用 F() 比较将逻辑写入我的视图中,尽管更喜欢作为 DRY 方法在模型本身上声明“currently_serviced”属性,因为它会在租用汽车时动态更新,并且服务。

当前显示的方法会导致属性错误:

“RelatedManager”对象没有属性“service_time”。

谢谢!

模型.py

from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone


class Dealer(models.Model):
    dealer_name = models.CharField(max_length=200)

    def __str__(self):
        return self.dealer_name

class Car(models.Model):
    car_name = models.CharField(max_length=200)
    dealership = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name="cars")

    # This section should return a Boolean field accessible from the Car model.
    def currently_serviced(self):
        # This was another failed approach.
        # Car.objects.filter(serviceevent__service_time__lte=timezone.now(), serviceevent__service_time__lte= F('hireevent__hire_time')).count()
        if self.service_events.service_time.latest() > self.hire_events.hire_end_time.latest() and self.hire_events.hire_end_time < timezone.now():
            return True
        else:
            return False

    def __str__(self):
        return self.car_name

class HireEvent(models.Model):
    hire_id = models.AutoField(primary_key=True)
    car = models.ForeignKey(Car, on_delete=models.CASCADE, related_name="hire_events")
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    hire_end_time = models.DateTimeField(null=True,blank=True)

    def __str__(self):
        return self.hire_id

class ServiceEvent(models.Model):
    event_id = models.AutoField(primary_key=True)
    car = models.ForeignKey(Car, on_delete=models.CASCADE, related_name="service_events")
    service_time = models.DateTimeField(null=True,blank=True)

    def __str__(self):
        return self.event_id

视图.py

from django.shortcuts import get_object_or_404, render
from .models import Dealer, Car, HireEvent, ServiceEvent
from django.db.models import Count, When, Sum, Case, IntegerField, BooleanField, F, Max, Window
import json
from django.http import JsonResponse
from django.utils import timezone
from datetime import timedelta

def car(request, car_name):
    car_name = car_name.replace('-', ' ')
    car = Car.get(car_name__iexact=car_name)
    # Commented out as this approach does not work and ideally I'd like to not have to work out the currently
    # serviced status every time in the views, but rather pull it from the model dynamically.
    # dealership = Dealer.objects.annotate(
    #   currently_serviced = Sum(Case(
    #   When(
    #       cars__hire_events__hire_end_time__lte=timezone.now(),
    #       cars__service_events__service_time__gte = Max(F('hire_events__end_date')),
    #       then=1),
    #   output_field=IntegerField(),))
    #   ).get(cars__car_name__iexact=car_name)

    all_hire_events = HireEvent.objects.filter(car = car)
    last_hire_event = all_hire_events.filter(hire_end_time__lte=timezone.now()).order_by('hire_end_time').first()
    
    service_events = ServiceEvent.objects.filter(car = car, service_time__lte=timezone.now())
    last_service = service_events.filter(car = car, service_time__lte=timezone.now()).order_by('service_time').first()
    if last_service is not None:
        if last_hire_event.hire_end_time < last_service.service_time:
            car.currently_serviced = True
        else:
            car.currently_serviced = False
    else:
        car.currently_serviced = False

    return render(request, 'dealership_app/car.html', {'car': car,  "all_hire_events":all_hire_events, "service_events":service_events})
4

1 回答 1

0

我解决了:) 感谢另一个问题中的这个很好的答案。放入我的代码,希望它可以帮助其他人。

我的代码

class Car(models.Model):
car_name = models.CharField(max_length=200)
dealership = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name="cars")

@property
def currently_serviced(self):
    last_service = ServiceEvent.objects.filter(car__car_name=self.car_name, service_time__lte=timezone.now()).latest('service_time')
    last_hire = HireEvent.objects.filter(car__car_name=self.desk_name, hire_end_date__lte=timezone.now()).latest('hire_end_time')
    if last_service.service_time < timezone.now() and last_service.service_time > last_hire.hire_end_time:
        return True
    else:
        return False

def __str__(self):
    return self.car_name

将此属性添加到我的 Car 模型类中,首先标识 ServiceEvent 的单个实例,通过self对象使用同一汽车的过滤器获取最新的服务时间。最后一次租用时间也是如此。

然后,我在 if 语句中比较时间戳的差异,并根据比较的输出返回 True 或 False。

于 2020-08-28T19:02:12.257 回答