在Django中使用F函数对多个字段进行运行时更新
在本教程中,我们将学习如何使用Django中的F 函数连续更新多个字段。
让我们想象一个场景--尼日利亚的一个爱国者通常在他的家里安装了一个电表。他每用一次电,用电数据就会从电表上传到电力局。
因此,通过智能手机向该用户发送两个数值,详细说明他的总耗电量和剩余电量。
在本教程中,我们将模拟一个电表,在一端读取用户的用电量,并在另一端流式传输电表生成的数据(消耗率、剩余电量和总用电量)。
这种运行时的数据流可以通过Django的F() 功能来轻松有效地完成。
前提条件
要充分利用本教程,需要具备以下条件。
- 对Python的基本了解。
- 熟悉Django框架和Django REST框架。
- 熟悉Django的可浏览API界面。
- 安装了PyCharm专业代码编辑器。
F()的效率如何?
传统的方法是不断地获取和迭代数据流--读数,获得所用电量的总和,并从阈值中减去,以获得所用电量和剩余电量的值。这是个效率较低的方法。
有了F(),一个单一的读数对象(为用户准备的)可以在不参考以前的数据的情况下被即时更新,可以被保存到数据库,并准备好让用户查看。你可以一次为多个字段做这件事。
实施
在这个关于F函数如何工作的演示中,我们将模拟一个读表过程,如本文前面所述。
模型
我们将需要三个模型。RegisterMeter,MeterReading 和CurrentUsage 。下面的片段显示了创建这些模型的代码。
from django.db import models
class RegisterMeter(models.Model):
meter_id = models.CharField(max_length=10)
date_added = models.DateTimeField(auto_now=True)
def __str__(self):
return str(self.pk)
class MeterReading(models.Model):
meter = models.CharField(max_length=10)
meter_reading = models.IntegerField()
date_sent = models.DateTimeField(auto_now=True)
def __str__(self):
return self.meter
class CurrentUsage(models.Model):
meter = models.CharField(max_length=10)
total_power_used = models.CharField(max_length=10)
power_remaining = models.CharField(max_length=10)
def __str__(self):
return self.meter
序列器
我们还需要为上述代码片段中的模型设置序列化器。
序列化器将对象转换为JSON格式。每个模型的序列化器如下所示。
from rest_framework import serializers
from .models import MeterReading, CurrentUsage, RegisterMeter
class MeterReadingSerializer(serializers.ModelSerializer):
class Meta:
model = MeterReading
fields = '__all__'
class CurrentUsageSerializer(serializers.ModelSerializer):
class Meta:
model = CurrentUsage
fields = '__all__'
class RegisterMeterSerializer(serializers.ModelSerializer):
class Meta:
model = RegisterMeter
fields = '__all__'
models.py 文件中的RegisterMeter ,用于搭载一个电表。
MeterReading 代表从电表产生的单一数据体,并发送给电力局。
CurrentUsage 是已用电量和剩余电量的读数。
我们的想法是,在加入一个新的电表时,会创建一个默认的CurrentUsage ,其中total_power_used 和power_remaining 默认设置为0 。
在每次创建MeterReading 对象时,CurrentUsage 对象会被更新。
查看
在views.py 文件中,我们创建了一个视图,以加入一个电表,如图所示。
class RegisterMeterCreateView(generics.CreateAPIView):
queryset = RegisterMeter.objects.all()
serializer_class = RegisterMeterSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
if not serializer.is_valid(raise_exception=True):
return Response({"message": "Something went wrong"}, status=status.HTTP_400_BAD_REQUEST)
meter = request.data.get('meter_id')
payload = {
"meter": meter,
"total_power_used": "0",
"power_remaining": "0",
}
CurrentUsage.objects.create(**payload)
if serializer.is_valid():
serializer.save()
return Response({"message": "Meter has been on-boarded and a default current reading created"},
status=status.HTTP_200_OK)
URL
在urls.py 文件内,我们显示访问创建的电表的端点,如下图所示。
from django.urls import path
from meter.views import RegisterMeterCreateView, check_meter_usage, CreateMeterReading, \
AllMetersListView
urlpatterns = [
path('add-meter', RegisterMeterCreateView.as_view()),
path('all-meters', AllMetersListView.as_view()),
]
要查看所有已注册的电表,请使用下面的代码片断。
class AllMetersListView(generics.ListAPIView):
queryset = RegisterMeter.objects.all()
serializer_class = RegisterMeterSerializer
因此,我们还需要创建一个视图,使用meter_id ,检查CurrentUsage 对象,如下图所示。
@api_view(['GET'])
def check_meter_usage(self, meter_id):
meter_reading = CurrentUsageSerializer(CurrentUsage.objects.get(meter=meter_id))
return Response(meter_reading.data)
同样,我们需要一个带有端点的URL来从上面的视图中获取响应。
from django.urls import path
from meter.views import RegisterMeterCreateView, check_meter_usage, CreateMeterReading, \
AllMetersListView
urlpatterns = [
path('add-meter', RegisterMeterCreateView.as_view()),
path('all-meters', AllMetersListView.as_view()),
path('meter-usage/<str:meter_id>', check_meter_usage), # New
为了创建一个单一的读表对象,我们创建一个新的视图,如下图所示。
class CreateMeterReading(generics.CreateAPIView): # New
queryset = MeterReading.objects.all()
serializer_class = MeterReadingSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
if not serializer.is_valid(raise_exception=True):
return Response({"message": "Something went wrong"}, status=status.HTTP_400_BAD_REQUEST)
meter = request.data.get('meter')
meter_reading = int(request.data.get('meter_reading'))
meter_usage_object = CurrentUsage.objects.get(meter=meter)
CurrentUsage.objects.filter(meter=meter_usage_object.meter).update(
total_power_used=F("total_power_used") + meter_reading,
power_remaining=25 - (F("total_power_used") + meter_reading)
)
meter_usage_object.refresh_from_db()
if serializer.is_valid():
serializer.save()
return Response({"message": "Reading created, current usage updated"}, status=status.HTTP_200_OK)
上面的视图的URL是。
from django.urls import path
from meter.views import RegisterMeterCreateView, check_meter_usage, CreateMeterReading, \
AllMetersListView
urlpatterns = [
path('add-meter', RegisterMeterCreateView.as_view()),
path('all-meters', AllMetersListView.as_view()),
path('meter-usage/<str:meter_id>', check_meter_usage),
path('create-reading', CreateMeterReading.as_view()), # new
]
F()是如何工作的?
现在我们已经准备好我们的模型、序列化器、视图和URL,让我们开始实际演示这个小项目。
我们使用端点localhost:8000/add-meter ,创建一个新的仪表,并相应地填入页面。
创建一个新的计量器
创建一个新的计量器对象
为了检查电表是否被成功注册,请访问localhost:8000/all-meters ,查看它们。
查看所有电表
要查看某个特定电表的细节,请访问localhost:8000/meter-usage/PM01 ,如图所示。
查看某个特定电表的详细信息
让我们为我们的电表PM01 ,创建一个新的MeterReading 对象,其值为1 (该读数将被发送到电力局),并在此过程中更新电表的CurrentUsage ,如图所示。
添加一个新的电表读数
为PM01添加一个新的电表读数
为PM01创建的电表读数
从上面,我们看到,从电表发送了一个1 单位的读数,因此,当前的使用量更新了,以反映来自MeterReading 的支出。
通过访问端点localhost:8000/meter-usage/PM01 来检查当前的使用情况,以确认POST操作是否成功。
我们假设使用量阈值为25个单位。因此,当电表读到用户已经花费了1个单位时,他还剩下24个。
PM01的MeterReading对象被更新为新的读数
让我们试试更高的电表读数,例如3 。
添加新的电表读数,数值为'3'。
我们看到,CurrentUsage 再次被更新,total_power_used 为4 ,power_remaining 为21 。
表计读数对象更新为PM01
在跟随本教程之后,你可能会有另一种见解,即人们可以操作Django模型的字段,而不必进行过多的调用和传递对象。
F()做了很多繁重的工作,你可以操纵你想操纵的许多字段。
需要注意的是
- 通过使用F(),人们可以跟踪所有的变化。例如,当
MeterReading,为了透明或审计的目的,可以获取任何用户的电力使用历史。 - 在更新任何值之前,可以设置验证和条件。例如,当更新
CurrentUsage,为每一个读数的创建,验证和条件可以在之前被设置。 - 当前的使用情况可以通过为
power_used字段发送一个负数来重置,例如-1。
结论
在这篇文章中,我们已经学会了如何使用F函数,以及为什么使用它比传统方法更好
在编程中,效率很重要。最好的办法是,我们总是寻找方法来提高我们程序的效率。在Python中,使用F函数使我们的程序更有效率。