我有一个模型FinancialTransaction
,其中包含典型的content_type
,object_id
和content_object
字段,可以与我的任何其他模型建立通用关系
我已经弄明白了如何序列化这种关系以便阅读:
class FinancialTransactionSerializer(serializers.HyperlinkedModelSerializer):
content_object = serializers.SerializerMethodField('get_content_obj_url')
def get_content_obj_url(self, obj):
obj = obj.content_object
view_name = obj._meta.object_name.lower() + "-detail"
s = serializers.HyperlinkedIdentityField(source=obj, view_name=view_name)
s.initialize(self, None)
return s.field_to_native(obj, None)
class Meta:
model = FinancialTransaction
fields = ('id', 'value', 'date', 'memo', 'banking_account', 'content_object')
ViewSet:
class FinancialTransactionViewSet(viewsets.ModelViewSet):
model = FinancialTransaction
serializer_class = FinancialTransactionSerializer
当我在视图上执行GET时,这会为序列化表示创建一个指向相关对象的超链接。
但是,我有点坚持如何制作它,以便我可以使用已经存在的相关对象发布新的FinancialTransaction。
理想情况下,它可以像普通的ForeignKey一样工作,我可以发布类似的东西:
{"value": "200.00",
"date": "2014-10-10",
"memo": "repairs",
"banking_account": "http://domain.com/api/banking_account/134/",
"content_object": "http://domain.com/api/property/432/"
}
答案 0 :(得分:1)
好的,回答我自己的问题......
我在我自己的序列化程序中覆盖restore_fields
,如下所示:
class FinancialTransactionSerializer(serializers.HyperlinkedModelSerializer):
content_object = serializers.SerializerMethodField('get_content_obj_url')
def get_content_obj_url(self, obj):
obj = obj.content_object
view_name = get_view_name(obj)
s = serializers.HyperlinkedIdentityField(source=obj, view_name=view_name)
s.initialize(self, None)
return s.field_to_native(obj, None)
def restore_fields(self, data, files):
content_object = None
if 'content_object' in data:
request = self.context.get('request')
content_object = get_object_from_url(request.DATA['content_object'])
attrs = super(FinancialTransactionSerializer, self).restore_fields(data, files)
if content_object:
attrs['content_object'] = content_object
return attrs
class Meta:
model = FinancialTransaction
fields = ('id', 'value', 'date', 'memo', 'banking_account', 'content_object')
def get_model_from_url(url: str):
return resolve(urlparse(url).path).func.cls.model
def get_object_from_url(url: str):
model = get_model_from_url(url)
pk = resolve(urlparse(url).path).kwargs.get('pk')
if not pk:
return None
return model.objects.get(pk=pk)
此设置序列化对象,以便content_object
字段包含指向相关对象的超链接,并在使用此序列化程序POST到视图时,并且数据包含content_object
键,我们得到相关的对象并传递它。
在restore_fields
方法中使用了从restore_object
返回的attrs,由于我们查找了内容对象并将其放在了attrs中,restore_object
设置了content_object
属性将FinancialTransaction对象发送到检索到的对象,然后Django负责其余部分。
到目前为止,我唯一能看到的缺点是,这并没有将content_object
字段添加到可浏览的API中......但我不确定它是如何工作的因为相关对象通常是在选择中提供的,我不认为我们想要在数据库中填充每个对象的选择。
答案 1 :(得分:0)
您可以在此DOC中了解更多信息:Marking extra actions for routing,如果您需要为POST
请求指定路由,请参阅其代码示例:
from django.contrib.auth.models import User
from rest_framework import status
from rest_framework import viewsets
# See these imports
from rest_framework.decorators import detail_route, list_route
from rest_framework.response import Response
from myapp.serializers import UserSerializer, PasswordSerializer
class UserViewSet(viewsets.ModelViewSet):
"""
A viewset that provides the standard actions
"""
queryset = User.objects.all()
serializer_class = UserSerializer
# just place your logical def with the existing decorator, like so:
@detail_route(methods=['post'])
def set_password(self, request, pk=None):
user = self.get_object()
serializer = PasswordSerializer(data=request.DATA)
if serializer.is_valid():
user.set_password(serializer.data['password'])
user.save()
return Response({'status': 'password set'})
else:
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
我希望这会有所帮助。