shell# pycharm终端
pip install django==3.2.8
pip install mysqlclient
#启动项目
django-admin startproject salary .
配置sql和日志和本地化 salary/settings.py
python
# SQL
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 't39',
'USER': 'root',
'PASSWORD': '123456',
'HOST': '127.0.0.1',
'PORT': '33306',
}
}
# 本地化
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
# 模板静态文件
STATIC_URL = '/static/'
STATICFILES_DIRS = [BASE_DIR / 'static']
# 日志
import os
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
},
'root': {
'handlers': ['console'],
'level': 'WARNING',
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'level': 'DEBUG',
'propagate': False,
},
},
}
生成django默认的表
bash# 迁移表
(venv) D:\py_projects\p3901>python manage.py makemigrations
(venv) D:\py_projects\p3901>python manage.py migrate
# 生成应用,启动app
(venv) D:\py_projects\p3901>python manage.py startapp employee
注册 INSTALLED_APPS = ['employee']
完成测试
hello world
配置主URL(一级路由)salary/urls.py
pythonfrom django.contrib import admin
from django.urls import path, include
# 主路由,一级路由, 根路由配置
urlpatterns = [
path('admin/', admin.site.urls),
path('emps/', include('employee.urls')) # restful风格
]
配置二级路由employee/urls.py
pythonfrom django.urls import path
from .views import *
# 二级路由, 应用路由
urlpatterns = [
path('', test), # 列表页,
path('<int:id>', test_detail), # 详情页, restful
]
employee/views.py
pythonfrom django.shortcuts import render
from django.http import HttpRequest, HttpResponse
"""
request -> wsgi www解析为 environ 对象
wsgi server调用 wsgi.py中的application(environ,start_response)
django框架是application, return 可迭代对象
其中路由,请求不同的path, 找到不同的解决问题的handler; handler可以是函数,可以是类
"""
# function-based view 第1个参数必须是request对象
# GET HEAD POST PUT PATCH DELETE 都可以,但POST不行,CSRF关了
# 列表页
def test(request: HttpRequest, **kwargs):
print(request) # <WSGIRequest: GET '/'>
print(**kwargs)
return HttpResponse('hello world', status=201)
# 详情页
def test_detail(request: HttpRequest, **kwargs):
print(request) # <WSGIRequest: GET '/'>
print(kwargs) # {'id': 1}
return HttpResponse('hello world', status=201)
# class-based view
由于以上函数可以处理GET, POST, PUT, PATCH, DELETE, 而post方法需要CSRF cookie, 所以先注释salary.settings.py
中的 MIDDLEWARE = ['django.middleware.csrf.CsrfViewMiddleware',]
访问 http://127.0.0.1:8000/ 正常
现在需求GET, POST结果不一样
视图由函数实现
响应:渲染模板返回HTML, 或直接返回JSON
employee/views.py
pythonfrom django.shortcuts import render
from django.http import HttpRequest, HttpResponse
"""
request -> wsgi www解析为 environ 对象
wsgi server调用 wsgi.py中的application(environ,start_response)
django框架是application, return 可迭代对象
其中路由,请求不同的path, 找到不同的解决问题的handler; handler可以是函数,可以是类
"""
# function-based view 第1个参数必须是request对象
# GET HEAD POST PUT PATCH DELETE 都可以,但POST不行,CSRF关了
# 列表页
def test(request: HttpRequest, **kwargs):
print(request) # <WSGIRequest: GET '/'>
print(**kwargs)
# 在handler中进行方法判断, 这个不好;更好的是 # /emps/ + methods -> handler
if request.method.lower() in ('get' ,'head'):
return HttpResponse('这是获取内容', status=200)
else: # POST PUT PATCH DELETE
return HttpResponse('这是修改或新加的内容返回', status=201)
# 详情页
def test_detail(request: HttpRequest, **kwargs):
print(request) # <WSGIRequest: GET '/'>
print(kwargs) # {'id': 1}
return HttpResponse('hello world', status=201)
# class-based view
在handler中进行方法判断, 这个不好;更好的是 /emps/ + methods -> handler
需要装饰器控制方法
from django.views.decorators.http import
require_http_methods
带参装饰器require_GET
无参装饰器 相当于 require_http_methods(["GET"])
返回 decoratorrequire_POST
无参装饰器 相当于 require_http_methods(["POST"])
返回 decoratorrequire_safe
无参装饰器 相当于 require_http_methods(["GET","HEAD"])
返回 decorator代码分析
pythondef require_http_methods(request_method_list):
def decorator(func):
@wraps(func)
def inner(request, *args, **kwargs):
if request.method not in request_method_list:
response = HttpResponseNotAllowed(request_method_list)
log_response(
'Method Not Allowed (%s): %s', request.method, request.path,
response=response,
request=request,
)
return response
return func(request, *args, **kwargs)
return inner
return decorator
require_GET = require_http_methods(["GET"]) # 返回 decorator, 单参函数;
require_POST = require_http_methods(["POST"])
注意:两次调用,生成不同的2个内嵌函数,均是单参函数。
所以每一个view函数装饰后,就是inner函数,真正的view函数是闭包func
装饰器的真正作用: 真正的路由变成现在/emps/ -> inner 函数,调用时-> 方法不允许 405 ,方法允许就调用原函数(request,*args,**kwargs)
employee/views.py
pythonfrom django.shortcuts import render
from django.http import HttpRequest, HttpResponse
from django.views.decorators.http import require_http_methods, require_GET,require_POST,require_safe
# require_safe 相当于GET, HEAD
"""
request -> wsgi www解析为 environ 对象
wsgi server调用 wsgi.py中的application(environ,start_response)
django框架是application, return 可迭代对象
其中路由,请求不同的path, 找到不同的解决问题的handler; handler可以是函数,可以是类
"""
# function-based view 第1个参数必须是request对象
# GET HEAD POST PUT PATCH DELETE 都可以,但POST不行,CSRF关了
# 列表页
# @require_http_methods(['GET', 'HEAD']) # 等价 test = require_http_methods(['GET', 'HEAD'])(test)
@require_GET
def test(request: HttpRequest, **kwargs):
print(request) # <WSGIRequest: GET '/'>
print(**kwargs)
return HttpResponse('这是获取内容', status=200)
# 装饰器等价
# test = require_GET(test)
@require_http_methods(['POST', 'PUT', 'PATCH', 'DELETE'])
def test(request: HttpRequest, **kwargs):
print(request) # <WSGIRequest: GET '/'>
print(**kwargs)
return HttpResponse('这是修改或新加的内容返回', status=201)
# 详情页
def test_detail(request: HttpRequest, **kwargs):
print(request) # <WSGIRequest: GET '/'>
print(kwargs) # {'id': 1}
return HttpResponse('hello world', status=201)
# 装饰器
# class-based view
注意: 装饰后,test已经相当于是inner函数了, 再被urls中导入Inner过去
在urls中导入的是是装饰后的inner
注意以下和以上是一样的结果,因为urls导入的装饰后的inner或直接在url处装饰这个原始函数是一样的效果
employee/urls.py
pythonfrom django.urls import path
from .views import *
from django.views.decorators.http import require_GET
# 二级路由, 应用路由
urlpatterns = [
# path('', test), # 列表页, /emps/ test = require_GET(test) => inner
# path('<int:id>', test_detail), # 详情页, restful
path('', require_GET(test)) # /emps/ inner
]
employee/views.py
pythonfrom django.shortcuts import render
from django.http import HttpRequest, HttpResponse
from django.views.decorators.http import require_http_methods, require_GET,require_POST,require_safe
# require_safe 相当于GET, HEAD
"""
request -> wsgi www解析为 environ 对象
wsgi server调用 wsgi.py中的application(environ,start_response)
django框架是application, return 可迭代对象
其中路由,请求不同的path, 找到不同的解决问题的handler; handler可以是函数,可以是类
"""
# function-based view 第1个参数必须是request对象
# GET HEAD POST PUT PATCH DELETE 都可以,但POST不行,CSRF关了
# 列表页
def test(request: HttpRequest, **kwargs):
print(request) # <WSGIRequest: GET '/'>
print(kwargs)
return HttpResponse('这是获取内容', status=200)
# 详情页
def test_detail(request: HttpRequest, **kwargs):
print(request) # <WSGIRequest: GET '/'>
print(kwargs) # {'id': 1}
return HttpResponse('hello world', status=201)
# 装饰器
# class-based view
from django.http import JsonResponse
此方法可以将python的字典,列表转换为javascript的对象, 数组
employee/urls.py
from django.urls import path from .views import * from django.views.decorators.http import require_GET # 二级路由, 应用路由 urlpatterns = [ # path('', test), # 列表页, /emps/ test = require_GET(test) => inner # path('<int:id>', test_detail), # 详情页, restful path('', require_GET(test)) # /emps/ inner ]
employee/views.py
from django.shortcuts import render from django.http import HttpRequest, HttpResponse from django.http import JsonResponse from django.views.decorators.http import require_http_methods, require_GET, require_POST, require_safe # require_safe 相当于GET, HEAD """ request -> wsgi www解析为 environ 对象 wsgi server调用 wsgi.py中的application(environ,start_response) django框架是application, return 可迭代对象 其中路由,请求不同的path, 找到不同的解决问题的handler; handler可以是函数,可以是类 """ # function-based view 第1个参数必须是request对象 # GET HEAD POST PUT PATCH DELETE 都可以,但POST不行,CSRF关了 # 列表页 def test(request: HttpRequest, **kwargs): print(request) # <WSGIRequest: GET '/'> print(kwargs) return JsonResponse({'books':[ (1,'python',3), (2,'c++',3) ]}) # json的数据类型, 字符串; json.dumps() -> str python 列表 => js 数组; python 字典 => js 对象 # 详情页 def test_detail(request: HttpRequest, **kwargs): print(request) # <WSGIRequest: GET '/'> print(kwargs) # {'id': 1} return HttpResponse('hello world', status=201) # 装饰器 # class-based view
正常
如果换成
def test(request: HttpRequest, **kwargs): print(request) # <WSGIRequest: GET '/'> print(kwargs) return JsonResponse([ (1,'python',3), (2,'c++',3) ]) # json的数据类型, 字符串; json.dumps() -> str python 列表 => js 数组; python 字典 => js 对象
会报错 TypeError: In order to allow non-dict objects to be serialized set the safe parameter to False.
因为默认python会转换安全的数据字典,如果使用列表,就认为不安全,需要加一个关键字参数safe=False
表示不进行安全检查,写代码的人认为是安全的。
diff# 列表页
def test(request: HttpRequest, **kwargs):
print(request) # <WSGIRequest: GET '/'>
print(kwargs)
return JsonResponse([
(1,'python',3),
(2,'c++',3)
+ ],safe=False) # json的数据类型, 字符串; json.dumps() -> str python 列表 => js 数组; python 字典 => js 对象
父类(from django.views import View
)有流程,子类只需要修改一点方法
https://docs.djangoproject.com/en/4.0/#the-view-layer
Class-based views: Overview | Built-in display views | Built-in editing views | Using mixins | API reference | Flattened index
view介绍: https://docs.djangoproject.com/en/4.0/topics/class-based-views/intro/
View是所有View的根基类, 它定义了所有视图类的基本处理规则。as_view()是仅仅类方法(仅类可以调用) , 本质就是一个视图函数。
employee/urls.py
pythonfrom django.urls import path
from .views import *
from django.views.decorators.http import require_GET
# 二级路由, 应用路由
urlpatterns = [
# 暴露handler函数,直接是函数
# path('', test), # 列表页, /emps/ test = require_GET(test) => inner
# path('<int:id>', test_detail), # 详情页, restful
# 暴露handler函数,装饰出来的handler函数
path('', require_GET(test)), # /emps/ 到 test 函数 require_GET(test) = inner == inner(request, *args, **kwargs)
# 暴露handler函数, 仅类方法返回的handler函数; as_view在TestView没有覆盖,就是父类View的as_view就去
path('ts/',TestView.as_view()), # /emps/ts/ 到 TestView类 TestView.as_view() = view == view(request, *args, **kwargs)
]
employee/views.py
pythonfrom django.shortcuts import render
from django.http import HttpRequest, HttpResponse
from django.http import JsonResponse
from django.views import View
"""
request -> wsgi www解析为 environ 对象
wsgi server调用 wsgi.py中的application(environ,start_response)
django框架是application, return 可迭代对象
其中路由,请求不同的path, 找到不同的解决问题的handler; handler可以是函数,可以是类
"""
# funciton-based view
# 列表页
def test(request: HttpRequest, **kwargs):
print(request) # <WSGIRequest: GET '/'>
print(kwargs)
return JsonResponse([
(1, 'python', 3),
(2, 'c++', 3)
], safe=False) # json的数据类型, 字符串; json.dumps() -> str python 列表 => js 数组; python 字典 => js 对象
# 详情页
# request, **kwargs
# class-based view
class TestView(View):
"""
发请求是 /emps/ts/ -> TestView.as_view() -> view == view(request, *args, **kwargs) 函数
view函数中,由dispatch函数,不同请求方法对应不同函数的映射。
最终由TestView中的 handler处理
未定义的方法, 统一是405
"""
def get(self, request: HttpRequest, **kwargs): # TestView 实例相关; 视图函数必须有request
print(request.content_type)
print(kwargs)
return HttpResponse('这是视图类TestView的get')
def post(self, request: HttpRequest, **kwargs): # TestView 实例相关; 视图函数必须有request
print(request.content_type)
print(kwargs)
return HttpResponse('这是视图类TestView的post')
urls.py源代码中TestView.as_view()结果是view,这里的view是每次请求返回新的view,还是一个view?
返回1个view, 每一个请求调用 这个同一个view函数 多次。
path('ts/',TestView.as_view()), # /emps/ts/ 到 TestView类 TestView.as_view() = view == view(request, *args, **kwargs) path('ts1/',TestView.as_view()), # /emps/ts/ 到 TestView类 TestView.as_view() = view == view(request, *args, **kwargs) ]
现在是两次调用,所以view函数生成2次
diff+view = TestView.as_view()
urlpatterns = [
# 暴露handler函数,直接是函数
# path('', test), # 列表页, /emps/ test = require_GET(test) => inner
# path('<int:id>', test_detail), # 详情页, restful
# 暴露handler函数,装饰出来的handler函数
path('', require_GET(test)), # /emps/ 到 test 函数 require_GET(test) = inner == inner(request, *args, **kwargs)
# 暴露handler函数, 仅类方法返回的handler函数; as_view在TestView没有覆盖,就是父类View的as_view就去
+ path('ts/',view), # /emps/ts/ 到 TestView类 TestView.as_view() = view == view(request, *args, **kwargs)
+ path('ts1/',view), # /emps/ts/ 到 TestView类 TestView.as_view() = view == view(request, *args, **kwargs)
]
这样也可以
来一个新的请求,最终调用的是view函数
python @classonlymethod
def as_view(cls, **initkwargs):
"""Main entry point for a request-response process."""
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError(
'The method name %s is not accepted as a keyword argument '
'to %s().' % (key, cls.__name__)
)
if not hasattr(cls, key):
raise TypeError("%s() received an invalid keyword %r. as_view "
"only accepts arguments that are already "
"attributes of the class." % (cls.__name__, key))
def view(request, *args, **kwargs):
self = cls(**initkwargs)
self.setup(request, *args, **kwargs)
if not hasattr(self, 'request'):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
return self.dispatch(request, *args, **kwargs)
self = cls(**initkwargs)
这一句cls是, TestView.as_view()调用的类 TestView
, 也是as_view传递进来的cls, 是闭包。
这一句是解构,将initkwargs解构, 变成关键字传参, 实例化TestView
类对象
/emps/ts -> view(request) -> 上面的view函数。
第1句相当于 self = TestView() # 实例; view是1个,每一次请求就会调用一次view函数, 在函数中为这个请求生成一个TestView实例, 处理这个request请求。
一次请求,一次view调用,一个独立的TestView实例。
return self.dispatch(request, *args, **kwargs)
self是 TestView实例
dispatch方法,因为自己没有这个方法,就继承父类View的dispatch方法
python # self对应 TestView的实例, request传递来的
def dispatch(self, request, *args, **kwargs):
# 当前请求的方法在支持的方法列表中 # get in ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
if request.method.lower() in self.http_method_names:
# 反射: 字符串找TestView实例的属性 # get() 方法 handler = self.get 绑定了self的函数
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs) # self.get(request, *args, **kwargs)
注意:
getattr(self, request.method.lower(), self.http_method_not_allowed)
反射先查找TestView实例字典,后查找TestView,父View类字典, 父类没有时,就第3个参数的handler
pythondef http_method_not_allowed(self, request, *args, **kwargs): logger.warning( 'Method Not Allowed (%s): %s', request.method, request.path, extra={'status_code': 405, 'request': request} ) return HttpResponseNotAllowed(self._allowed_methods())
handler(request, *args, **kwargs)
就是我们定义的TestView实例的方法,因为已经绑定了TestView实例
pythonclass TestView(View):
"""
发请求是 /emps/ts/ -> TestView.as_view() -> view == view(request, *args, **kwargs) 函数
view函数中,由dispatch,不同请求方法对应不同函数的映射。
最终由TestView中的 handler处理
未定义的方法, 统一是405
"""
def get(self, request: HttpRequest, **kwargs): # TestView 实例相关; 视图函数必须有request
print(request.content_type)
print(kwargs)
return HttpResponse('这是视图类TestView的get')
def post(self, request: HttpRequest, **kwargs): # TestView 实例相关; 视图函数必须有request
print(request.content_type)
print(kwargs)
return HttpResponse('这是视图类TestView的post')
本文作者:mykernel
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!