Python Queryset返回响应自定义
我是Django restframework的新手,我现在尝试的是用foreignkey返回对象Python Queryset返回响应自定义,python,django,django-rest-framework,Python,Django,Django Rest Framework,我是Django restframework的新手,我现在尝试的是用foreignkey返回对象 class User(models.Model): name = models.CharField(max_length=255,blank=True) date_created = models.DateTimeField(auto_now_add=True) date_modiefied = models.DateTimeField(auto_now=True)
class User(models.Model):
name = models.CharField(max_length=255,blank=True)
date_created = models.DateTimeField(auto_now_add=True)
date_modiefied = models.DateTimeField(auto_now=True)
area = models.CharField(max_length=255,blank=True)
uuid = models.CharField(max_length=255)
home = models.CharField(max_length=255,blank=True)
work = models.CharField(max_length=255,blank=True)
mobileNo = models.CharField(max_length=255,blank=True)
email = models.CharField(max_length=255,blank=True)
appVersionCode = models.CharField(max_length=255,blank=True)
photoUrl = models.CharField(max_length=255,blank=True)
serverTime = models.CharField(max_length=255,blank=True)
fcmTokenId = models.CharField(max_length=255,blank=True)
def __str__(self):
return self.name
class LocationData(models.Model):
user = models.ForeignKey(
User, related_name='user', on_delete=models.DO_NOTHING)
source_id = models.CharField(max_length=255)
latitude = models.CharField(max_length=255)
longitude = models.CharField(max_length=255)
speed = models.CharField(max_length=255)
kms = models.CharField(max_length=255)
date_created = models.DateTimeField(auto_now=True)
date_modiefied = models.DateTimeField(auto
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
class LocationDataSerializer(serializers.ModelSerializer):
class Meta:
model = LocationData
fields = '__all__'
depth = 1
我正在使用def get_querysetself:
class SyncIndexLastDataViewSet(viewsets.ModelViewSet):
serializer_class = LocationDataSerializer
def get_queryset(self):
userid = self.request.query_params.get('user_id', None)
userExist = User.objects.filter(id=userid)
if userExist.exists():
# call the original 'list' to get the original response
queryset = LocationData.objects.values('source_id').filter(user__id=userid).order_by('-source_id')[:1]
lastSourceId = queryset[0]['source_id']
response = {"collection": {"data": lastSourceId,"statusCode": status.HTTP_200_OK,"version":"1.0"}}
json = JSONRenderer().render(response)
# customize the response data
if response is not None:
return json
else:
# return response with this custom representation
response = {"collection": {"data": "","statusCode":status.HTTP_404_NOT_FOUND,"version":"1.0","error":"Not found"}}
return response
现在结果在下面的响应中,它立即抛出这个错误
但我希望查询集返回如下,因此我可以在android中读取这些密钥对值
{ "collection": {
"data": {
"id": 31,
"source_id": "55",
"latitude": "24654",
"longitude": "454654",
"date_created": "2019-02-08T17:10:09.318644Z",
"date_modiefied": "2019-02-08T17:10:09.318714Z",
"area": "54546",
"user": {
"id": 1,
"name": "Dormy",
"date_created": "1992-01-18T03:29:53.388000Z",
"date_modiefied": "2018-02-19T05:17:00.164000Z",
"serverTime": "",
"fcmTokenId": ""
}
},
"statusCode": 200,
"version": "1.0"
}
现在错误抛出
AttributeError:尝试获取序列化程序LocationDataSerializer上的字段源\u id的值时获取AttributeError。
序列化程序字段的名称可能不正确,并且与int实例上的任何属性或键都不匹配。
原始异常文本为:“int”对象没有属性“source\u id”
谢谢 这一问题的答案取决于您使用的视图类型,但底线是您不在get_queryset中执行此操作,而是在reguest类型的方法中执行此操作 例如,如果您使用的是,您应该覆盖类似的检索方法: 类MyAPIViewRetrieveAPIView: queryset=MyModel.objects.all 序列化程序\u class=MySerializer def retrieveself,request,*args,**kwargs: instance=self.get\u对象 serializer=self.get\u serializer 数据={ 收藏:{ 数据:serializer.data }, 状态代码:200, 版本:1.0 } 返回响应数据 如果您使用的是ListAPIView之类的其他对象,那么您希望在相关方法中看到它使用了什么,并覆盖它以包装数据
这里要认识到的主要问题是,它与获取queryset无关,而queryset只是从数据库获取数据。这是关于在发回响应时将数据转换为正确的格式。因此,工作应该在做出响应时完成。这一问题的答案取决于您使用的是什么类型的视图,但底线是您不在“获取查询”中执行此操作,而是在reguest类型的方法中执行此操作 例如,如果您使用的是,您应该覆盖类似的检索方法: 类MyAPIViewRetrieveAPIView: queryset=MyModel.objects.all 序列化程序\u class=MySerializer def retrieveself,request,*args,**kwargs: instance=self.get\u对象 serializer=self.get\u serializer 数据={ 收藏:{ 数据:serializer.data }, 状态代码:200, 版本:1.0 } 返回响应数据 如果您使用的是ListAPIView之类的其他对象,那么您希望在相关方法中看到它使用了什么,并覆盖它以包装数据
这里要认识到的主要问题是,它与获取queryset无关,而queryset只是从数据库获取数据。这是关于在发回响应时将数据转换为正确的格式。因此,应在做出响应时完成工作。对于此问题,有几种可能的解决方案。NDevox已经提到了如何覆盖我们的检索函数并得到我们期望的响应。但是,如果我们希望每个api端点的每个响应都能做到这一点,并且如果我们这样做,我们需要覆盖每个函数,那么它的负担和枯燥,我们应该尽可能避免这种情况。引入中间件或覆盖响应的一种可能方式,这样我们就可以在不显式覆盖每个功能的情况下获得每个api端点的通用响应 可能的解决方案一 当我们在这里使用DRF时,我们可以使用各种媒体类型添加我们自己的返回响应,比如application/json 首先,我们需要添加settings.py
REST_FRAMEWORK = {
...
'DEFAULT_RENDERER_CLASSES': (
'app_name.renderers.ApiRenderer', # our own render middleware
),
...
}
在我们的定制渲染中间件中
from rest_framework.renderers import BaseRenderer
from rest_framework.utils import json
class ApiRenderer(BaseRenderer):
def render(self, data, accepted_media_type=None, renderer_context=None):
our_response_dict = {
'version': '1.0'
'data': {},
'message': '',
}
if data.get('data'):
our_response_dict['data'] = data.get('data')
if data.get('status'):
our_response_dict['statusCode'] = data.get('status')
if data.get('message'):
our_response_dict['message'] = data.get('message')
data = our_response_dict
return json.dumps(data)
参考文献
可能的解决方案二
如果我们使用ModelViewset,那么还有另一种方法可以实现这一点。假设我们的观点如下
class A(serializer.ModelSerializer):
........
class B(serializer.ModelSerializer):
........
class C(serializer.ModelSerializer):
........
from collections import OrderedDict
class OurParentViewset(serializer.ModelSerializer):
......
def to_representation(self, instance):
data = super(serializers.ModelSerializer, self).to_representation(instance)
result = OrderedDict()
result['data'] = data
result['version'] = '1.0'
result['statusCode'] = '2xx' # i am not fully sure how to customize this
return result
class A(OurParentViewset):
........
class B(OurParentViewset):
........
class C(OurParentViewset):
........
我们的目标是覆盖ModelViewset的to_表示函数并返回自定义结果。这将如下所示
class A(serializer.ModelSerializer):
........
class B(serializer.ModelSerializer):
........
class C(serializer.ModelSerializer):
........
from collections import OrderedDict
class OurParentViewset(serializer.ModelSerializer):
......
def to_representation(self, instance):
data = super(serializers.ModelSerializer, self).to_representation(instance)
result = OrderedDict()
result['data'] = data
result['version'] = '1.0'
result['statusCode'] = '2xx' # i am not fully sure how to customize this
return result
class A(OurParentViewset):
........
class B(OurParentViewset):
........
class C(OurParentViewset):
........
这个问题有几种可能的解决方案。NDevox已经提到了如何覆盖我们的检索函数并得到我们期望的响应。但是,如果我们希望每个api端点的每个响应都能做到这一点,并且如果我们这样做,我们需要覆盖每个函数,那么它的负担和枯燥,我们应该尽可能避免这种情况。引入中间件或覆盖响应的一种可能方式,这样我们就可以在不显式覆盖每个功能的情况下获得每个api端点的通用响应 可能的解决方案一 当我们在这里使用DRF时,我们可以使用各种媒体类型添加我们自己的返回响应,比如application/json 首先,我们需要添加settings.py
REST_FRAMEWORK = {
...
'DEFAULT_RENDERER_CLASSES': (
'app_name.renderers.ApiRenderer', # our own render middleware
),
...
}
在我们的定制渲染中间件中
from rest_framework.renderers import BaseRenderer
from rest_framework.utils import json
class ApiRenderer(BaseRenderer):
def render(self, data, accepted_media_type=None, renderer_context=None):
our_response_dict = {
'version': '1.0'
'data': {},
'message': '',
}
if data.get('data'):
our_response_dict['data'] = data.get('data')
if data.get('status'):
our_response_dict['statusCode'] = data.get('status')
if data.get('message'):
our_response_dict['message'] = data.get('message')
data = our_response_dict
return json.dumps(data)
参考文献
可能的解决方案二
如果我们使用的是ModelViewset
还有另一种方法可以实现这一目标。假设我们的观点如下
class A(serializer.ModelSerializer):
........
class B(serializer.ModelSerializer):
........
class C(serializer.ModelSerializer):
........
from collections import OrderedDict
class OurParentViewset(serializer.ModelSerializer):
......
def to_representation(self, instance):
data = super(serializers.ModelSerializer, self).to_representation(instance)
result = OrderedDict()
result['data'] = data
result['version'] = '1.0'
result['statusCode'] = '2xx' # i am not fully sure how to customize this
return result
class A(OurParentViewset):
........
class B(OurParentViewset):
........
class C(OurParentViewset):
........
我们的目标是覆盖ModelViewset的to_表示函数并返回自定义结果。这将如下所示
class A(serializer.ModelSerializer):
........
class B(serializer.ModelSerializer):
........
class C(serializer.ModelSerializer):
........
from collections import OrderedDict
class OurParentViewset(serializer.ModelSerializer):
......
def to_representation(self, instance):
data = super(serializers.ModelSerializer, self).to_representation(instance)
result = OrderedDict()
result['data'] = data
result['version'] = '1.0'
result['statusCode'] = '2xx' # i am not fully sure how to customize this
return result
class A(OurParentViewset):
........
class B(OurParentViewset):
........
class C(OurParentViewset):
........
在这里实现自定义渲染器似乎是一个不错的选择 您可以让来自android客户端的请求在Accept标头中包含一种向呈现程序标识客户端的方法。e、 g 然后使用JSONRenderer类编写一个渲染器,为Android客户端提供格式
# ./formatters/android_format.py
from rest_framework.renderers import JSONRenderer, BaseRenderer
from django.http.multipartparser import parse_header
class AndroidV1FormatRenderer(BaseRenderer):
media_type = 'application/json'
format = 'json'
json_renderer = JSONRenderer()
def android(self, accepted_media_type):
base_media_type, params = parse_header(accepted_media_type.encode('ascii'))
return 'android' in params
def render(self, data, accepted_media_type=None, renderer_context=None):
response = renderer_context['response']
android = self.android(accepted_media_type)
if android:
data = {
"collection": {"data": data},
"statusCode": response.status_code,
"version": "1.0"
}
return json_renderer.render(
wrapped_data, accepted_media_type, renderer_context)
然后,在需要使用APIView的renderer_classes属性以这种方式格式化响应的情况下,可以使用此选项 在这里实现自定义渲染器似乎是一个不错的选择 您可以让来自android客户端的请求在Accept标头中包含一种向呈现程序标识客户端的方法。e、 g 然后使用JSONRenderer类编写一个渲染器,为Android客户端提供格式
# ./formatters/android_format.py
from rest_framework.renderers import JSONRenderer, BaseRenderer
from django.http.multipartparser import parse_header
class AndroidV1FormatRenderer(BaseRenderer):
media_type = 'application/json'
format = 'json'
json_renderer = JSONRenderer()
def android(self, accepted_media_type):
base_media_type, params = parse_header(accepted_media_type.encode('ascii'))
return 'android' in params
def render(self, data, accepted_media_type=None, renderer_context=None):
response = renderer_context['response']
android = self.android(accepted_media_type)
if android:
data = {
"collection": {"data": data},
"statusCode": response.status_code,
"version": "1.0"
}
return json_renderer.render(
wrapped_data, accepted_media_type, renderer_context)
然后,在需要使用APIView的renderer_classes属性以这种方式格式化响应的情况下,可以使用此选项 因为get_queryset不允许您自定义响应数据。我决定接受对我来说很重要的查询值 ->更改为…api/users/1
因为get_queryset不允许您自定义响应数据。我决定接受对我来说很重要的查询值 ->更改为…api/users/1
你希望你所有的回应都是这样,还是仅仅是一种特殊类型的请求-回应?按照我上面提到的方式!您可以在查询中添加注释。使用它,您可以将自定义字段添加到query@Sarang你们有样品吗!请张贴它检查此链接。有许多功能可以与annotate一起使用。您希望所有的响应都是这种方式,还是只需要特殊类型的请求-响应?按照我上面提到的方式!您可以在查询中添加注释。使用它,您可以将自定义字段添加到query@Sarang你们有样品吗!请张贴它检查此链接。有许多功能可以与annotate一起使用。queryset=LocationData.objects.filteruser\uuuuID=userid.order\uBy'-source\uID'[:1]如何?我需要过滤查询集,并将数据提供给响应!您可以在get_queryset中进行筛选,也可以使用我在回答中提到的方法调用get_queryset并筛选它返回的值。举个例子就好了!,我找不到做那件事的线索!queryset=LocationData.objects.filteruser\uuu id=userid.order\u by'-source\u id'[:1]怎么样?我需要过滤查询集,并将数据提供给响应!您可以在get_queryset中进行筛选,也可以使用我在回答中提到的方法调用get_queryset并筛选它返回的值。举个例子就好了!,我找不到做那件事的线索@Tot先生,你不能仅仅改变任务集的定义/响应。当您使用ModelViewset时,请通过RetrieveModelMixin,在这里,您可以找到在ModelViewset的Retrieve下实际发生的情况,您可以看到在查询集传递到Serializer之后,当您使用ModelSerializer时,那里没有集合部分。@Tot sir您不能仅仅更改quest\u集的定义/响应。当您使用ModelViewset时,请通过RetrieveModelMixin,在这里您将发现在ModelViewset的Retrieve下实际发生的情况,您可以看到在查询集传递到序列化程序之后,当您使用ModelSerializer时,那里没有集合部分。因此有一些指针。1您应该更新您的docstring,目前它只是一个与方法无关的代码示例。2响应永远不能是无,正如您在上面定义的,所以如果响应不是无,则无是多余的。3您确实不应该将状态代码作为响应主体的一部分返回。您应该在响应中设置状态,如return-Responseresponse,status=404。是的,处理好所有声明的内容!。谢谢你的指点。1您应该更新您的docstring,目前它只是一个与方法无关的代码示例。2响应永远不能是无,正如您在上面定义的,所以如果响应不是无,则无是多余的。3您确实不应该将状态代码作为响应主体的一部分返回。您应该在响应中设置状态,如return-Responseresponse,status=404。是的,处理好所有声明的内容!。谢谢