使Django REST Framework的HyperlinkedModelSerializer识别非默认路由
我正在进行一个项目,使用的是Django、Django REST框架和SQL。我对DRF非常陌生。
我有一个模型,用来跟踪游戏服务的用户信息,这个服务在不同的地区(比如北美、欧洲等)运行不同的服务器。用户ID在每个地区是唯一的,但所有用户都使用同一个模型(表)来存储。我在模型的Meta类中使用了unique_together = ('user_id', 'region')
,以确保没有重复的记录。请注意,因此数据库中的主键(PK)与用户ID没有关系。
DRF默认会使用数据库中用户的主键来创建接口,但我把它改成了像/users/na/123
这样的形式,以获取user_id = 123
和region = '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 个回答
我今天也遇到了同样的问题。在查看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"
...现在一切都运行得很好。希望这对你有帮助。
经过几天的阅读和换个角度思考这个问题,我发现 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
我还在继续探索其他选项,但这个看起来挺简洁的。