如何在Django中使用F函数对多个字段进行运行时更新

132 阅读6分钟

在Django中使用F函数对多个字段进行运行时更新

在本教程中,我们将学习如何使用Django中的F 函数连续更新多个字段。

让我们想象一个场景--尼日利亚的一个爱国者通常在他的家里安装了一个电表。他每用一次电,用电数据就会从电表上传到电力局。

因此,通过智能手机向该用户发送两个数值,详细说明他的总耗电量和剩余电量。

在本教程中,我们将模拟一个电表,在一端读取用户的用电量,并在另一端流式传输电表生成的数据(消耗率、剩余电量和总用电量)。

这种运行时的数据流可以通过Django的F() 功能来轻松有效地完成。

前提条件

要充分利用本教程,需要具备以下条件。

  • 对Python的基本了解。
  • 熟悉Django框架和Django REST框架。
  • 熟悉Django的可浏览API界面。
  • 安装了PyCharm专业代码编辑器。

F()的效率如何?

传统的方法是不断地获取和迭代数据流--读数,获得所用电量的总和,并从阈值中减去,以获得所用电量和剩余电量的值。这是个效率较低的方法。

有了F(),一个单一的读数对象(为用户准备的)可以在不参考以前的数据的情况下被即时更新,可以被保存到数据库,并准备好让用户查看。你可以一次为多个字段做这件事。

实施

在这个关于F函数如何工作的演示中,我们将模拟一个读表过程,如本文前面所述。

模型

我们将需要三个模型。RegisterMeter,MeterReadingCurrentUsage 。下面的片段显示了创建这些模型的代码。

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_usedpower_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 ,创建一个新的仪表,并相应地填入页面。

wecreatemeter 创建一个新的计量器

wecreatedmeter 创建一个新的计量器对象

为了检查电表是否被成功注册,请访问localhost:8000/all-meters ,查看它们。

allmeterlist 查看所有电表

要查看某个特定电表的细节,请访问localhost:8000/meter-usage/PM01 ,如图所示。

defmeterusage 查看某个特定电表的详细信息

让我们为我们的电表PM01 ,创建一个新的MeterReading 对象,其值为1 (该读数将被发送到电力局),并在此过程中更新电表的CurrentUsage ,如图所示。

create-reading-act 添加一个新的电表读数

create-reading-act1 为PM01添加一个新的电表读数

create-reading-act2 为PM01创建的电表读数

从上面,我们看到,从电表发送了一个1 单位的读数,因此,当前的使用量更新了,以反映来自MeterReading 的支出。

通过访问端点localhost:8000/meter-usage/PM01 来检查当前的使用情况,以确认POST操作是否成功。

我们假设使用量阈值为25个单位。因此,当电表读到用户已经花费了1个单位时,他还剩下24个。

create-reading-act3 PM01的MeterReading对象被更新为新的读数

让我们试试更高的电表读数,例如3

create-reading-act4 添加新的电表读数,数值为'3'。

我们看到,CurrentUsage 再次被更新,total_power_used4power_remaining21

create-reading-act5 表计读数对象更新为PM01

在跟随本教程之后,你可能会有另一种见解,即人们可以操作Django模型的字段,而不必进行过多的调用和传递对象。

F()做了很多繁重的工作,你可以操纵你想操纵的许多字段。

需要注意的是

  • 通过使用F(),人们可以跟踪所有的变化。例如,当MeterReading ,为了透明或审计的目的,可以获取任何用户的电力使用历史。
  • 在更新任何值之前,可以设置验证和条件。例如,当更新CurrentUsage ,为每一个读数的创建,验证和条件可以在之前被设置。
  • 当前的使用情况可以通过为power_used 字段发送一个负数来重置,例如-1

结论

在这篇文章中,我们已经学会了如何使用F函数,以及为什么使用它比传统方法更好

在编程中,效率很重要。最好的办法是,我们总是寻找方法来提高我们程序的效率。在Python中,使用F函数使我们的程序更有效率。