使Django REST Framework的HyperlinkedModelSerializer识别非默认路由

3 投票
2 回答
1498 浏览
提问于 2025-04-18 08:14

我正在进行一个项目,使用的是Django、Django REST框架和SQL。我对DRF非常陌生。

我有一个模型,用来跟踪游戏服务的用户信息,这个服务在不同的地区(比如北美、欧洲等)运行不同的服务器。用户ID在每个地区是唯一的,但所有用户都使用同一个模型(表)来存储。我在模型的Meta类中使用了unique_together = ('user_id', 'region'),以确保没有重复的记录。请注意,因此数据库中的主键(PK)与用户ID没有关系。

DRF默认会使用数据库中用户的主键来创建接口,但我把它改成了像/users/na/123这样的形式,以获取user_id = 123region = 'na'(北美)的对象。以下是urls.py中的相关代码片段:

url(r'^users/(?P<region>.+)/$', UserList.as_view()),
url(r'^users/(?P<region>.+)/(?P<user_id>.+$)', UserDetail.as_view()),

这些是通用视图(generics.ListAPIView和generics.RetrieveAPIView)。

目前,我的其他视图都是ViewSets。

我建模的一个内容是历史比赛数据,用户通过一个游戏模型关联,以跟踪谁参与了比赛,像这样:

class Game(models.Model):
    player_1 = models.ForeignKey(User)
    player_2 = models.ForeignKey(User)

我计划为游戏实现一个类似于用户的路由(再次强调,game_id在每个地区也是唯一的),这样我就可以使用/game/<region>/<game_id>

我的问题是:

我如何在API的游戏列表/详情视图中,使用我已经建立的/user/<region>/<user_id>路由来获取用户的超链接?

class GameSerializer(serializers.ModelSerializer):
    class Meta:
        model = Game
        exclude = ('id',)

当我把它改成HyperlinkedModelSerializer时,访问游戏接口时出现以下错误:

无法通过视图名称“user-detail”解析超链接关系的URL。你可能没有在API中包含相关模型,或者在这个字段上错误配置了lookup_field属性。

我猜这是因为我的用户接口实现方式与它预期的不同(它无法知道我放弃了默认的主键索引方法,而选择了像/users/<region>/<user_id>这样的自定义路由,而不是/users/<pk>,对吧?)

我该如何解决这个问题?如果我想要的方向看起来不合理(不想使用主键),我也愿意接受一些与DRF无关的建议,比如重构我的数据库/Django模型。

2 个回答

0

我今天也遇到了同样的问题。在查看Django-Rest-Framework的文档时,发现了一个叫做“lookup_field”的东西。

lookup_field是用来查找单个模型实例的字段。默认情况下,它使用的是'pk',也就是主键。如果你在使用超链接的API时,需要确保API视图和序列化类都设置了查找字段,特别是当你想用自定义值时。

http://www.django-rest-framework.org/api-guide/generic-views

在我的情况下,我在models.py文件里做了这个设置。

class UserDetailView(generics.RetrieveAPIView):
    model = User
    serializer_class = UserSerializer
    lookup_field = "username"

...现在一切都运行得很好。希望这对你有帮助。

1

经过几天的阅读和换个角度思考这个问题,我发现 Meta.unique_together 有点像在SQL中表达一个复合键。这让我找到了这个解决方案:

https://groups.google.com/forum/#!topic/django-rest-framework/tHmEAzSNgG4

比如,之前我用这样的URL来识别一个员工:

api/1.3/employee/5/

现在我改用这样的URL:

api/1.3/company/23/employee/5/

我使用了一个叫做 HyperlinkedModelSerializer 的工具来处理这个模型。不过,我找不到办法配置 HyperlinkedIdentityField 来处理复合键(因为它只能指定一个查找字段),所以我用一个 SerializerMethodField 来覆盖这个URL,像这样:

class EmployeeSerializer(serializers.HyperlinkedModelSerializer):
   url = serializers.SerializerMethodField('get_employee_detail_url')


   def get_employee_detail_url(self, obj):
       # generate the URL for the composite key
       ...
       return composite_key_url

我还在继续探索其他选项,但这个看起来挺简洁的。

撰写回答