【问题标题】:HyperlinkedModelSerializer custom lookup_field to related_modelHyperlinkedModelSerializer 自定义lookup_field 到related_model
【发布时间】:2020-06-13 13:09:49
【问题描述】:

我有以下配置,我想将UserProfileView 中的url 字段映射到相关用户的用户名,而不是默认的pk 字段

目前url 如下所示,感谢任何帮助

{
        "user": 23,
        "bio": "My bio",
        "created_on": "2020-06-12T21:24:52.746329Z",
        "url": "http://localhost:8000/bookshop/bio/8/?format=api"
    },

我要找的是

{
        "user": 23,   <--- this is the user <pk> 
        "bio": "My bio",
        "created_on": "2020-06-12T21:24:52.746329Z",
        "url": "http://localhost:8000/bookshop/bio/username/?format=api"
    },
models.py

class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
    bio = models.CharField(max_length=255)
    created_on = models.DateTimeField(auto_now_add=True)
    last_updated = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.user.username
views.py

class UserProfileViewSets(viewsets.ModelViewSet):

    authentication_classes = [TokenAuthentication, ]
    permission_classes = [rest_permissions.IsAuthenticated, permissions.UserProfileOwnerUpdate, ]
    queryset = models.UserProfile.objects.all()
    serializer_class = serializers.UserProfileSerializer
    renderer_classes = [renderers.AdminRenderer, renderers.JSONRenderer, renderers.BrowsableAPIRenderer, ]
    # lookup_field = 'user.username'

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)
serializer.py

class UserProfileSerializer(serializers.ModelSerializer):

    class Meta:
        model = models.UserProfile
        fields = ['user', 'bio', 'created_on', 'url']
        extra_kwargs = {
            'last_updated': {
                'read_only': True
            },
            'user': {
                'read_only': True
            },
        }

【问题讨论】:

  • 谁能帮我解决这个问题

标签: django serialization django-rest-framework related-content


【解决方案1】:

在努力阅读了许多文章之后,如果有人在寻找相同的用例,我做到了并发布了解决方案。

  • 字段通过 OneToOne 关系相互关联
models.py

class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
    bio = models.CharField(max_length=255)
    created_on = models.DateTimeField(auto_now_add=True)
    last_updated = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.user.username

class User(AbstractBaseUser, PermissionsMixin):
    """"
    Customizes the default user account
    """
    email = models.EmailField(unique=True, help_text='username is the email address')
    first_name = models.CharField(max_length=40, blank=False)
    last_name = models.CharField(max_length=40, blank=False)
    date_joined = models.DateTimeField(auto_now_add=True)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    username = models.CharField(max_length=15, unique=True, null=True, blank=False,
                                validators=(validators.UnicodeUsernameValidator, ))
    is_borrower = models.BooleanField(default=False)

  • serializer 是一个HyperlinkedModelSerializer,如下所示user SerializerField 是PrimaryKeyRelatedField,它与User 模型user.username 中的另一个列/字段相关 - 我将其设为默认PrimaryKeyRelatedFieldpk,我不想在API 上公开它

  • url 键被自定义为 HyperlinkedRelatedField 以指向上述字段 - user 具有视图名称 user-related

serializer.py

class UserProfileSerializer(serializers.HyperlinkedModelSerializer):

    user = serializers.PrimaryKeyRelatedField(source='user.username', read_only=True)
    url = serializers.HyperlinkedRelatedField(read_only=True, view_name='user-detail', )

    class Meta:
        model = models.UserProfile
        fields = ['user', 'bio', 'created_on', 'url']
        extra_kwargs = {
            'last_updated': {
                'read_only': True
            },
            'user': {
                'read_only': True
            },
        }
  • 在视图上,我将lookup_field 定义为user 并覆盖get_object 方法,因为现在查询集应由username 过滤
views.py

class UserProfileViewSets(viewsets.ModelViewSet):

    authentication_classes = [TokenAuthentication, ]
    permission_classes = [rest_permissions.IsAuthenticated, permissions.UserProfileOwnerUpdate, ]
    queryset = models.UserProfile.objects.all()
    serializer_class = serializers.UserProfileSerializer
    renderer_classes = [renderers.AdminRenderer, renderers.JSONRenderer, renderers.BrowsableAPIRenderer, ]
    lookup_field = 'user'

    def perform_create(self, serializer):
        serializer.save(user=self.request.user)

    def get_object(self):
        queryset = self.filter_queryset(models.UserProfile.objects.get(user__username=self.kwargs.get('user')))
        return queryset

编辑:

我用另一种方法完成了要求,认为这种方法更简洁,所以在下面进行修改。

  • 您需要在kwargs 的右上方创建一个新的自定义HyperLinkedIdentityField,检查下面的kwargs,该值映射到定义OneToOneForgienKey 的相关模型

    class AuthorHyperLinkedIdentityField(serializers.HyperlinkedIdentityField):
        def get_url(self, obj, view_name, request, format):
            if hasattr(obj, 'pk') and obj.pk is None:
                return None
            return self.reverse(view_name, kwargs={
                'obj_username': obj.author.username
            }, format=format, request=request)

  • 在您使用 CustomizedField 中定义的 kwargs 覆盖 lookup_field 的视图上

    class AuthorViewSet(viewsets.ModelViewSet):
        serializer_class = serializers.AuthorSerializer
        queryset = models.Author.objects.all()
        renderer_classes = [renderers.JSONRenderer, renderers.BrowsableAPIRenderer, renderers.AdminRenderer]
        # the below is not used but i keep it for reference
        # lookup_field = 'author__username'
        # the below should match the kwargs in the customized HyperLinkedIdentityField
        lookup_field = 'obj_username'

  • 最终的序列化程序如下所示
class AuthorSerializer(serializers.HyperlinkedModelSerializer):
    """
    Serializers Author Model
    """

    # first_name = serializers.SlugRelatedField(source='author', slug_field='first_name',
    #                                           read_only=True)
    # last_name = serializers.SlugRelatedField(source='author', slug_field='last_name',
    #                                          read_only=True)
    author = serializers.PrimaryKeyRelatedField(queryset=models.User.objects.filter(groups__name='Authors'),
                                                write_only=True)
    name = serializers.SerializerMethodField()
    username = serializers.PrimaryKeyRelatedField(source='author.username', read_only=True)
    # the below commented line is building the URL field based on the lookup_field = username
    # which takes its value from the username PrimaryKeyRelatedField above
    # url = serializers.HyperlinkedRelatedField(view_name='user-detail', read_only=True)
    url = AuthorHyperLinkedIdentityField(view_name='author-detail', read_only=True)

    class Meta:
        model = models.Author
        fields = ['author', 'name', 'username', 'url', ]

    def get_name(self, author):
        return '%s %s' % (author.author.first_name, author.author.last_name)
  • 作者模型下方供您参考
class Author(models.Model):
    """
    A Model to store the Authors info
    """
    author = models.OneToOneField(User, on_delete=models.CASCADE, related_name='authors')
    is_author = models.BooleanField(default=True, editable=True, )

    class Meta:
        constraints = [
            models.UniqueConstraint(fields=['author'], name='check_unique_author')
        ]

    def __str__(self):
        return '%s %s' % (self.author.first_name, self.author.last_name)

    def author_full_name(self):
        return '%s %s' % (self.author.first_name, self.author.last_name)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-18
    • 2020-07-22
    • 2017-10-29
    • 2018-03-01
    • 1970-01-01
    • 2021-05-28
    • 2023-04-02
    相关资源
    最近更新 更多