Python DRF PrimaryKeyRelatedField 使用 uuid 而不是 PK



我正在编写一个Django REST Framework API。 我的模型有默认的 Django PK 供内部使用,uuid 字段供外部参考。

class BaseModel(models.Model):
uuid = models.UUIDField(default=uuid.uuid4, editable=False)
class Event(BaseModel):
title = models.TextField()
location = models.ForeignKey('Location', null=True, on_delete=models.SET_NULL)
class Location(BaseModel):
latitude  = models.FloatField()
longitude = models.FloatField()

还有我的序列化程序:

class BaseSerializer(serializers.ModelSerializer):
default_fields = ('uuid',)                                                                        
class EventSerializer(BaseSerializer):                                                                                                                   
class Meta:                                                                  
model = Event                                                                                 
lookup_field = 'uuid' # This does not work                                                                      
fields = BaseSerializer.default_fields + ('title', 'location',)
class LocationSerializer(BaseSerializer):
class Meta:
model = Location
lookup_field = 'uuid' # This does not work 
fields = BaseSerializer.default_fields + ('latitude', 'longitude',)   

这工作正常,这是我检索事件时得到的:

{
"uuid": "ef33db27-e98b-4c26-8817-9784dfd546c6",
"title": "UCI Worldcup #1 Salzburg",
"location": 1 # Note here I have the PK, not UUID
}

但我想要的是:

{
"uuid": "ef33db27-e98b-4c26-8817-9784dfd546c6",
"title": "UCI Worldcup #1 Salzburg",
"location": "2454abe7-7cde-4bcb-bf6d-aaff91c107bf" # I want UUID here
}

当然,我希望这种行为适用于我所有的外键和多对多字段。 有没有办法自定义DRF用于嵌套模型的字段? 谢谢!

from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ObjectDoesNotExist
from rest_framework.relations import RelatedField
from django.utils.encoding import smart_text

class UUIDRelatedField(RelatedField):
"""
A read-write field that represents the target of the relationship
by a unique 'slug' attribute.
"""
default_error_messages = {
'does_not_exist': _('Object with {uuid_field}={value} does not exist.'),
'invalid': _('Invalid value.'),
}
def __init__(self, uuid_field=None, **kwargs):
assert uuid_field is not None, 'The `uuid_field` argument is required.'
self.uuid_field = uuid_field
super().__init__(**kwargs)
def to_internal_value(self, data):
try:
return self.get_queryset().get(**{self.uuid_field: data})
except ObjectDoesNotExist:
self.fail('does_not_exist', uuid_field=self.uuid_field, value=smart_text(data))
except (TypeError, ValueError):
self.fail('invalid')
def to_representation(self, obj):
return getattr(obj, self.uuid_field)

示例用法:

class ProductSerializer(serializers.ModelSerializer):
category = UUIDRelatedField(
queryset=Category.objects.all(),
uuid_field='alias'
)

class Meta:
model = Product
fields = (
'id',
'alias',
'name',
'category',
)
read_only_fields = (
'id',
'alias',
)

请注意,从 Django 版本 4 开始,删除了smart_textugettext_lazy,请使用smart_strgettext_lazy代替它们:

from django.utils.encoding import gettext_lazy
from django.utils.encoding import smart_str

我的一个朋友给我发了这个解决方案: 它适用于我所有相关的对象。

from rest_framework import serializers                                                                
from rest_framework.relations import SlugRelatedField                 
class UuidRelatedField(SlugRelatedField):                                                             
def __init__(self, slug_field=None, **kwargs):                                                    
slug_field = 'uuid'
super().__init__(slug_field, **kwargs)                                                        

class BaseSerializer(serializers.ModelSerializer):                                                    
default_fields = ('uuid',)                                                                        
serializer_related_field = UuidRelatedField
class Meta:                                                                                       
pass

对于嵌套模型字段,您可以在序列化程序中使用source参数,如下所示

class EventSerializer(BaseSerializer):
location = serializers.CharField(source='location.uuid')
class Meta:
model = Event
lookup_field = 'uuid'  # This does not work
fields = BaseSerializer.default_fields + ('title', 'location',)

最新更新