0


自学Python第二十二天- Django框架(四) 其他组件和工具:富文本、RESTful、邮件、单元测试

Django官方文档

富文本方案

django-mdeditor

DjangoUeditor

django2集成DjangoUeditor富文本编辑器

django-RESTful

REST 即表述性状态传递(Representational State Transfer),它是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。REST通常基于使用HTTP,URI,和XML以及HTML这些现有的广泛流行的协议和标准。

RESTful API 设计规范
官方网站
中文文档

RESTful API四大基本原则:

  • 为每个资源设置URI
  • 通过XML / JSON进行数据传递
  • 无状态连接,服务器端不应保存过多上下文状态,即每个请求都是独立的
  • 使用HTTP动词:GET POST PUT DELETE

安装和环境配置

django-RESTful 需要以下包支持(除了主插件程序包外,其他的包为可选项)

  • DjangoRESTframework - 主插件程序包
  • PyYAML, uritemplate (5.1+, 3.0.0+) - Schema生成支持。
  • Markdown (3.0.0+) - 为browsable API 提供Markdown支持。
  • Pygments (2.4.0+) - 为Markdown处理提供语法高亮。
  • django-filter (1.0.1+) - Filtering支持。
  • django-guardian (1.1.1+) - 对象级别的权限支持。

可以根据需要安装相应的包

pip install djangorestframework
pip install markdown       # 为browsable API 提供Markdown支持。
pip install django-filter  # Filtering支持。

使用 django-RESTful 需要注册应用到 settings.py 的 INSTALLED_APPS 中

INSTALLED_APPS =[...'rest_framework',]

REST framework API的所有全局设定都会放在一个叫REST_FRAMEWORK的配置词典里,并添加到 settings.py 中:

REST_FRAMEWORK ={# 在这里配置访问许可'DEFAULT_PERMISSION_CLASSES':[# 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'   # 匿名只读,登录用户可用'rest_framework.permissions.IsAdminUser',# 只能管理员使用],# 配置分页器'DEFAULT_PAGINATION_CLASS':'rest_framework.pagination.PageNumberPagination','PAGE_SIZE':10,}

如果打算用browsable API(一个可视化API测试工具),可能也会用REST framework的登录注销视图。可以添加路由到根目录的urls.py文件

urlpatterns =[...
    path('api-auth/', include('rest_framework.urls'))# 路由路径可以更改]

测试安装和配置

以一个实例进行测试:创建一个读写API来访问项目的用户信息。

在根目录的 urls.py 中创建API

# urls.py from django.urls import path, include
from django.contrib.auth.models import User
from rest_framework import routers, serializers, viewsets

# 序列化器是用来定义API的表示形式。classUserSerializer(serializers.HyperlinkedModelSerializer):classMeta:
        model = User
        fields =['url','username','email','is_staff']# ViewSets定义视图的行为。classUserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

# 路由器提供一个简单自动的方法来决定URL的配置。
router = routers.DefaultRouter()
router.register(r'users', UserViewSet)# 注册路由# 通过URL自动路由来给我们的API布局。# 此外,我们还要把登录的URL包含进来。
urlpatterns =[
    path('', include(router.urls)),# 注册 REST 路由到主路由
    path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))]

现在可以在浏览器中打开(默认页,即第一条路由)http://127.0.0.1:8000/,查看 ‘user’ API了。如果使用了右上角的登录控制,还可以在系统中添加、创建并删除用户。这样就是安装和配置成功了。

基本用法

在实际项目中,django-RESTful 的使用分为三个步骤:

  1. 创建序列化器:及定义API的表现形式
  2. 创建视图:用来处理api请求并返回响应
  3. 注册路由:将视图注册到路由中

创建序列化器

定义序列化程序,可以创建一个新的文件,这里使用 serializers.py

from django.contrib.auth.models import User
from rest_framework import serializers

# 定义序列化处理器,继承自 serializers.HyperlinkedModelSerializer,使用超链接关系classUserSerializer(serializers.ModelSerializer):classMeta:# model 定义使用的数模型
        model = User
        # fields 定义使用数据模型的哪些字段
        fields =('url','username','email','groups')

