使用 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
DateTimeField
将 auto_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)
我们正在尝试实施时态数据库,以便我们能够跟踪所做的更改
我们所有的模型都有以下字段
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
DateTimeField
将 auto_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)