在Django的Refuel创建视图类中利用历史数据计算车辆油耗并存储至FuelConsumption模型
解决加油记录油耗计算并存储的问题
Hey there! Congrats on diving into your first real Django project after the tutorial—this is where the fun really starts! Let's walk through how to add that fuel consumption logic to your RefuelCreationView step by step.
核心思路
When a new refuel record is saved, we need to:
- 抓取当前车辆的上一条加油记录
- 计算两次加油之间的行驶距离
- 用上一次的加油量除以行驶距离,得到每公里油耗
- 将计算结果存入
FuelConsumption模型
修改后的视图代码
下面是更新后的form_valid方法,包含完整逻辑和注释说明:
class RefuelCreationView(LoginRequiredMixin, CreateView): model = Refuel form_class = RefuelCreationForm template_name = "refuel/refuel_form.html" success_url = reverse_lazy("refuel:controlor-refuel-list") def form_valid(self, form): # 步骤1:先保存新的加油记录(暂不跳转) # 使用commit=False先在内存中获取实例,设置必填字段后再存入数据库 refuel_instance = form.save(commit=False) refuel_instance.user_id = self.request.user.id refuel_instance.gaz_station = GazStation.objects.get(Controlor_id=self.request.user.id) refuel_instance.save() # 步骤2:获取当前车辆的上一条有效加油记录(排除刚创建的这条) # 按创建时间倒序,取最近的一条旧记录 previous_refuel = Refuel.objects.filter( vehicle=refuel_instance.vehicle, is_active=True ).exclude(id=refuel_instance.id).order_by("-created_at").first() # 步骤3:仅当存在上一条记录,且两次里程表读数都有效时,才计算油耗 if previous_refuel and previous_refuel.odometer_reading and refuel_instance.odometer_reading: # 计算行驶距离 distance_driven = refuel_instance.odometer_reading - previous_refuel.odometer_reading # 避免除数为0(比如两次里程表读数相同的情况) if distance_driven > 0: # 计算每公里油耗:上一次加油量 ÷ 行驶距离 # 用Decimal保证精度 per_km_consumption = previous_refuel.fuel_quantity / Decimal(distance_driven) # 步骤4:创建并保存FuelConsumption记录 FuelConsumption.objects.create( vehicle=refuel_instance.vehicle, gaz_station=refuel_instance.gaz_station, Controlor_id=self.request.user, # 注意:如果Refuel模型没有关联司机,你可能需要从Vehicle模型获取 # driver=refuel_instance.vehicle.driver, # 如果Vehicle有driver字段,可取消注释 consumption=per_km_consumption.quantize(Decimal('0.00')), # 保留两位小数 ) # 步骤5:执行默认的跳转逻辑 return super().form_valid(form) def test_func(self): return self.request.user.is_active and self.request.user.is_controlor
关键细节说明
- 先保存加油记录:使用
commit=False可以在存入数据库前操作实例,这样能获取新记录的ID,避免在查询上一条记录时误取到刚创建的这条。 - 边界情况处理:
- 如果是第一次加油(没有上一条记录),则跳过油耗计算(没有足够数据)
- 检查两次里程表读数是否有效,且行驶距离大于0,避免出现除以0的错误
- 精度控制:使用Decimal类型进行计算保证精度,并用
quantize将结果保留两位小数(可根据需求调整位数)
可选小优化
为了避免模型__str__方法在vehicle为空时抛出错误,可以修改如下:
# 在Refuel模型中 def __str__(self): return self.vehicle.serie if self.vehicle else "未关联车辆的加油记录" # 在FuelConsumption模型中 def __str__(self): return f"{self.vehicle.serie} - {self.consumption} L/km" if self.vehicle else "未关联车辆的油耗记录"
这样即使记录没有关联车辆,也能正常显示字符串,不会触发AttributeError。
内容的提问来源于stack exchange,提问作者E.Mohammed
相关产品推荐
相关产品推荐