django-RESTful 提供了3个序列化类的模板: Serializer、ModelSerializer、HyperlinkedModelSerializer

  • Serializer : 标准序列化类。需要完全自定义声明相关字段,且需实现 create(**validate_data)update(instance, **validate_data) 两个函数
  • ModelSerializer : Serialize 的子类,使用数据模型的序列化类。通过声明 Meta 类来指定相关属性(使用的模型类和需要序列化的字段)
  • HyperlinkedModelSerializer : ModelSerializer 的子类,

创建视图

django-RESTful 提供了一些预设的视图处理类

from rest_framework import viewsets
from django.contrib.auth.models import User
from.serializers import UserSerializer

# 视图处理类classUserViewSet(viewsets.ModelViewSet):# queryset 为查询的结果集合
    queryset = User.objects.all()# serializer_classs 使用的序列化器
    serializer_class = UserSerializer

创建路由

可以在应用中创建路由,并注册到主路由中

# 应用中的 urls.pyfrom django.urls import path, include
from rest_framework import routers

# 路由器提供一个简单自动的方法来决定URL的配置。# 创建路由器
router = routers.DefaultRouter()# 在路由器中注册路由及处理器(FBV或CBV)
router.register(r'users', UserViewSet)# 通过URL自动路由来给我们的API布局。
urlpatterns =[
    path('', include(router.urls)),# 将 REST 路由器中的路由添加到 app 的路由中
    path('api-auth/', include('rest_framework.urls')),# 添加 browsable API 的登录路由]

自定义 API 相关

根据实际情况自定义序列化器或者使用自定义的视图函数,有一些需要使用到的

序列化与反序列化

在自定义的视图中,需要将数据通过序列化器序列化后发出响应,接收到的请求也需要通过反序列化获取具体数据

from.serializers import UserSerializer
from django.contrib.auth.models import User

s1 = UserSerializer(User.objects.get(pk=1))# 序列化单条数据
s2 = UserSerializer(User.objects.all(),    many=True)# 序列化多条数据# 渲染成Json字符串from rest_framework.renderers import JSONRenderer
content = JSONRenderer().render(s1.data)# 对已经渲染的Json字符串反序列化为Json对象
data = JSONParser().parse(io.BytesIO(content))# 或直接解析 request 对象(POST)# from rest_framework.parsers import JSONParser# data = JSONParser().parse(request)# serializer = UserSerializer(data=data)    # 根据提交的参数获取序列化对象# if serizlizer.is_valid():        # 通过验证#     serializer.save()        # 保存数据

关联关系的序列化

在查看和测试API的过程中,会发现某些数据表的外键指向的是关联表的数据链接,而不是数据内容。如果要使用数据内容,就需要将关联关系序列化。在定义序列化器时,将外键字段声明成为字符串关系字段即可。

from django.contrib.auth.models import User
from rest_framework import serializers

# 定义序列化处理器,继承自 serializers.HyperlinkedModelSerializer,使用超链接关系classUserSerializer(serializers.HyperlinkedModelSerializer):# 在此定义字段# 定义关系字段,将数据信息字符串化
    groups = serializers.StringRelatedField()# 如果是对多关系(对方表是多端)则需要参数 many=True# 也可以将关系对象整体序列化(即返回的 json 中,此字段是给包含了所有数据的 json 对象)# groups = GroupSerializer()    # 需先定义 GroupSerializer 序列化器,且如果对方是多端也需要 many=TrueclassMeta:# model 定义使用的数模型
        model = User
        # fields 定义使用数据模型的哪些字段
        fields =('url','username','email','groups')

这个方法在一对一、一对多、多对多关系都适用。如果对方是多端(即此表的该外键字段或反查字段有多个数据),添加参数 many=True 即可。其中一些关联字段类型为

  • StringRelatedField : 获取关联模型对象字符串化(使用 str() 函数返回的字符串)
  • PrimarykeyRelatedField : 获取关联模型对象的主键
  • SlugRelatedField : 获取关联模型对象指定字段(slug_field 参数值)
  • HyperlinkedRelatedField : 获取关联模型对象的查询api链接,此种方式为默认方式

自定义视图处理器

使用的封装好的django-RESTful视图处理类能够方便的获取请求返回响应,但是如果需要使用自定义的视图处理器,则可使用

@api_view

装饰器(FBV)或

APIView

基类(CBV)。

FBV 方式举例,需注意的是注册路由使用 path 方法直接到 urlpatterns

from rest_framework.decorators import api_view
from.serializers import UserSerializer
from rest_framework.response import Response
from rest_framework.parsers import JSONParser
from django.contrib.auth.models import User
from django.http import JsonResponse

@api_view(['GET','POST'])defuser_func(request, pk=None):if request.method =='GET':ifnot pk:
            queryset = User.objects.all()
            serializer = UserSerializer(queryset, many=True, context={'request': request})else:
            queryset = User.objects.get(pk=pk)
            serializer = UserSerializer(queryset, context={'request': request})# django-RESTful 重新封装了 Response,可以直接序列化# 可以根据请求类型返回数据,例如浏览器直接请求会返回管理页面,请求 json 格式则返回 json 数据# 也可以在请求地址后添加参数 ?format=json 指定获取 json 数据或 api 页面return Response(serializer.data)# return JsonResponse(serializer.data)        # 返回的是序列化后的json字符串elif request.method =='POST':# 接收的 request 是 django-RESTful 重新封装后的 request 对象# 可以直接将其内容反序列化,然后通过序列化器从数据库中查询相关数据,再进行序列化
        data = JSONParser().parse(request)
        serializer = UserSerializer(data, context={'request': request})# 序列化器对象可以进行验证if serializer.is_valid():
            serializer.save()# 保存至数据库return Response(serializer.data, status=201)else:return Response(serializer.errors, status=400)

CBV 方式举例,需注意的是注册路由使用 path 方法直接到 urlpatterns

from rest_framework.views import APIView
from.serializers import UserSerializer
from django.contrib.auth.models import User
from rest_framework.response import Response
from rest_framework.parsers import JSONParser

classUserClass(APIView):defget(self, request, pk=None):ifnot pk:
            queryset = User.objects.all()
            serializer = UserSerializer(queryset, many=True, context={'request': request})else:
            queryset = User.objects.get(pk=pk)
            serializer = UserSerializer(queryset, context={'request': request})return Response(serializer.data)defpost(self,request):
        data = JSONParser().parse(request)
        serializer = UserSerializer(data, context={'request': request})# 序列化器对象可以进行验证if serializer.is_valid():
            serializer.save()# 保存至数据库return Response(serializer.data, status=201)else:return Response(serializer.errors, status=400)

获取 request 的数据

rest_framework 的 request 和 django 的 request 不太一样,获取数据是使用

request.data

,类似于 django 的

request.body

但是不是字节码,而是字符串。

name = request.data.get('name',None)

关于授权认证

默认情况下, APIView 中的相关接口方法不验证权限(授权),对资源并不安全,所以需要增加验证。

首先在 settings.py 中的 REST_FRAMEWORK 字段配置权限访问许可

# settings.py

REST_FRAMEWORK ={'DEFAULT_AUTHENTICATION_CLASSES':[# 默认使用的授权认证'rest_framework.authentication.BasicAuthentication',# 基本授权认证'rest_framework.authentication.SessionAuthentication',# 基于session的授权认证]}

然后可以在视图中指定验证方式和许可类型

classEcampleView(APIView):
    authentication_classes =(SessionAuthentication, BasicAuthentication)# 验证方式
    permission_classes =(IsAuthenticated,)# 许可类型defget(self, request):pass

但是这种授权是基于 session 登录的,即 auth 模块的认证。在 api 接口中,通常会使用 token 进行认证。

django-RESTful 使用的 token 验证

TokenAuthentication 提供了简单的基于 Token 的HTTP认证方案,适用于客户端 - 服务器设置,如本地桌面和移动客户端。要在 django-RESTful 中使用 TokenAuthentication,需要配置认证类包含 TokenAuthentication,另外需要注册 rest_framework.authtoken 这个 app。需注意的是,确保在修改设置后运行一下

manage.py migrate

,因为 rest_framework.authtoken 会提交一些数据库迁移操作。

# settings.py
INSTALLED_APPS =[...'rest_framework.authtoken',]
REST_FRAMEWORK ={'DEFAULT_AUTHENTICATION_CLASSES':[# 默认使用的授权认证'rest_framework.authentication.TokenAuthentication',# token授权认证]}

创建令牌

from rest_framework.authtoken.models import Token
from django.contrib.auth.models import User

user = User.objects.get(pk=1)# 获取用户
token = Token.objects.create(user=user)# 根据用户创建 token 实例print(token.key)# token.key 就是需要验证的字段

通常会在每个用户创建时,创建对应的 token,可以捕捉用户的 post_save 信号

from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token

@receiver(post_save, sender=settings.AUTH_USER_MODEL)defcreate_auth_token(sender, instance=None, created=False,**kwargs):if created:
        Token.objects.create(user=instance)

也可以为现有用户生成令牌

from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token

for user in User.objects.all():
    Token.objects.get_or_create(user=user)# 此方法可以返回2个值,分别为 token 对象和 created

验证令牌

对客户端进行身份验证,token需要包含在名为 Authorization 的HTTP头中。密钥应该是以字符串"Token"为前缀,以空格分割的两个字符串。例如:

functionajax_get(){fetch('/api/user/,{headers:{'Authorization':'Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b'}}).then(response=>response.json()).then(data=>{
            console.log(data)})}

如果认证成功,TokenAuthentication 提供以下认证信息:

  • request.user 将是一个Django User 实例。
  • request.auth 将是一个rest_framework.authtoken.models.Token 实例。

那些被拒绝的未经身份验证的请求会返回使用适当WWW-Authenticate标头的HTTP 401 Unauthorized响应。例如:

WWW-Authenticate: Token

通过暴露 api 端点获取令牌

当使用TokenAuthentication时,可能希望为客户端提供一个获取给定用户名和密码的令牌的机制。 REST framework 提供了一个内置的视图来提供这个功能。要使用它,需要将 obtain_auth_token 视图添加到你的URLconf:

from rest_framework.authtoken import views
urlpatterns +=[
    path('api-token-auth/', views.obtain_auth_token)# url 路由可以自定义]

当使用form表单或JSON将有效的username和password字段POST提交到视图时,obtain_auth_token 视图将返回JSON响应:

{'token':'9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b'}

请注意,默认的obtain_auth_token视图显式使用JSON请求和响应,而不是使用settings中配置的默认渲染器和解析器类。如果需要自定义版本的obtain_auth_token视图,可以通过重写ObtainAuthToken类,并在url conf中使用它来实现。默认情况下,没有权限或限制应用于obtain_auth_token视图。如果你希望应用限制,则需要重写视图类,并使用throttle_classes属性包含它们。

邮件

django 有组件支持发送邮件

配置邮件信息

发送邮件需要配置邮件服务器信息

EMAIL_HOST ='smtp.163.com'
EMAIL_PORT =25
EMAIL_HOST_USER ='[email protected]'
EMAIL_HOST_PASSWORD ='emailPassword'

需注意的是 EMAIL_HOST_PASSWORD 使用的是邮件服务器的授权码而不是登录密码。

发送邮件的方法

django 使用

django.core.mail.send_mail()

发送邮件,其用法为

from django.core.mail import send_mail

send_mail(title, message, from_email, recipient_list, fail_silently, auth_user, auth_password, connection, html_message)
  • title : 邮件标题,字符串
  • message : 邮件正文,字符串,必须有此参数,哪怕是空字符串。
  • from_mail : 发送者,字符串
  • recipient_list : 接收者,字符串列表,可以有多个接收者
  • fail_silently :
  • auth_user :
  • auth_password :
  • connection :
  • html_message : 正文中的 html 文本,可以代替 message 参数,接受 html 内容

单元测试

可以使用 unittest 库进行单元测试。创建测试类,继承自 unittest 库中的相应类,然后创建方法。方法可以独立执行,用作测试使用。单元测试的方便之处在于可以直接调用已经写好的代码,或测试写好的代码。注意单元测试的方法名以test为开头,如果不以test开头则不会进行测试。

from django.test import TestCase
import unittest

classUserTestCase(TestCase):defsetUp(self):print('--执行测试前进行配置--')deftest_01_add(self):print('--add order--')deftest_02_get_info(self):print('--info--')deftearDown(self):print('--测试后进行释放资源等收尾工作--')if __name__ =='__main__':
    unittest.main()# 执行单个测试

执行顺序为:

  • setUp 用于初始化资源
  • 各自定义方法,不是按照声明顺序,而是按照 ASCII 排序,所以常以 test 加序号加业务名称来声明
  • tearDown 用于回收资源

单个套件

除了直接执行测试类,也可以创建套件执行

from unittest import TestSuite, TextTestRunner

defsuite():# 声明套件类
    suite_ = TestSuite()# 创建测试套件对象
    suite_.addTest(UserTestCase.test_01_add)# 添加套件的测试方法
    suite_.addTest(UserTestCase.test_02_get_info)return suite_

if __name__ =='__main__':
    runner = TextTestRunner()
    runner.run(suite())

多个套件

多个套件可以按照排序来进行不同的测试类的测试

defsuite1():# 套件类1
    suite_ = TestSuite()# 创建测试套件对象
    suite_.addTest(UserTestCase.test_01_add)# 添加套件的测试方法
    suite_.addTest(UserTestCase.test_02_get_info)return suite_

defsuite2():# 套件类2
    suite_ = TestSuite()# 创建测试套件对象
    suite_.addTest(OrderTestCase.test_01_add)# 添加套件的测试方法
    suite_.addTest(OrderTestCase.test_02_get_info)if __name__ =='__main__':
    TextTestRunner().run(TestSuite((suite1(), suite2())))# 按顺序进行测试

TestCase类的方法

  • assertTrue(boolean condition) 如果 condition 为 false 则失败;否则通过测试;
  • assertEquals(Object expected, Object actual) 根据 equals() 方法,如果 expected 和 actual 不相等则失败;否则通过测试;
  • assertEquals(int expected, int actual) 根据==操作符,如果 expected 和 actual 不相等则失败;否则通过测试。对每一个原始类型:int、float、double、char、byte、long、short和boolean,这个方法都会都一个函数的重载。(参见assertEquals() 的注释)
  • assertSame(Object expected, Object actual) 如果 expected 和 actual 引用不同的内存对象则失败;如果它们引用相同的内存对象则通过测试。两个对象可能并不是相同的,但是它们可能通过 equals() 方法仍然可以是相等的
  • assertNull(Object object) 如果对象为null则通过测试,反之看作失败

测试模型

django 测试模型不会使用实际数据库, 会为测试创建单独的空白数据库。所以进行模型测试需要先在

setUp()

方法中创建模型数据,进行数据初始化。当测试正常结束后,无论结果如何,会将临时创建的数据库销毁。

测试接口(视图、单元)

django 在TestCase对象中定义了Client类用于模拟客户端发送请求,所以可以使用这个工具来测试接口或视图。

deftest_api(self):
    response = self.client.post('/api')
    self.assertEqual(r.status_code,200)
    content = json.loads(r.content)
    self.assertEqual(content['result'],True)

client 可以使用 get 、post、put 等常用发放模拟客户端发送请求,这些方法的格式基本一致:

post(path, data, content_type, follow, secure)

。其参数含义为:

  • path 发送请求使用url
  • data 发送请求时携带的数据
  • content_type 携带数据的格式
  • secure 客户端将模拟HTTPS请求
  • follow 客户端将遵循任何重定向

测试 UI (模板)

标签: django python

本文转载自: https://blog.csdn.net/runsong911/article/details/127936167
版权归原作者 runsong911 所有, 如有侵权,请联系我们删除。

“自学Python第二十二天- Django框架(四) 其他组件和工具:富文本、RESTful、邮件、单元测试”的评论:

还没有评论