在我的 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})