Python DRF PrimaryKeyRelatedField使用uuid代替PK

时间:2019-09-03 11:37:09

标签: python django-rest-framework

我正在编写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
}

当然,我希望此行为适用于我的所有ForeignKeys和ManyToMany字段。 有没有一种方法可以自定义DRF用于嵌套模型的字段? 谢谢!

3 个答案:

答案 0 :(得分:1)

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


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', slug_name=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',
        )

答案 1 :(得分:0)

对于嵌套模型字段,您可以在类似这样的序列化器中使用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',)

答案 2 :(得分:0)

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

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