在 Django DRFs 的 `ListCreateAPIView` POST 方法中,我们如何获取模型字段的 pk?
In Django DRFs' `ListCreateAPIView` POST method, how do we get the pk of a model field?
我有一个简单的 ModelSerializer
,看起来像这样:
class DBaaSOrderSerializer(serializers.ModelSerializer):
class Meta:
model = DBaaSOrder
fields = '__all__'
上面还有一些validate
方法与本题无关
现在,我在基于 Class 的视图中使用上述序列化程序,如下所示:
class DBaaSOrderView(generics.ListCreateAPIView):
serializer_class = DBaaSOrderSerializer
permission_classes = (permissions.IsAuthenticated,)
def get_queryset(self):
usd = UsdDBActions()
orders = DBaaSOrder.objects.filter(user=self.request.user).order_by('updated_at')
for change_ref, change_status, updated_at in orders.values_list('change_ref', 'change_status', 'updated_at'):
usd_change_status = usd.get_rfc_status(change_ref)
usd_change_updated_at = usd.get_last_mod_date(change_ref)
if change_status != usd_change_status:
orders.filter(change_ref=change_ref).update(change_status=usd_change_status)
if updated_at != usd_change_updated_at:
orders.filter(change_ref=change_ref).update(updated_at=usd_change_updated_at)
return orders.filter(change_status__in=VALID_USD_STATUSES)[:MAX_ORDERS]
@staticmethod
def get_dns(value):
dns_records = DNS.objects.all()
for dns in dns_records:
if dns.server in value and dns.domain in value:
return dns
@staticmethod
def get_backup_plan(value):
backup_plans = BackupPlan.objects.all()
for backup_plan in backup_plans:
if backup_plan.customer_policy_name in value and backup_plan.retention in value:
return backup_plan
def post(self, request, *args, **kwargs):
data = request.data
data['user'] = request.user.pk
dns_record = self.get_dns(data['dns'])
backup_plan = self.get_backup_plan(data['backup'])
dns_record_pk = dns_record.pk
backup_plan_pk = backup_plan.pk
data['dns'] = dns_record_pk
data['backup'] = backup_plan_pk
serializer = DBaaSOrderSerializer(data=data)
if serializer.is_valid():
usd = UsdDBActions()
data = request.data
usd_data = {
'organization': int(data['organization_pk']),
'category': 'SSC.DBM.DB.ADD',
'assignee': request.user.username,
}
response = usd.create_rfc(usd_data)
try:
result = response.json()
if result['code'] == 200:
change_ref = int(response.json()['reference_number'])
data['user'] = request.user
data['change_ref'] = int(response.json()['reference_number'])
data['change_status'] = usd.get_rfc_status(change_ref)
data['dns'] = dns_record
data['backup'] = backup_plan
DBaaSOrder.objects.create(**data)
return Response({
'message': f"The RFC {data['change_ref']} has been successfully created!",
'code': 200
})
一切都按预期工作,但我不喜欢的是我有 2 种方法 get_dns()
和 get_backup_plan()
来获取 dns
的实际模型对象或backup
个字段。没有更简单的方法吗?
这样做的原因是:
- 这里,
serializer = DBaaSOrderSerializer(data=data)
,data
应该包含字段的pk
;
- 这里,
DBaaSOrder.objects.create(**data)
,data
应该包含模型字段的实例;
更多,dns
和backup
是外键:
class DNS(models.Model):
server = models.CharField(max_length=64)
domain = models.CharField(max_length=64)
def __str__(self):
return f'{self.server} - {self.domain}'
class BackupPlan(models.Model):
customer_policy_name = models.CharField(max_length=64)
retention = models.CharField(max_length=64)
def __str__(self):
return f'{self.customer_policy_name} - {self.retention}'
主模型长这样:
class DBaaSOrder(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
dns = models.ForeignKey(DNS, on_delete=models.CASCADE)
backup = models.ForeignKey(BackupPlan, on_delete=models.CASCADE)
# ... other fields here
这里有两个相关的问题。首先是将“192.168.0.1 - test.domain.com”转换为 DNS 实例的逻辑。其次是您如何进行数据库查找。
这种事情总是属于序列化程序;这是负责将接收到的数据转换为正确格式的代码。这个特定的逻辑属于验证方法;它们的名称为 validate_<fieldname>
,因此我们需要 validate_dns
和 validate_backup
。
目前您执行数据库查找的方式效率很低。我们可以通过拆分输入然后使用该数据进行直接数据库查询来更简单地做到这一点。
为了完成所有这些工作,我们需要覆盖这些字段的定义,使它们成为 CharFields,以便它们接受任意数据。然后验证方法将负责将它们转换为实际的模型实例。
所以:
class DBaaSOrderSerializer(serializers.ModelSerializer):
dns = serializers.CharField()
backup = serializers.CharField()
class Meta:
model = DBaaSOrder
fields = '__all__'
def validate_dns(self, value):
try:
server, domain = value.split(' - ')
except ValueError:
raise serializers.ValidationError('not in correct format')
try:
instance = DNS.objects.get(server=server, domain=domain)
except DNS.DoesNotExist:
raise serializers.ValidationError('no matching DNS')
return instance
def validate_backup(self, value):
... similar to above...
现在您可以摆脱 post
中的自定义逻辑。
我有一个简单的 ModelSerializer
,看起来像这样:
class DBaaSOrderSerializer(serializers.ModelSerializer):
class Meta:
model = DBaaSOrder
fields = '__all__'
上面还有一些validate
方法与本题无关
现在,我在基于 Class 的视图中使用上述序列化程序,如下所示:
class DBaaSOrderView(generics.ListCreateAPIView):
serializer_class = DBaaSOrderSerializer
permission_classes = (permissions.IsAuthenticated,)
def get_queryset(self):
usd = UsdDBActions()
orders = DBaaSOrder.objects.filter(user=self.request.user).order_by('updated_at')
for change_ref, change_status, updated_at in orders.values_list('change_ref', 'change_status', 'updated_at'):
usd_change_status = usd.get_rfc_status(change_ref)
usd_change_updated_at = usd.get_last_mod_date(change_ref)
if change_status != usd_change_status:
orders.filter(change_ref=change_ref).update(change_status=usd_change_status)
if updated_at != usd_change_updated_at:
orders.filter(change_ref=change_ref).update(updated_at=usd_change_updated_at)
return orders.filter(change_status__in=VALID_USD_STATUSES)[:MAX_ORDERS]
@staticmethod
def get_dns(value):
dns_records = DNS.objects.all()
for dns in dns_records:
if dns.server in value and dns.domain in value:
return dns
@staticmethod
def get_backup_plan(value):
backup_plans = BackupPlan.objects.all()
for backup_plan in backup_plans:
if backup_plan.customer_policy_name in value and backup_plan.retention in value:
return backup_plan
def post(self, request, *args, **kwargs):
data = request.data
data['user'] = request.user.pk
dns_record = self.get_dns(data['dns'])
backup_plan = self.get_backup_plan(data['backup'])
dns_record_pk = dns_record.pk
backup_plan_pk = backup_plan.pk
data['dns'] = dns_record_pk
data['backup'] = backup_plan_pk
serializer = DBaaSOrderSerializer(data=data)
if serializer.is_valid():
usd = UsdDBActions()
data = request.data
usd_data = {
'organization': int(data['organization_pk']),
'category': 'SSC.DBM.DB.ADD',
'assignee': request.user.username,
}
response = usd.create_rfc(usd_data)
try:
result = response.json()
if result['code'] == 200:
change_ref = int(response.json()['reference_number'])
data['user'] = request.user
data['change_ref'] = int(response.json()['reference_number'])
data['change_status'] = usd.get_rfc_status(change_ref)
data['dns'] = dns_record
data['backup'] = backup_plan
DBaaSOrder.objects.create(**data)
return Response({
'message': f"The RFC {data['change_ref']} has been successfully created!",
'code': 200
})
一切都按预期工作,但我不喜欢的是我有 2 种方法 get_dns()
和 get_backup_plan()
来获取 dns
的实际模型对象或backup
个字段。没有更简单的方法吗?
这样做的原因是:
- 这里,
serializer = DBaaSOrderSerializer(data=data)
,data
应该包含字段的pk
; - 这里,
DBaaSOrder.objects.create(**data)
,data
应该包含模型字段的实例;
更多,dns
和backup
是外键:
class DNS(models.Model):
server = models.CharField(max_length=64)
domain = models.CharField(max_length=64)
def __str__(self):
return f'{self.server} - {self.domain}'
class BackupPlan(models.Model):
customer_policy_name = models.CharField(max_length=64)
retention = models.CharField(max_length=64)
def __str__(self):
return f'{self.customer_policy_name} - {self.retention}'
主模型长这样:
class DBaaSOrder(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
dns = models.ForeignKey(DNS, on_delete=models.CASCADE)
backup = models.ForeignKey(BackupPlan, on_delete=models.CASCADE)
# ... other fields here
这里有两个相关的问题。首先是将“192.168.0.1 - test.domain.com”转换为 DNS 实例的逻辑。其次是您如何进行数据库查找。
这种事情总是属于序列化程序;这是负责将接收到的数据转换为正确格式的代码。这个特定的逻辑属于验证方法;它们的名称为 validate_<fieldname>
,因此我们需要 validate_dns
和 validate_backup
。
目前您执行数据库查找的方式效率很低。我们可以通过拆分输入然后使用该数据进行直接数据库查询来更简单地做到这一点。
为了完成所有这些工作,我们需要覆盖这些字段的定义,使它们成为 CharFields,以便它们接受任意数据。然后验证方法将负责将它们转换为实际的模型实例。
所以:
class DBaaSOrderSerializer(serializers.ModelSerializer):
dns = serializers.CharField()
backup = serializers.CharField()
class Meta:
model = DBaaSOrder
fields = '__all__'
def validate_dns(self, value):
try:
server, domain = value.split(' - ')
except ValueError:
raise serializers.ValidationError('not in correct format')
try:
instance = DNS.objects.get(server=server, domain=domain)
except DNS.DoesNotExist:
raise serializers.ValidationError('no matching DNS')
return instance
def validate_backup(self, value):
... similar to above...
现在您可以摆脱 post
中的自定义逻辑。