HyperlinkedModelSerializer 自定义 lookup_field 到 related_model
HyperlinkedModelSerializer custom lookup_field to related_model
我有以下配置,我想将 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
},
}
经过努力和阅读许多文章后,我做到了,如果有人正在寻找相同的用例,我会发布解决方案。
- 字段通过一对一关系相互关联
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
并且正在与另一个 column/field 在 User
模型中 user.username
- 我将其设为默认值 PrimaryKeyRelatedField
是 pk
我不想在 API
[= 上公开它37=]
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)
我有以下配置,我想将 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
},
}
经过努力和阅读许多文章后,我做到了,如果有人正在寻找相同的用例,我会发布解决方案。
- 字段通过一对一关系相互关联
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
并且正在与另一个 column/field 在User
模型中user.username
- 我将其设为默认值PrimaryKeyRelatedField
是pk
我不想在API
[= 上公开它37=]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)