使用 perform_update 的 Django Rest Framework 时态数据库

Temporal Database with Django Rest Framework using perform_update

我们正在尝试实施时态数据库,以便我们能够跟踪所做的更改

我们所有的模型都有以下字段

vt = models.DateTimeField(db_column='VT', default=datetime(3000, 12, 31, 23, 00, 00, 000000))  # Value To
vflag = models.IntegerField(db_column='VFlag', default=1)  # Version Flag 1 = Current, 0 = Old   

在使用 Django 休息框架时,我尝试修改视图集中的 perform_update 以复制现有记录,进行更新,然后适当地设置时间字段。

当我有 1 条记录和第一次更新时有效

然而,一旦我尝试进行第二次更新,它就会失败并创建更改的副本并覆盖第一个记录。

原始记录

Currency = AUD, VFlag = 1, VT = time1

执行更新 - 成功

Currency = USD, VFlag = 1, VT = time2

Currency = AUD, VFlag = 0, VT = time1

下一步执行当前生成的更新 - 失败

Currency = GBP, VFlag = 1, VT = time3

Currency = GBP, VFlag = 1, VT = time3

Currency = USD , VFlag = 0, VF = time2

预期更新输出

Currency = GBP, VFlag = 1, VT = time3

Currency = USD, VFlag = 0, VT = time2

Currency = AUD, VFlag = 0, VT = time1

在 django rest 中是否可以使用时态数据库?

有没有人能指出正确的方向

下面是我的视图集代码

class OptionMasterViewSet(viewsets.ModelViewSet):
serializer_class = OptionMasterSerializer
paginate_by = None
queryset = OptionMaster.objects.filter(vflag=1)
# queryset = OptionMaster.objects.all()

def get_queryset(self):
    queryset = OptionMaster.objects.filter(vflag=1)
    contractcode = self.request.query_params.get('contractcode', None)
    if contractcode is not None:
        queryset = queryset.filter(contractcode=contractcode, vflag=1)
    return queryset

def perform_update(self, serializer):

    changes = serializer.validated_data
    original_object = serializer.instance

    vt = datetime.now()

    changes['vf'] = vt

    #Build the old record
    old_record = {}
    for field in original_object._meta.get_fields():           
        old_record[field.name] = getattr(original_object, field.name)

    old_record['vflag'] = 0                
    old_record['vt'] = vt

    old_record = OrderedDict(old_record)

    #Save the new rrecord
    serializer.save()

    #Create the old record
    obj = OptionMaster.objects.create(**old_record)

    return serializer

我的连载

class OptionMasterSerializer(TemporalModelSerializer):

class Meta:
    model = OptionMaster
    fields = '__all__'

潜在的时间序列化器

class TemporalHyperlinkedModelSerializer(serializers.HyperlinkedModelSerializer):
vt = serializers.HiddenField(default=datetime(3000, 12, 31, 00, 00, 00, 000000))
vflag = serializers.HiddenField(default=1)

class TemporalModelSerializer(serializers.ModelSerializer):
vt = serializers.HiddenField(default=datetime(3000, 12, 31, 23, 00, 00, 000000))
vflag = serializers.HiddenField(default=1)

class TemporalModel(models.Model):
vt = models.DateTimeField(db_column='VT')  # Field name made lowercase.
vflag = models.IntegerField(db_column='VFlag')  # Field name made lowercase..
class Meta:
        abstract = True

老实说,我以前从未处理过这样的问题,但这更多是数据库设计问题,而不是 django-rest 问题。因此,在不太了解这些模型的用途和关系的情况下,我只能做一个大概的猜测(如下)。

我的一个直观方法是实现一个额外的 IntegerField,称为 series_group_id 的东西,它将用作分组时间序列的标识符和 created_add DateTimeFieldauto_now_add 标志设置为 True。这样,该字段会在创建时填充 datetime 值。

class SomeModel(models.Model):
    # some other stuff like vflag and vt
    goup_id = models.IntegerField()
    created_at = models.DateTimeField(auto_now_add=True)

此外,如果您要跟踪时间序列,从技术上讲,您不是在更新实例,而是在创建新实例。因此,我的方法是将您的整个逻辑移至 create 方法。事实上,ModelSerializer 提供了这种开箱即用的行为。您唯一需要做的就是将 group_id 添加到序列化程序的 fields 元组中。当您使用“all”时,该字段将在您将其添加到模型后立即自动出现在您的字段中。

如果您需要更精细的控制,您可以重写 create 方法,如下所示:

class OptionMasterViewSet(viewsets.ModelViewSet):
    serializer_class = OptionMasterSerializer
    paginate_by = None
    queryset = OptionMaster.objects.filter(vflag=1)


    def get_queryset(self):
        queryset = OptionMaster.objects.filter(vflag=1)
        contractcode = self.request.query_params.get('contractcode', None)
        if contractcode is not None:
            queryset = queryset.filter(contractcode=contractcode, vflag=1)
        return queryset

    def create(self, request):
        data = request.get('data', None)
        serializer = self.serializer_class(data=data)
        serializer.is_valid(raise_exception=True)

        # some custom logic here in case you need it..
        self.perform_create(serializer)
        return Response({'detail': 'ok'}, status=status.HTTP_201_CREATED)

限制

此方法期望到达的数据包含您当前正在跟踪的相应时间序列的 group_id

我的问题的解决方案是使用字典数据进行过滤和更新。

 self.Meta.model.objects.filter(pk=instance.pk, vflag=1).update(**new_record)

下面是我最后的工作 TemporalModelSerializer

    class TemporalModelSerializer(serializers.ModelSerializer):
        vf = serializers.HiddenField(default=timezone.now())
        vt = serializers.HiddenField(default=datetime(3000, 12, 31, 23, 00, 00, 000000))
        vflag = serializers.HiddenField(default=1)
        vu = serializers.HiddenField(default='Theodore')

        class Meta:
            model = None
            fields = '__all__'

        def update(self, instance, validated_data):

            time_now = timezone.now()

            old_record = {}
            new_record = {}

            for (key, value) in validated_data.items():
                old_record[key] = getattr(instance, key)
                new_record[key] = validated_data[key]
                setattr(instance, key, value)

            old_record['vt'] = time_now
            old_record['vflag'] = 0
            new_record['vf'] = time_now

            self.delete_me(old_record)

            self.Meta.model.objects.filter(pk=instance.pk, vflag=1).update(**new_record)

            return instance

        def delete_me(self, old_record):
            obj = self.Meta.model.objects.create(**old_record)