Django学习记录
一、项目创建
1、使用pycharm创建Django项目
会创建一个跟项目同名的app,在该app中可以设置整个项目主路由、配置信息等
2、在pycharm 终端使用python3 manage.py 操作django项目。如 python3 manage.py runserver启动django项目(截图中的提示表示没有同步数据库信息)
二、URL
Django处理URL请求
浏览器地址栏 -> http://127.0.0.1:8000/admin
1、Django 从配置文件中根据ROOT_URLCONF找到主路由文件;默认情况下,该文件是项目同名app下的urls.py
2、Django 加载主路由文件中的urlpatterns变量(包含很多路由的数组)
3、依次匹配urlpatterns中的path,若匹配到第一个合适的路劲,则会中断后续匹配
4、匹配成功->调用对应的师徒函数处理请求,返回响应
5、匹配失败-> 返回404
主路由 urls.py 示例 from django.contrib import admin from django.urls import path urlpatterns = [ path('admin/', admin.site.urls), ]
三、视图函数
定义:视图函数是用于接收一个浏览器请求(httprequest对象)并通过httpresponse对象返回响应的函数。
语法:
def xxx_view(request): pass return HttpResponse对象
ps:第一个参数必须是request
返回值必须是HttpResponse对象
示例:
1、在与项目同名的app下新建urls.py文件
2、创建视图函数 page_1_view
from django.http import HttpResponse
def page_1_view(request): html = "<h1>test1</h1>" return HttpResponse(html)
3、在主路由中添加路径及对应视图函数
from django.urls import path from . import views # 在当前目录引用 urlpatterns = [ path('admin/', admin.site.urls), path('page/1', views.page_1_view) #配置路由和视图函数 ]
path函数详解:
1、导入 from django.urls import path
2、语法 path(route, views, name=None)
3、参数 route: 字符串类型,匹配请求的路径。
views:指定路径对应的视图处理函数名称。(views不能添加括号,添加括号之后就是将函数的结果引用过来了,两者完全不同)
name:为地址起的别名,在模板中地址反向解析时使用
四、path转换器
语法: <转换器类型:自定义名称>
作用:若转换器类型匹配到对应类型的数据,则将数据按照关键字传参的方式传给视图函数
例: path('update/<int:note_id>', views.update_note)
转换器类型及作用:
转换器类型 | 作用 | 示例 |
str |
匹配除'/'之外的非空字符串 | 'user/<str:username>'匹配'user/wx' |
int | 匹配0或者任何整数,返回一个int | 'page/<int:page>'匹配'page/1' |
slug | 匹配任意由ascll字母或数字以及连字符和下划线组成的短标签 | 'detail/<slug:sl>'匹配'detail/test-django' |
path | 匹配非空字段,包括路径分隔符'/' | 'test/<path:pa>'匹配'test/a/b/c' |
示例:
views.py def page_view(request): html = f"这是编号为{str(pg)}"的页面 return HttpResponse(html)
urls.py path('page/<int:pg>', views.page_view)
ps: urls.py中的urlpatterns按从上往下的顺序匹配
五、re_path()
定义:在url的匹配中可以使用正则表达式进行精确匹配
语法:re_path(reg, view, name=xxx)
正则表达式为命名分组模式(?P<name>pattern); 匹配提取参数后用关键字传参方式传递给视图函数
示例:
from django.urls import path, re_path urlpatterns = [ path('admin/', admin.site.urls), re_path(r'^(?P<x>\d{1,2})/(?P<y>\w+)/(?P<z>\d{1,2})$', views.page_view()) ]
^ 代表开头 & 代表结束 P 代表正则匹配 <x> 代表我们给他命名为x \d 匹配数字 {1,2} 匹配1-2位 \w+ 匹配字符串
六、请求和响应
Django主要做的是网页,而请求和响应是网页十分重要的东西。
请求:指浏览器通过http协议发送给服务端的数据
响应:指服务收到请求后做出响应处理后再回复给浏览器的数据。
请求方式:
1、根据http标准,http请求可以使用多种请求方法。
2、http1.0定义了三种请求方发:get、post、head(最常用)。
3、http1.1新增了五种请求方法:options、put、delete、trace、connect。
序号 | 方法 | 描述 |
1 | GET | 请求指定的页面信息,并返回实体主体 |
2 | HEAD | 类似于get方法,只是返回的数据中没有具体的内容,用于获取报头 |
3 | POST | 向指定资源提交数据进行处理请求(例如提交表单或上传文件),数据被包含在请求中,post请求可能导致新的资源建立或已有的资源修改 |
4 | PUT | 从客户端向服务端传送的数据取代指定的文档中的内容 |
5 | DELETE | 请求服务器删除指定的页面 |
6 | CONNECT | http1.1协议中预留给能够将连接改为管道方式的代理服务器 |
7 | OPTIONS | 允许客户端查看服务器的性能 |
8 | TRACE | 回显服务器收到的请求,主要用于测试或诊断 |
Django中的请求:
1、请求在Django中是视图函数的第一个参数,即HttpRequest对象。
2、Django接收到http协议的请求后,会根据请求数据报文创建HttpRequest对象。
3、HttpRequest对象通过属性描述了请求的所有相关信息。
Django中请求的属性:
1、path_info:URL字符串。
2、method:标识http请求的方法。
3、GET:QueryDict查询字典的对象,包含get请求方法的所有数据。
4、POST:QueryDict查询字典的对象,包含post请求方法的所有数据。
5、FILES:类似于字典的对象,包含所有上传的文件信息。
6、COOKIES:python字典,包含所有的cookie,key和value都是字符串。
7、session:类似字典的对象,表示当前的会话。
8、body:字符串,请求体的内容。
9、scheme:请求协议(http/https).
10、get_full_path():请求的完整路径。
11、META:请求中的元数据。 META['EMOTE_ADDR']:客户端IP地址。
响应状态码:
200 | 请求成功 |
301 | 永久重定向 |
302 | 临时重定向 |
404 | 请求的资源不存在 |
500 | 服务器错误 |
响应状态码分类:
HTTP状态码由三位十进制数字组成,第一个十进制数字定义了状态码的类型,后两个数字没有分类的作用。
分类 | 分类描述 |
1** | 信息。服务器收到信息,需要请求者继续执行操作 |
2** | 成功。操作被成功接受并处理 |
3** | 重定向。需要进一步的操作以完成请求 |
4** | 客户端错误。请求包含语法错误或无法完成的请求 |
5** | 服务端错误。服务端在处理请求的过程中发生错误 |
Django响应:
构造函数格式:
HttpResponse(content=响应体,content_type=响应体数据类型,status=状态码)
作用:
向浏览器返回响应,同时携带响应体内容
常用的Content-Type:
'text/html' | 默认的,html文件 |
'text/plain' | 纯文本 |
'text/css' | css文件 |
'text/javascript' | js文件 |
'multipart/rom-data' | 文件提交 |
'application/json' | json传输 |
'application/xml' | xml文件 |
HttpResponse子类:
类型 |
作用 | 状态码 |
HttpResponseRedirect |
重定向 | 302 |
HttpResponseNotModified | 未修改 | 304 |
HttpResponseBadRequest | 错误请求 | 400 |
HttpResponseNotFound | 没有对应的资源 | 404 |
HttpResponseForbidden | 请求被禁止 | 403 |
HttpResponseServerError | 服务器错误 | 500 |
七、GET和POST请求
GET和POST请求:
无论是GET还是POST,统一由视图函数接收请求,通过request.methed区分具体的请求操作。
示例:
def test_get_post(request): if request.method == 'GET': pass elif request.method == 'POST': pass else: pass return HttpResponse('--test get post is ok--')
GET处理:
1、GET请求动作,一般用于向服务器获取数据
2、能够产生GET请求的场景:
-浏览器地址栏中输入URL后,回车
-<a href='地址?参数=值&参数=值'>
-form表单中的methed为get
3、GET请求中如果有数据需要传递给服务器,通常会用查询字符串(Query String)传递。【不要传递敏感数据】
-URL格式:xxx?参数名1=值1&参数名2=值2
-如:http://127.0.0.1/page?a=1&b=2
-服务端接收参数。获取客户端GET请求的数据
方法示例: request.GET['参数名'] #QueryDict request.GET.get('参数名','默认值') request.GET.getlist('参数名') views.py def test_get_post(request): if request.method == 'GET': print(request.GET['a']) print(request.GET.get('c', 'no c')) print(request.GET.getlist('a')) elif request.method == 'POST': pass else: pass return HttpResponse('--test get post is ok--') urls.py path('test_get_post',views.test_get_post),
POST处理:
1、post请求动作,一般用于向服务器提交大量/隐私数据
2、客户端通过表单等POST请求将数据传递给服务器,如:
<form methed='post' action="/login"> 姓名: <input type="text" name="username"> <input type="submit" value="登录"> </form>
3、服务端接收参数:
-通过request.methed来判断是否为post请求,如:
if request.methed =='POST': 处理post请求的数据并响应 else: 处理非post请求
4、取消csrf验证,否则Django会拒绝服务端发来的POST请求,返回403
5、使用POST方法接收客户端数据
request.POST['参数名'] #QueryDict request.POST.get('参数名', '') request.POST.getlist('参数名')
八、Django设计模式及模板层
MTV
MTV代表Model-Template-View(模型-模板-视图)
M 模型层(Model): 负责与数据库交互
T 模板层(Template): 负责呈现内容到浏览器(HOW)
V 视图层(View): 负责接收请求、获取数据、返回结果(WHAT)
模板层
1、定义
模板是可以根据字典数据动态变化的html网页
模板可以根据视图中传递的字典数据动态生成相应的html网页
2、模板配置
创建模板文件夹 <项目名>/templates
在settings.py中TEMPLATES配置项
BACKEND: 指定模板的引擎
DIRS: 模板的搜索目录(可以是一个也可以是多个)
APP_DIRS: 是否要在应用的templates文件夹中搜索模板文件
OPTIONS: 有关模板的选项
3、模板的加载方式
方式一:通过loader获取模板,通过HttpResponse进行响应
在视图函数中:
from django.template import loader # 1、通过loader加载模板 t = loader.get_template('模板文件名') # 2、将t转换成HTML字符串 html = t.render('字典数据') # 3、用响应对象将返回的字符串内容返回给浏览器 return HttpResponse(html)
views.py
def test_html(request): from django.template import loader t = loader.get_template('test_html.html') html = t.render() return HttpResponse(html)
urls.py
path('test_html',views.test_html)
test_html.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>来自模板层</h3> </body> </html>
运行结果如下图:
方式二: 使用render()直接加载并响应模板
在视图函数中:
from django.shortcuts import render return render(request, '模板文件名', '字典数据')
views.py
def test_html(request): # from django.template import loader # # t = loader.get_template('test_html.html') # html = t.render() # return HttpResponse(html) from django.shortcuts import render return render(request, 'test_html.html')
运行结果如下图:
4、视图层与模板层之间的交互
视图函数中可以将python变量封装到字典中传递给模板
def xxx_view(request): dic = { "变量1": "值1", "变量2": "值2" } return render(request, 'xxx.html', dic)
在模板中可以使用{{变量名}}调用视图函数传来的变量
views.py
def test_html(request): # from django.template import loader # # t = loader.get_template('test_html.html') # html = t.render() # return HttpResponse(html) from django.shortcuts import render dic = {'name': 'tester', 'age': '17'} return render(request, 'test_html.html', dic)
test_html.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>{{name}}来自模板层<h3> </body> </html>
运行结果如下图:
5、能从视图传递到模板中的数据类型
str -字符串 int -整型 list -数组 tuple -元组 dict -字典 func -方法 object -类实例化的对象
模板中使用变量的语法:
{{变量名}}
{{变量名.index}}
{{变量名.key}}
{{对象.方法}}
{{函数名}}
views.py
def test_html_param(request): dic = {} dic['int'] = 88 dic['str'] = 'zhangsan' dic['lst'] = ['Tom', 'Jack', 'Lily'] dic['dict'] = {'a': 9, 'b': 8} dic['func'] = say_hi dic['class_obj'] = Test() return render(request, 'test_html_param.html', dic) def say_hi(): return 'hahha' class Test: def say(self): return 'tester'
urls.py
path('test_html_param',views.test_html_param),
test_html_param.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>int 是 {{ int }}</h3> <h3>str 是 {{ str }}</h3> <h3>lst 是 {{ lst }}</h3> <h3>lst 是 {{ lst.0 }}</h3> <h3>dict 是 {{ dict }}</h3> <h3>dict['a'] 是 {{ dict.a }}</h3> <h3>function 是 {{ func }}</h3> <h3>class_obj 是 {{ class_obj.say }}</h3> </body> </html>
运行结果如下图:
6、模板中的标签
作用:将一些服务端的功能嵌入到模板中,例如流程控制等。
标签语法:
{%标签%} ...... {%结束标签%}
if 标签
语法:
{% if 条件表达式1 %} ... {% elif 条件表达式2 %} ... {% elif 条件表达式3 %} ... {% else 条件表达式4 %} ... {% endif %}
注意:
if条件表达式里可以用的运算符 ==, !=, >, <, >=, <=, in, not in, is, is not, not, and, or
在if标记中使用实际括号是无效的语法,如果需要指定他们的优先级,应使用嵌套的if标记
示例:
views.py
def test_if_for(request): dic = {} dic['x'] = 10 return render(request,'test_if_for.html',dic)
urls.py
path('test_if_for',views.test_if_for),
test_if_for.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>测试 if和for</title> </head> <body> {% if x > 10 %} 今天天气很好 {% else %} 今天天气非常好 {% endif %} </body> </html>
for 标签
语法:
{% for 变量 in 可迭代对象%} ...循环语句 {% empty %} ...可迭代对象无数据时填充的数据 {% endfor %}
内置变量:
变量 | 描述 |
forloop.counter | 循环的当前迭代(从1开始索引) |
forloop.counter0 | 循环的当前迭代(从0开始索引) |
forloop.revcounter | counter值的倒序 |
forloop.revcounter0 | revcounter值的倒序 |
forloop.first | 如果这是第一次通过循环,则为真 |
forloop.last | 如果这是最后一次循环,则为真 |
forloop.parentloop | 当嵌套循环,parentloop表示外层循环 |
示例:
views.py
def test_if_for(request): dic = {} dic['x'] = 10 dic['lst'] = {'Tom', 'Jack', 'Lily'} return render(request,'test_if_for.html',dic)
test_if_for.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>测试 if和for</title> </head> <body> {% if x > 10 %} 今天天气很好 {% else %} 今天天气非常好 {% endif %} <br> {% for name in lst %} {% if forloop.first %} $$$$${% endif %} <!--当第一次循环的时候会输出该符号--> <p>{{ forloop.counter }} {{ name }}</p> <!--forloop.counter 从1开始计数--> {% if forloop.last %} ===={% endif %}<!--当第最后一次循环的时候会输出该符号--> {% empty %} 当前无数据 {% endfor %} </body> </html> </body> </html>
运行结果如下图:
九、模板层-过滤器和继承
1、过滤器
定义:在变量输出时对变量的值进行处理
作用:可以通过过滤器来改变变量的输出显示
语法:{{变量|过滤器1:'参数值1'|过滤器2:'参数值2'......}}
2、常用过滤器
过滤器 | 说明 |
lower | 将字符串转换为全部小写 |
upper | 将字符串转换为大写模式 |
safe | 默认不对变量内的字符串进行html转义 |
add:'n' |
将value的值增加n |
truncatechars:'n' | 如果字符串字符数量多于指定的字符数量,那么会被截断,截断的字符串将以可翻译的省略号序列("...")结尾 |
3、upper、add、safe使用示例
test_html_param.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>int 是 {{ int|add:'2' }}</h3> # 为原有数值增加2 <h3>str 是 {{ str|upper }}</h3> # 将字符串转换为大写 <h3>lst 是 {{ lst }}</h3> <h3>lst 是 {{ lst.0 }}</h3> <h3>dict 是 {{ dict }}</h3> <h3>dict['a'] 是 {{ dict.a }}</h3> <h3>function 是 {{ func }}</h3> <h3>class_obj 是 {{ class_obj.say }}</h3> <h3>script 是 {{ script|safe }}</h3> # 告知浏览器不要转义该字符串 </body> </html>
views.py
def test_html_param(request): dic = {} dic['int'] = 88 dic['str'] = 'zhangsan' dic['lst'] = ['Tom', 'Jack', 'Lily'] dic['dict'] = {'a': 9, 'b': 8} dic['func'] = say_hi dic['class_obj'] = Test() dic['script'] = '<script>alert(111)</script>' return render(request, 'test_html_param.html', dic)
4、继承
使用场景:如下图,很多页面的头和尾都是相同的,这就是继承的主要使用场景。
作用:模板继承可以使父模板的内容重用,子模板直接继承父模板的全部内容并可以覆盖父模板中相应的快。
语法-父模板中:
定义父模板中的块block标签
标识出哪些在子模块中是允许被修改的
block标签:在父模板中定义,可以在子模板中覆盖
语法-子模板中:
继承模板extends标签(写在模板文件的第一行) 例如:{% extends 'base.html' %}
子模板复写父模板中的内容块
{% block block_name %}
子模板块用来覆盖父模板块中的 block_name 块的内容
{% endblock block_name %}
5、关于继承的测试
首先设计一个场景,有一个测试平台,网页上一共就有3个页面,主页、用例和测试计划,其中三个页面内的头尾一样,只有里面的显示内容不一样,
因此,我们可以使用我们的继承来实现这个逻辑,实现过程如下:
首先创建我们的主页,
base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
{% block mytitle %}
<title>主页</title>
{% endblock %}
</head>
<body>
<a href="/base">主页</a>
<a href="/case">测试用例</a>
<a href="/plan">测试计划</a>
<br>
{% block info %}
这是主页
{% endblock %}
<br>
<h3>有任何问题请联系tester</h3>
</body>
</html>
如上:让头部的测试用例、测试计划和尾部的联系信息继承了base.html, 可以在其他页面复写mytitle和info
case.html
{% extends 'base.html' %} <!--继承主页 base.html--> {% block mytitle %} <!--修改不同的地方 名称'mytitle'要与base.html保持一致--> <title>测试用例</title> {% endblock %} {% block info %} <!--修改不同的地方 名称'info'要与base.html保持一致--> 欢迎进入测试用例 {% endblock %}
plan.html
{% extends 'base.html' %} <!--继承主页 base.html--> {% block mytitle %} <!--修改不同的地方--> <title>测试计划</title> {% endblock %} {% block info %} <!--修改不同的地方--> 欢迎进入测试计划 {% endblock %}
views.py
def base_index(request): return render(request, 'base.html') def case_index(request): return render(request, 'case.html') def plan_index(request): return render(request, 'plan.html')
urls.py
path('base', views.base_index), path('case', views.case_index), path('plan', views.plan_index)
6、关于继承的注意事项
不重写,将按照父模板的效果显示
重写,则按照重写效果显示
模板继承时,服务器端的动态内容无法继承
如:传递的变量无法继承
views.py
def base_index(request): lst = ['zhansan', 'lisi'] return render(request, 'base.html', locals())
base.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> {% block mytitle %} <title>主页</title> {% endblock %} </head> <body> <a href="/base">主页</a> <a href="/case">测试用例</a> <a href="/plan">测试计划</a> <br> {% block info %} 这是主页 {% endblock %} {{ lst }} <br> <h3>有任何问题请联系tester</h3> </body> </html>
十、URL反向解析
1、代码中的URL
模板[HTML]中:
<a href='url'>超链接</a> <from action='url', method='post'> from表单中的数据,用post方法提交到url
视图函数中:
HttpResponseRedirect('url')
将用户地址栏中的地址重定向到url
2、URL书写规范
绝对地址:
http://127.0.0.1:8000/page/1
相对地址:
· '/page/1', '/'开头的相对地址,浏览器会把当前地址栏里的协议、ip和端口加上这个地址,作为最终的访问地址,
即,如果当前的访问地址是'http://127.0.0.1:8000/page/3',则当前相对地址的最终路径为'http://127.0.0.1:8000/page/1'
· 'page/1', 没有'/'开头的相对地址,浏览器会根据当前url的最后一个'/'之前的内容加上该相对地址,作为最终的访问地址。
即,如果当前的访问地址是'http://127.0.0.1:8000/test/detail',则当前相对地址的最终路径为'http://127.0.0.1:8000/test/page/1'
· 建议使用以'/'开头的相对地址
3、URL反向解析
定义:指在视图和模块中,用path定义的名称来动态查找或计算出相应的路由。
语法:
path(route, views, name='别名')
path('page', views.page_view, name='page_url')
根据path中的'name='关键字传参给url确定了一个唯一确定的名字,在模板或视图中,可以通过这个名字反向推断出此url的信息