[Python自学] restframework
Posted 风间悠香
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Python自学] restframework相关的知识,希望对你有一定的参考价值。
参考博客:https://www.cnblogs.com/yuanchenqi/articles/8719520.html
一、数据序列化的几种方式
在Django的视图函数中,我们从数据库中获取数据,由以下几种方式将其转化为JSON数据:
1.list强转方式
class PublishView(View): def get(self, request): publish_list = list(Publish.objects.all().values()) return HttpResponse(json.dumps(publish_list))
通过list强转的方式。
前台接收到的数据:
[{"id": 1, "name": "\\u6e05\\u534e\\u5927\\u5b66\\u51fa\\u7248\\u793e", "email": "qh@gmail.com"}, {"id": 2, "name": "\\u5de5\\u4e1a\\u51fa\\u7248\\u793e", "email": "gy@gmail.com"}, {"id": 3, "name": "\\u90ae\\u7535\\u51fa\\u7248\\u793e", "email": "yd@gmail.com"}, {"id": 4, "name": "\\u56db\\u5ddd\\u6587\\u5b66\\u51fa\\u7248\\u793e", "email": "scwx@gmail.com"}]
2.手动封装方式
class PublishView(View): def get(self, request): publish_list = Publish.objects.all() temp = [] for obj in publish_list: temp.append({ \'id\': obj.id, \'name\': obj.name, \'email\': obj.email }) return HttpResponse(json.dumps(temp))
通过字段拼接的方式。
使用model_to_dict方法来将对象转换成字典:
class PublishView(View): def get(self, request): from django.forms.models import model_to_dict publish_list = Publish.objects.all() temp = [] for obj in publish_list: temp.append(model_to_dict(obj)) return HttpResponse(json.dumps(temp))
前台接收到的数据:
[{"id": 1, "name": "\\u6e05\\u534e\\u5927\\u5b66\\u51fa\\u7248\\u793e", "email": "qh@gmail.com"}, {"id": 2, "name": "\\u5de5\\u4e1a\\u51fa\\u7248\\u793e", "email": "gy@gmail.com"}, {"id": 3, "name": "\\u90ae\\u7535\\u51fa\\u7248\\u793e", "email": "yd@gmail.com"}, {"id": 4, "name": "\\u56db\\u5ddd\\u6587\\u5b66\\u51fa\\u7248\\u793e", "email": "scwx@gmail.com"}]
3.Django提供的序列化
利用django中的序列化组件:·
class PublishView(View): def get(self, request): from django.core import serializers publish_list = Publish.objects.all() ret = serializers.serialize("json", publish_list) return HttpResponse(ret)
前台接收到的数据:
[{"model": "demo.publish", "pk": 1, "fields": {"name": "\\u6e05\\u534e\\u5927\\u5b66\\u51fa\\u7248\\u793e", "email": "qh@gmail.com"}}, {"model": "demo.publish", "pk": 2, "fields": {"name": "\\u5de5\\u4e1a\\u51fa\\u7248\\u793e", "email": "gy@gmail.com"}}, {"model": "demo.publish", "pk": 3, "fields": {"name": "\\u90ae\\u7535\\u51fa\\u7248\\u793e", "email": "yd@gmail.com"}}, {"model": "demo.publish", "pk": 4, "fields": {"name": "\\u56db\\u5ddd\\u6587\\u5b66\\u51fa\\u7248\\u793e", "email": "scwx@gmail.com"}}]
4.restframework提供的序列化
利用restframework中的serializer:
from rest_framework import serializers class PublishSerializers(serializers.Serializer): name = serializers.CharField() email = serializers.EmailField() class PublishView(View): def get(self, request): publish_list = Publish.objects.all() # 如果序列化queryset,则需要参数many=True ps = PublishSerializers(publish_list, many=True) # 如果是queryset其中一个obj,则不需要many参数 # ps = PublishSerializers(obj) return HttpResponse(ps.data)
这种方式的前提是安装djangorestframework。
前端接收到的数据:
OrderedDict([(\'name\', \'清华大学出版社\'), (\'email\', \'qh@gmail.com\')])OrderedDict([(\'name\', \'工业出版社\'), (\'email\', \'gy@gmail.com\')])OrderedDict([(\'name\', \'邮电出版社\'), (\'email\', \'yd@gmail.com\')])OrderedDict([(\'name\', \'四川文学出版社\'), (\'email\', \'scwx@gmail.com\')])
数据是多个有序字典组成的列表。
二、安装restframe
pip install djangorestframework
三、POST请求的Content-Type
当我们使用postman发送POST请求时,选择form-data或x-www-form-urlencoded内容类型发送:
对应请求头中的Content-Type为application/x-www-form-urlencoded。
Django收到请求后,会将body中的数据a=1&b=2转化为request.POST字典。
<QueryDict: {\'a\': [\'11\'], \'b\': [\'2\']}> # django自动帮我们将body中的数据转换为字典
但是,如果内容类型选择raw-->Json发送:
对应请求头中的Content-Type为application/json。
Django收到请求后,不会自动将其转换为request.POST。
<QueryDict: {}> # 拿到的request.POST是空的
这时,我们通过request.body可以拿到原始的body数据:
b\'{"a":"1","b":"2"}\'
四、restframe中的APIView
1.视图函数继承APIView
在我们写视图函数的时候,原来我们继承的事View类,而要使用restframework的话,我们需要继承APIView类:
from rest_framework.views import APIView class PublishView(APIView): def get(self, request): pass def post(self, request): pass
2.APIView和View之间的区别
我们看以下APIView的源码:
class APIView(View): # The following policies may be set at either globally, or per-view. renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES parser_classes = api_settings.DEFAULT_PARSER_CLASSES authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS metadata_class = api_settings.DEFAULT_METADATA_CLASS versioning_class = api_settings.DEFAULT_VERSIONING_CLASS # Allow dependency injection of other settings to make testing easier. settings = api_settings schema = DefaultSchema() @classmethod def as_view(cls, **initkwargs): """ Store the original class on the view function. This allows us to discover information about the view when we do URL reverse lookups. Used for breadcrumb generation. """ if isinstance(getattr(cls, \'queryset\', None), models.query.QuerySet): def force_evaluation(): raise RuntimeError( \'Do not evaluate the `.queryset` attribute directly, \' \'as the result will be cached and reused between requests. \' \'Use `.all()` or call `.get_queryset()` instead.\' ) cls.queryset._fetch_all = force_evaluation view = super().as_view(**initkwargs) view.cls = cls view.initkwargs = initkwargs # Note: session based authentication is explicitly CSRF validated, # all other authentication is CSRF exempt. return csrf_exempt(view)
我们可以看到,APIView实际上是继承自View类。并且做了一些扩展。
APIView也有as_view()方法,其中做了一些判断后,然后调用了父类(View)中的as_view()方法,并且得到返回的view函数引用,最后将这个view函数引用进行了返回(并且去除了csrf)。
但是不同的是,虽然是父类的as_view()方法返回的view函数引用,但是view中调用dispatch函数就不应该是调用的父类的dispatch,而应该是APIView的dispatch()方法。
我们找到APIView类中的dispatch()方法:
def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django\'s regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs # 首先把request使用initialize_request方法封装成了一个Request类的对象。 request = self.initialize_request(request, *args, **kwargs) # 将封装后的request替换到self.request中,以后使用的都是Request类的对象,里面封装了原生的request # 如果要获取原生request,在这里使用request._request。 self.request = request self.headers = self.default_response_headers # deprecate? try: self.initial(request, *args, **kwargs) # 判断请求类型,并且返回对应的视图函数,例如get()和post(),并赋值给handler if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed # 运行请求对应的视图函数,得到response response = handler(request, *args, **kwargs) except Exception as exc: response = self.handle_exception(exc) # 在finalize_response方法中,针对response做一些处理 self.response = self.finalize_response(request, response, *args, **kwargs) # 返回给客户端 return self.response
3.使用restframework中的request
既然restframe中的APIView将request进行了封装,得到一个新的request(Request对象),那么我们如何使用:
# 使用APIView class PublishView(APIView): def get(self, request): pass def post(self, request): # 使用新的request获取body数据 print(request.data) print(type(request.data)) return HttpResponse("OK")
使用postman发POST请求,数据类型为JSON,数据为{"a":"1","b":"2"}:
后台打印结果:
{\'a\': \'1\', \'b\': \'2\'} <class \'dict\'>
说明restframe帮我们将JSON转换为了字典。这是Django原生View没有提供的功能。
注意:application/json数据只能使用request.data来取,而application/x-www-form-urlencoded数据可以使用request.POST和request.data来获取。
同样的,在get()中也可以通过request.data获取数据:
# 使用APIView class PublishView(APIView): def get(self, request): print(request.data) print(type(request.data)) return HttpResponse("get...") def post(self, request): pass
打印结果:
{} <class \'dict\'>
可以看到,get请求附带在url中的数据,新的request.data并没有值,也就是说restframe的request只帮我们处理了POST请求的值。
如果我们要获取GET请求的值,我们可以使用:
# 使用APIView class PublishView(APIView): def get(self, request): print(request._request.GET) # 通过新request中封装的原生request获取GET print(request.GET) # 新的request也帮我们封装了一样的GET return HttpResponse("get...") def post(self, request): pass
五、通过Response返回值
1.使用Response返回值
from rest_framework import serializers from rest_framework.views import APIView from rest_framework.response import Response from .models import Book, Author class BookSerializers(serializers.Serializer): title = serializers.CharField() price = serializers.IntegerField() pub_date = serializers.DateTimeField() # publish = serializers.ForeignKey("Publish", on_delete=models.CASCADE) # authors = serializers.ManyToManyField("Author") class BookView(APIView): def get(self, request): book_list = Book.objects.all() bs = BookSerializers(book_list, many=True) return Response(bs.data) def post(self, request): pass
这里返回值,我们没使用HttpResponse,而是使用的restframe提供的Response类。
Response类继承于HttpResponse类。其中扩展了一些功能。
使用postman发GET请求,看返回结果:
[ { "title": "Python3标准库", "price": 99, "pub_date": null }, { "title": "C标准库", "price": 55, "pub_date": null }, { "title": "机器学习", "price": 45, "pub_date": null }, { "title": "深度学习指南", "price": 67, "pub_date": null }, { "title": "意志力", "price": 33, "pub_date": null }, { "title": "股市抄手", "price": 23, "pub_date": null } ]
Response帮我们将返回的JSON数据进行了格式化。
如果用浏览器请求,可以看到页面:
六、处理一对多和多对多数据
1.一对多和多对多的处理
前面小节所描述的都是表中的简单字段,而没有包含外键和多对多的字段,下面就说明一下一对多和多对多字段的处理方法。
class BookSerializers(serializers.Serializer): title = serializers.CharField() price = serializers.IntegerField() pub_date = serializers.DateTimeField() publish = serializers.CharField(source="publish.name") # 如果想显示email,则改为source="publish.email" authors = serializers.SerializerMethodField() # 多对多字段,使用这个函数配置后面定义的方法来自定义数据 # 定义一个方法,方法名必须是get_authors,SerializerMethodField()会调用get_authors来获取返回值,并赋值给authors def get_authors(self, obj): temp = [] for obj in obj.authors.all(): # 将authors中该book对应的所有作者名字加入列表,并返回 temp.append(obj.name) return temp class BookView(APIView): def get(self, request): book_list = Book.objects.all() bs = BookSerializers(book_list, many=True) return Response(bs.data) def post(self, request): pass
在一对多字段(外键)中,使用source参数来指定想获取的外表字段。
在多对多字段中,使用 SerializerMethodField() 并配合自定义处理函数,来定义数据怎么组织。
Postman发GET请求,返回结果:
[ { "title": "Python3标准库", "price": 99, "pub_date": null, "publish": "工业出版社", "authors": [ "leo", "alex" ] }, { "title": "C标准库", "price": 55, "pub_date": null, "publish": "清华大学出版社", "authors": [ "alex", "Lucy" ] }, { "title": "机器学习", "price": 45, "pub_date": null, "publish": "工业出版社", "authors": [ "leo" ] }, { "title": "深度学习指南", "price": 67, "pub_date": null, "publish": "邮电出版社", "authors": [ "leo", "Lucy" ] }, { "title": "意志力", "price": 33, "pub_date": null, "publish": "四川文学出版社", "authors": [ "Jone", "Lucy" ] }, { "title": "股市抄手", "price": 23, "pub_date": null, "publish": "邮电出版社", "authors": [ "alex", "Jone" ] } ]
可以看到,所有的作者名都以列表的形式放在返回数据中了。
2.一对多的另外一种处理方式
既然多对多可以使用SerializerMethodField() 配合自定义处理函数来获取所有的值。那么一对多的情况,我们也可以使用这种方式来处理:
class BookSerializers(serializers.Serializer): title = serializers.CharField() price = serializers.IntegerField() pub_date = serializers.DateTimeField() publish = serializers.SerializerMethodField() def get_publish(self, obj): temp = {"name": obj.publish.name, "email": obj.publish.email} return temp authors = serializers.SerializerMethodField() # 多对多字段,使用这个函数配置后面定义的方法来自定义数据 # 定义一个方法,方法名必须是get_authors,SerializerMethodField()会调用get_authors来获取返回值,并赋值给authors def get_authors(self, obj): temp = [] for obj in obj.authors.all(): # 将authors中该book对应的所有作者名字加入列表,并返回 temp.append(obj.name) return temp class BookView(APIView): def get(self, request): book_list = Book.objects.all()以上是关于[Python自学] restframework的主要内容,如果未能解决你的问题,请参考以下文章
[Python自学] restframework (解析器)