Python Queryset返回响应自定义

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)

我是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)
    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。是的,处理好所有声明的内容!。谢谢