Django框架的使用
安装之前注意看下自己的计算机名 是否是英文 如果是中文 安装后启动Django时极有可能报”utf-8″解码错误
终端安装:
pip install django==1.11.22 -i https://pypi.tuna.tsinghua.edu.cn/simple
启动Django:
①终端启动 (麻烦)
第一种: django-admin startproject mysite 启动: python manage.py runserver 127.0.0.1:8000
②pycharm创建(推荐)
File---new project---Django 创建Django ### Django默认端口是8000 可以手动修改 点击下拉DJango的Edit下拉框更改port
关于Pycharm激活 可以看我的另一篇博客 :https://www.cnblogs.com/wakee/p/10952062.html
''' 目录介绍 py目录: settings.py : 用户自定义的各种配置 urls.py : 路由文件 wsgi.py : 启动socket服务端的 文件 mange.py: 管理文件 python mange.py 各种命令 __init__.py: 一般用来指定连接数据的格式问题(需要导入pymysql) 文件: templates: 存放html的地方 static: 静态文件 js(可以包含jQuery) css img bootstrap等文件 models : ORM链接数据库py 自创的连接数据库的py文件 其他手动创建的路由分发文件 '''
1 settings中: 配置模板文件路径:'DIRS': [os.path.join(BASE_DIR, 'templates')] (一般系统自动配置好) 2:配置静态资源的文件路径: STATIC_URL = '/static/' ### 在下面配置静态资源文件 STATICFILES_DIRS = ( os.path.join(BASE_DIR, 'static'), (逗号不能少) ) 3:注释中间件 MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', #'django.middleware.csrf.CsrfViewMiddleware', (注释中间件,这是取消防止csrf攻击,后期可开启csrf攻击) 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] 4:在app中__init__.py文件中 # 连接mysql数据库时指定连接 import pymysql pymysql.install_as_MySQLdb()
wsgiref模块 帮你处理http数据 这是本地环境使用的 上线使用uwsgi 中间件 处理连接的的过程和 处理一些安全问题 比如CSRF攻击 URL系统 处理函数分发 jinja2模板语言 django用的是自己写的模板语法 flask框架 用的jinja2
关于中间件的博客还未整理好 整理好在更新发布
路由定义
路由即请求地址与视图函数的映射关系,如果把网站比喻为一本书,那路由就好比是这本书的目录,在Django中路由默认配置在urls.py中,如下图:
# urls.py from django.conf.urls import url # 由一条条映射关系组成的urlpatterns这个列表称之为路由表 urlpatterns = [ url(regex, view, kwargs=None, name=None), # url本质就是一个函数 ] #函数url关键参数介绍 # regex:正则表达式,用来匹配url地址的路径部分, # 例如url地址为:http://127.0.0.1:8001/index/,正则表达式要匹配的部分是index/ # view:通常为一个视图函数,用来处理业务逻辑 # kwargs:略(用法详见有名分组) # name:略(用法详见反向解析)
注意
''' 当APPEND_SLASH=True(如果配置文件中没有该配置,APPEND_SLASH的默认值为True),并且用户请求的url地址的路径部分不是以 / 结尾,第一次请求之后没有发现路径会在请求路径后面加/ 再去匹配一次 当APPEND_SLASH=False时 默认就匹配一次 不成功不会加/再次匹配 '''
无名分组
在普通的正则匹配中加上()就是无名分组
urls.py文件
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), # 该正则会匹配url地址的路径部分为:article/数字/,匹配成功的分组部分会以关键字参数(article_id=匹配成功的数字)的形式传给视图函数,有几个有名分组就会传几个关键字参数 url(r'^aritcle/(?P<article_id>\d+)/$',views.article), ]
views.py文件
from django.shortcuts import render from django.shortcuts import HttpResponse # 需要额外增加一个形参,形参名必须为article_id def article(request,article_id): return HttpResponse('id为 %s 的文章内容...' %article_id)
测试:
python manage.py runserver 8001 # 在浏览器输入:http://127.0.0.1:8001/article/3/ 会看到: id为 3 的文章内容...
有名分组
有名分组其实就是在无名的分组的基础上加上了名字
语法为:(?P<名字> 正则表达式),就是在无名分组的括号里面加上了?P<名字>,注意其中P为大写
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), # 该正则会匹配url地址的路径部分为:article/数字/,匹配成功的分组部分会以关键字参数(article_id=匹配成功的数字)的形式传给视图函数,有几个有名分组就会传几个关键字参数 url(r'^aritcle/(?P<article_id>\d+)/$',views.article), ]
区别
区别在于无名分组是以位置参数的形式传递,有名分组是以关键字参数的形式传递
路由分发
1 创建两个app
# 新建项目mystie2 E:\git>django-admin startproject mysite2 # 切换到项目目录下 E:\git>cd mysite2 # 创建app01和app02 E:\git\mysite2>python3 manage.py startapp app01 E:\git\mysite2>python3 manage.py startapp app02
2 在每个app下手动创建urls.py来存放自己的路由,如下:
from django.conf.urls import url # 导入app01的views from app01 import views urlpatterns = [ url(r'^index/$',views.index), ]
app01下的views.py
from django.shortcuts import render from django.shortcuts import HttpResponse def index(request): return HttpResponse('我是app01的index页面...')
app02下的urls.py文件
from django.conf.urls import url # 导入app02的views from app02 import views urlpatterns = [ url(r'^index/$',views.index), ]
app02下的views.py
from django.shortcuts import render from django.shortcuts import HttpResponse def index(request): return HttpResponse('我是app02的index页面...')
3 在总的urls.py文件中(mysite2文件夹下的urls.py)
from django.conf.urls import url,include from django.contrib import admin # 总路由表 urlpatterns = [ url(r'^admin/', admin.site.urls), # 新增两条路由,注意不能以$结尾 # include函数就是做分发操作的,当在浏览器输入http://127.0.0.1:8001/app01/index/时,会先进入到总路由表中进行匹配,正则表达式r'^app01/'会先匹配成功路径app01/,然后include功能会去app01下的urls.py中继续匹配剩余的路径部分 url(r'^app01/', include('app01.urls')), url(r'^app02/', include('app02.urls')), ]
测试
python manage.py runserver 8001 # 在浏览器输入:http://127.0.0.1:8001/app01/index/ 会看到:我是app01的index页面... # 在浏览器输入:http://127.0.0.1:8001/app02/index/ 会看到:我是app02的index页面...
反向解析
路由设置时通过别名来获取路径
案例:登录成功跳转到index.html页面
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/$', views.login,name='login_page'), # 路径login/的别名为login_page url(r'^index/$', views.index,name='index_page'), # 路径index/的别名为index_page ]
在views.py
from django.shortcuts import render from django.shortcuts import reverse # 用于反向解析 from django.shortcuts import redirect #用于重定向页面 from django.shortcuts import HttpResponse def login(request): if request.method == 'GET': # 当为get请求时,返回login.html页面,页面中的{% url 'login_page' %}会被反向解析成路径:/login/ return render(request, 'login.html') # 当为post请求时,可以从request.POST中取出请求体的数据 name = request.POST.get('name') pwd = request.POST.get('pwd') if name == 'kevin' and pwd == '123': url = reverse('index_page') # reverse会将别名'index_page'反向解析成路径:/index/ return redirect(url) # 重定向到/index/ else: return HttpResponse('用户名或密码错误') def index(request): return render(request, 'index.html')
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录页面</title> </head> <body> <!--强调:login_page必须加引号--> <form action="{% url 'login_page' %}" method="post"> {% csrf_token %} <!--强调:必须加上这一行,如果中间件csrf没有注释掉--> <p>用户名:<input type="text" name="name"></p> <p>密码:<input type="password" name="pwd"></p> <p><input type="submit" value="提交"></p> </form> </body> </html>
index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h3>我是index页面...</h3> </body> </html>
测试
python manage.py runserver 8001 # 在浏览器输入:http://127.0.0.1:8001/login/ 会看到登录页面,输入正确的用户名密码会跳转到index.html # 当我们修改路由表中匹配路径的正则表达式时,程序其余部分均无需修改
总结:
在views.py中,反向解析的使用: url = reverse('index_page') 在模版login.html文件中,反向解析的使用 {% url 'login_page' %}
1:获取数据
2:对数据进行处理
3:连接数据库(ORM)对数据库操作)
4:对模板进行渲染(模板系统)
django将http协议请求报文中的请求行、首部信息、内容主体封装到了HttpRequest对象中(类似于我们自定义框架的environ参数)。 django会将HttpRequest对象当做参数传给视图函数的第一个参数request,在视图函数中,通过访问该对象的属性便可以提取http协议的请求数据
一.HttpRequest.method 获取请求使用的方法(值为纯大写的字符串格式)。例如:"GET"、"POST" 应该通过该属性的值来判断请求方法 二.HttpRequest.GET 值为一个类似于字典的QueryDict对象,封装了GET请求的所有参数,可通过HttpRequest.GET.get('键')获取相对应的值 三.HttpRequest.POST 值为一个类似于字典的QueryDict对象,封装了POST请求所包含的表单数据,可通过HttpRequest.POST.get('键')获取相对应的值 针对表单中checkbox类型的input标签、select标签提交的数据,键对应的值为多个,需要用:HttpRequest.POST.getlist("hobbies")获取存有多个值的列表,
同理也有HttpRequest.GET.getlist("键")
urls.py
from django.urls import re_path from app01 import views urlpatterns = [ re_path(r'^login/$',views.login), ]
Views.py
from django.shortcuts import render,HttpResponse def login(request): if request.method == 'GET': # 当请求url为:http://127.0.0.1:8001/login/?a=1&b=2&c=3&c=4&c=5 # 请求方法是GET,?后的请求参数都存放于request.GET中 print(request.GET) # 输出<QueryDict: {'a': ['1'], 'b': ['2'], 'c': ['3', '4', '5']}> # 获取?后参数的方式为 a=request.GET.get('a') # 1 b=request.GET.get('b') # 2 c=request.GET.getlist('c') # ['3', '4', '5'] return render(request,'login.html') elif request.method == 'POST': # 在输入框内输入用户名egon、年龄18,选择爱好,点击提交 # 请求方法为POST,表单内的数据都会存放于request.POST中 print(request.POST) # 输出<QueryDict: {..., 'name': ['egon'], 'age': ['18'], 'hobbies': ['music', 'read']}> # 获取表单中数据的方式为 name=request.POST.get('name') # egon age=request.POST.get('age') # 18 hobbies=request.POST.getlist('hobbies') # ['music', 'read'] return HttpResponse('提交成功')
在templates目录下新建login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录页面</title> </head> <body> <!-- method="post"代表在提交表单时会以POST方法提交表单数据 action="/login/" 代表表单数据的提交地址为http://127.0.0.1:8001/login/,可以简写为action="/login/",或者action="" --> <form action="http://127.0.0.1:8001/login/" method="post"> {% csrf_token %} <!--强调:必须加上这一行,后续我们会详细介绍--> <p>用户名:<input type="text" name="name"></p> <p>年龄:<input type="text" name="age"></p> <p> 爱好: <input type="checkbox" name="hobbies" value="music">音乐 <input type="checkbox" name="hobbies" value="read">阅读 <input type="checkbox" name="hobbies" value="dancing">跳舞 </p> <p><input type="submit" value="提交"></p> </form> </body> </html>
当浏览器基于http协议的POST方法提交数据时,数据会被放到请求体中发送给django,django会将接收到的请求体数据存放于HttpRequest.body属性中,因为该属性的值为Bytes类型,所以通常情况下直接处理Bytes、并从中提取有用数据的操作是复杂而繁琐的,好在django会对它做进一步的处理与封装以便我们更为方便地提取数据,比如
对于form表单来说,提交数据的常用方法为GET与POST
1:如果表单属性method=’GET’,那么在提交表单时,表单内数据不会存放于请求体中,而是会将表单数据按照k1=v1&k2=v2&k3=v3的格式放到url中,然后发送给django,django会将这些数据封装到request.GET中,注意此时的request.body为空、无用
2:如果表单属性method=’POST’,那么在提交表单时,表单内的所有数据都会存放于请求体中,在发送给django后会封装到request.body里,此时django为了方便我们提取数据,会request.body的数据进行进一步的处理,具体如何处理呢,需要从form表单提交数据的编码格式说起:
form表单对提交的表单数据有两种常用的编码格式,可以通过属性enctype进行设置,如下
编码格式1(默认的编码格式):enctype=”application/x-www-form-urlencoded”
编码格式2(使用form表单上传文件时只能用该编码):enctype=”multipart/form-data”
如果form表单提交数据是按照编码格式1,那么request.body中数据的格式类似于GET方法的数据格式,如k1=v1&k2=v2,此时django会将request.body中的数据提取出来封装到request.POST中方便我们提取
如果form表单提交数据是按照编码格式2,那么request.body中数据的格式为b’——WebKitFormBoundaryKtcwuksQltpNprep\r\nContent-Disposition: form-data;……’,,此时django会将request.body中的数据提取出来封装到request.POST中,将上传的文件数据专门提取出来封装到request.FILES属性中
强调:毫无疑问,编码格式2的数据量要大于编码格式1,如果无需上传文件,还是推荐使用更为精简的编码格式1
我们除了可以采用form表单向django提交数据外,还可以采用ajax技术,ajax可以提交的数据格式有:1、编码格式1 2、编码格式2 3、json,当ajax采用POST方法提交前两种格式的数据时,django的处理方案同上,但是当ajax采用POST方法提交json格式的数据时,django会将接收到的数据存放于HttpRequest.body,此时需要我们自己对HttpRequest.body属性值做反序列化操作,
具体的,我们在讲解ajax时再做具体介绍
二.HttpRequest.FILES
如果使用form表单POST上传文件的话,文件数据将包含在HttpRequest.FILES属性中。
强调:HttpRequest.FILES 只有在请求的方法为POST 且提交的<form> 带有enctype=”multipart/form-data” 的情况下才会包含数据。否则,FILES 将为一个空的类似于字典的对象。
案例:form表单上传文件
from django.urls import path,register_converter,re_path from app01 import views urlpatterns = [ re_path(r'^register/$',views.register), ]
views.py
from django.shortcuts import render,HttpResponse def register(request): if request.method == 'GET': return render(request,'register.html') elif request.method == 'POST': print(request.body) # 从request.POST中获取用户名 name=request.POST.get('name') # 从request.FILES获取文件对象 file_obj=request.FILES.get('header_img') # 上传的文件存放于templates文件夹下 with open('templates/header.png','wb') as f: for line in file_obj: f.write(line) return HttpResponse('注册成功')
在templates目录下新建register.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>注册页面</title> </head> <body> <form action="" method="POST" enctype="multipart/form-data" > {% csrf_token %} <p> 用户名:<input type="text" name="name"> </p> <p> 头像:<input type="file" name="header_img"> </p> <p> <input type="submit" value="提交"> </p> </form> </body> </html>
一.HttpRequest.path 获取url地址的路径部分,只包含路径部分 二.HttpRequest.get_full_path() 获取url地址的完整path,既包含路径又包含参数部分 如果请求地址是http://127.0.0.1:8001/order/?name=egon&age=10#_label3, HttpRequest.path的值为"/order/" HttpRequest.get_full_path()的值为"/order/?name=egon&age=10"
案例:
urls.py
from django.urls import path,register_converter,re_path from app01 import views urlpatterns = [ re_path(r'^order',views.order), ]
views.py
from django.shortcuts import render,HttpResponse # 针对请求的url地址:http://127.0.0.1:8001/order/?name=egon&age=10#_label3 # 从域名后的最后一个“/”开始到“?”为止是路径部分,即/order/ # 从“?”开始到“#”为止之间的部分为参数部分,即name=egon&age=10 def order(request): print(request.path) # 结果为“/order/” print(request.get_full_path()) # 结果为"/order/?name=egon&age=10" return HttpResponse('order page')
一.HttpRequest.META 值为包含了HTTP协议的请求头数据的Python字典,字典中的key及期对应值的解释如下 CONTENT_LENGTH —— 请求的正文的长度(是一个字符串)。 CONTENT_TYPE —— 请求的正文的MIME类型。 HTTP_ACCEPT —— 响应可接收的Content-Type。 HTTP_ACCEPT_ENCODING —— 响应可接收的编码。 HTTP_ACCEPT_LANGUAGE —— 响应可接收的语言。 HTTP_HOST —— 客服端发送数据的目标主机与端口 HTTP_REFERER —— Referring 页面。 HTTP_USER_AGENT —— 客户端使用的软件版本信息 QUERY_STRING —— 单个字符串形式的查询字符串(未解析过的形式)。 REMOTE_ADDR —— 客户端的IP地址。 REMOTE_HOST —— 客户端的主机名。 REMOTE_USER —— 服务器认证后的用户。 REQUEST_METHOD —— 一个字符串,例如"GET" 或"POST"。 SERVER_NAME —— 服务器的主机名。 SERVER_PORT —— 服务器的端口(是一个字符串)。 从上面可以看到,除 CONTENT_LENGTH 和 CONTENT_TYPE 之外,HTTP协议的请求头数据转换为 META 的键时, 都会 1、将所有字母大写 2、将单词的连接符替换为下划线 3、加上前缀HTTP_。 所以,一个叫做 X-Bender 的头部将转换成 META 中的 HTTP_X_BENDER 键。 注意:下述常用属性暂且了解即可,待我们讲到专门的知识点时再专门详细讲解 二.HttpRequest.COOKIES 一个标准的Python 字典,包含所有的cookie。键和值都为字符串。 三.HttpRequest.session 一个既可读又可写的类似于字典的对象,表示当前的会话。只有当Django 启用会话的支持时才可用。 11.HttpRequest.user(用户认证组件下使用) 一个 AUTH_USER_MODEL 类型的对象,表示当前登录的用户。 2.HttpRequest.is_ajax() 如果请求是通过XMLHttpRequest 发起的,则返回True,方法是检查 HTTP_X_REQUESTED_WITH 相应的首部是否是字符串'XMLHttpRequest'。 大部分现代的 JavaScript 库都会发送这个头部。如果你编写自己的 XMLHttpRequest 调用(在浏览器端),你必须手工设置这个值来让 is_ajax() 可以工作。 如果一个响应需要根据请求是否是通过AJAX 发起的,并且你正在使用某种形式的缓存例如Django 的 cache middleware, 你应该使用 vary_on_headers('HTTP_X_REQUESTED_WITH') 装饰你的视图以让响应能够正确地缓存
from django.shortcuts import HttpResponse,render,redirect # 导入相关包
括号内直接跟一个具体的字符串作为响应体,比较直接很简单,所以这里主要介绍后面两种形式。
render(request, template_name[, context]) 参数: 1、request:用于生成响应的请求对象,固定必须传入的第一个参数 2、template_name:要使用的模板的完整名称,必须传入,render默认会去templates目录下查找模板文件 3、context:可选参数,可以传入一个字典用来替换模块文件中的变量 综上,render的功能可以总结为:根据给定字典渲染模板文件,并返回一个渲染后的 HttpResponse对象。
# 返回重定向信息 def my_view(request): ... return redirect('/some/url/') # 重定向的地址也可以是一个完整的URL: def my_view(request): ... return redirect('http://www.baidu.com/')
向前端返回一个json格式字符串的两种方式
import json def my_view(request): data=['egon','kevin'] return HttpResponse(json.dumps(data) )
方式二
from django.http import JsonResponse def my_view(request): data=['egon','kevin'] return JsonResponse(data,safe=False) #默认safe=True代表只能序列化字典对象,safe=False代表可以序列化字典以外的对象
django的视图层由两种形式构成:FBV和CBV
1、FBV基于函数的视图(Function base view),我们之前一直介绍的都是FBV
2、CBV基于类的视图(Class base view)
案例:
urls.py
from django.urls import path,register_converter,re_path from app01 import views urlpatterns = [ re_path(r'^login/',views.LoginView.as_view()), # 必须调用类下的方法as_view ]
views.py
from django.shortcuts import render,HttpResponse,redirect from django.views import View class LoginView(View): def dispatch(self, request, *args, **kwargs): # 可在该方法内做一些预处理操作 # 当请求url为:http://127.0.0.1:8008/login/会先触发dispatch的执行 # 如果http协议的请求方法为GET,则调用下述get方法 # 如果http协议的请求方法为POST,则调用下述post方法 obj=super().dispatch(request, *args, **kwargs) # 必须继承父类的dispatch功能 return obj # 必须返回obj def get(self,request): return render(request,'login.html') def post(self,request): name=request.POST.get('name') pwd=request.POST.get('pwd') if name == 'egon' and pwd == '123': res='登录成功' else: res='用户名或密码错误' return HttpResponse(res)
测试
python manage.py runserver 8001 # 验证GET请求:在浏览器输入:http://127.0.0.1:8001/login/ # 验证POST请求:在表单内输入数据然后提交
一、变量:{{ 变量名 }} 1.1 深度查询:句点符的应用 1.2 过滤器 二、标签:{% 标签名 %} 三、自定义标签和过滤器 四、模板的导入和继承
2.1 变量的基本使用
如果html代码中的数据不是固定死的,而是动态变化的,则必须在html中嵌入变量,为此,模板语法提供了变量的概念,允许我们在html代码中嵌入变量,我们只需要在视图函数中用render方法为html文件中指定的变量赋值即可,具体用法如下
test.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <p>{{ msg }}</p> <p>{{ dic }}</p> <p>{{ obj }}</p> <p>{{ li }}</p> </body> </html>
我们需要在视图函数中为模板test.html的变量名msg、li、dic、obj、obj_li赋值,views.py内容如下
from django.shortcuts import render def test(request): # 传给模板的变量值可以是任意python类型,如下 msg='hello world' dic={'k1':1,'k2':2} class Person(object): def __init__(self,name,age): self.name=name self.age=age obj=Person('egon',18) li = [1,'aaa',obj] return render(request,'test.html',{'msg':msg,'dic':dic,'obj':obj,'li':li}) # 注意: # 1、render函数的第三个参数包含了要传给模板的变量值,是一个字典类型,该字典中的key必须与模板文件中的变量名相对应,render函数会去templates目录下找到模板文件,
然后根据字典中的key对应到模板文件中的变量名进行赋值操作,最后将赋值后的模板文件内容返回给浏览器 # 2、可以将render函数的第三个参数简写为locals(),如下 return render(request,'test.html',locals()) #locals()会将函数test内定义的名字与值转换为字典中的k与v
2.2 深度查询之句点符的使用
当视图函数传给模板的值中包含多个元素时,若想取出其中的单个元素,就必须使用句点符了。
句点符既可以引用容器类型的元素,也可以引用对象的方法,如下
test.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--调用字符串对象的upper方法,注意不要加括号--> <p>{{ msg.upper }}</p> <!--取字典中k1对应的值--> <p>{{ dic.k1 }}</p> <!--取对象的name属性--> <p>{{ obj.name }}</p> <!--取列表的第2个元素,然后变成大写--> <p>{{ li.1.upper }}</p> <!--取列表的第3个元素,并取该元素的age属性--> <p>{{ li.2.age }}</p> </body> </html>
2.3 过滤器
过滤器类似于python的内置函数,用来把视图传入的变量值加以修饰后再显示,具体语法如下
{{ 变量名|过滤器名:传给过滤器的参数 }}
常用内置过滤器
#1、default #作用:如果一个变量值是False或者为空,使用default后指定的默认值,否则,使用变量本身的值,如果value=’‘则输出“nothing” {{ value|default:"nothing" }} #2、length #作用:返回值的长度。它对字符串、列表、字典等容器类型都起作用,如果value是 ['a', 'b', 'c', 'd'],那么输出是4 {{ value|length }} #3、filesizeformat #作用:将值的格式化为一个"人类可读的"文件尺寸(如13KB、4.1 MB、102bytes等等),如果 value 是 12312312321,输出将会是 11.5 GB {{ value|filesizeformat }} #4、date #作用:将日期按照指定的格式输出,如果value=datetime.datetime.now(),按照格式Y-m-d则输出2019-02-02 {{ value|date:"Y-m-d" }} #5、slice #作用:对输出的字符串进行切片操作,顾头不顾尾,如果value=“egon“,则输出"eg" {{ value|slice:"0:2" }} #6、truncatechars #作用:如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾,如果value=”hello world egon 嘎嘎“,则输出"hello...",注意8个字符也包含末尾的3个点 {{ value|truncatechars:8 }} #7、truncatewords #作用:同truncatechars,但truncatewords是按照单词截断,注意末尾的3个点不算作单词,如果value=”hello world egon 嘎嘎“,则输出"hello world ..." {{ value|truncatewords:2 }} #8、safe #作用:出于安全考虑,Django的模板会对HTML标签、JS等语法标签进行自动转义,例如value="<script>alert(123)</script>",模板变量{{ value }}会被渲染成<script>alert(123)</script>交给浏览器后会被解析成普通字符”<script>alert(123)</script>“,失去了js代码的语法意义,但如果我们就想让模板变量{{ value }}被渲染的结果又语法意义,那么就用到了过滤器safe,比如value='<a href="https://www.baidu.com">点我啊</a>',在被safe过滤器处理后就成为了真正的超链接,不加safe过滤器则会当做普通字符显示’<a href="https://www.baidu.com">点我啊</a>‘ {{ value|safe }}
标签是为了在模板中完成一些特殊功能,语法为{% 标签名 %},一些标签还需要搭配结束标签 {% endtag %}
3.1 常用标签之for标签
#1、遍历每一个元素: {% for person in person_list %} <p>{{ person.name }}</p> {% endfor %} #2、可以利用{% for obj in list reversed %}反向循环。 #3、遍历一个字典: {% for key,val in dic.items %} <p>{{ key }}:{{ val }}</p> {% endfor %} #4、循环序号可以通过{{ forloop }}显示 forloop.counter 当前循环的索引值(从1开始) forloop.counter0 当前循环的索引值(从0开始) forloop.revcounter 当前循环的倒序索引值(从1开始) forloop.revcounter0 当前循环的倒序索引值(从0开始) forloop.first 当前循环是第一次循环则返回True,否则返回False forloop.last 当前循环是最后一次循环则返回True,否则返回False forloop.parentloop 本层循环的外层循环 #5、for标签可以带有一个可选的{% empty %} 从句,在变量person_list为空或者没有被找到时,则执行empty子句 {% for person in person_list %} <p>{{ person.name }}</p> {% empty %} <p>sorry,no person here</p> {% endfor %}
案例
url.py
from django.urls import re_path from app01 import views urlpatterns = [ re_path(r'^test/',views.test) ]
view.py
def test(request): names=['egon','kevin'] dic={'name':'egon','age':18,'sex':'male'} list1=[] return render(request,'test.html',locals())
test.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <hr> {% for name in names %} <p>{{ forloop.counter0 }} {{ name }}</p> {% endfor %} <!-- 输出结果为: 0 egon 1 kevin --> <hr> {% for name in names reversed %} <p>{{ forloop.revcounter0 }} {{ name }}</p> {% endfor %} <!-- 输出结果为: 1 kevin 0 egon --> <hr> {% for k,v in dic.items %} <p>{{ forloop.counter }} {{ k }} {{ v }}</p> {% endfor %} <!-- 输出结果为: 1 name egon 2 age 18 3 sex male --> <hr> {% for item in list1 %} <p>{{ item }}</p> {% empty %} <p>sorry,no value here</p> {% endfor %} <!-- 输出结果为: sorry,no value here --> </body> </html>
3.2 常用标签之if标签
# 1、注意: {% if 条件 %}条件为真时if的子句才会生效,条件也可以是一个变量,if会对变量进行求值,在变量值为空、或者视图没有为其传值的情况下均为False # 2、具体语法 {% if num > 100 or num < 0 %} <p>无效</p> {% elif num > 80 and num < 100 %} <p>优秀</p> {% else %} <p>凑活吧</p> {% endif %} #3、if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断。
案例
from django.urls import path,register_converter,re_path from app01 import views urlpatterns = [ # 输入http://127.0.0.1:8008/或者http://127.0.0.1:8008/index/都会转发给视图函数index re_path(r'^$',views.index), re_path(r'^index/$',views.index), re_path(r'^login/',views.login), ]
views.py
from django.shortcuts import render def index(request): return render(request,'index.html') def login(request): if request.method == 'GET': return render(request,'login.html') name=request.POST.get('name') pwd=request.POST.get('pwd') if name == 'egon' and pwd == '123': current_user=name return render(request,'index.html',locals()) else: msg='账号或密码错误' return render(request,'login.html',locals())
在templates目录下新建模板文件index.html与login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h3>首页</h3> <!-- 如果用户已经登录,则current_user变量有值,if判断结果为真,会打印变量current_user的值,为当前登录的用户名 如果用户没有登录,则current_user变量无值,if判断结果为假,会打印a标签要求用户先登录 --> {% if current_user %} <p>当前登录用户为:{{ current_user }}</p> {% else %} <p><a href="/login/">请先登录</a></p> {% endif %} </body> </html>
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录页面</title> </head> <body> <form action="" method="POST"> {% csrf_token %} <p>用户名:<input type="text" name="name"></p> <p>密码:<input type="password" name="pwd"></p> <p><input type="submit" value="提交"></p> </form> <!--输错账号密码时的提示信息--> <p style="color: red">{{ msg }}</p> </body> </html>
3.3 常用标签之with标签
# with标签用来为一个复杂的变量名起别名,如果变量的值来自于数据库,在起别名后只需要使用别名即可,无需每次都向数据库发送请求来重新获取变量的值 {% with li.1.upper as v %} {{ v }} {% endwith %
3.4 常用标签之csrf_token标签
# 当用form表单提交POST请求时必须加上标签{% csrf_token%},该标签用于防止跨站伪造请求 <form action="" method="POST"> {% csrf_token %} <p>用户名:<input type="text" name="name"></p> <p>密码:<input type="password" name="pwd"></p> <p><input type="submit" value="提交"></p> </form> # 具体工作原理为: # 1、在GET请求到form表单时,标签{% csrf_token%}会被渲染成一个隐藏的input标签,该标签包含了由服务端生成的一串随机字符串,如<input type="hidden" name="csrfmiddlewaretoken" value="dmje28mFo...OvnZ5"> # 2、在使用form表单提交POST请求时,会提交上述随机字符串,服务端在接收到该POST请求时会对比该随机字符串,对比成功则处理该POST请求,否则拒绝,以此来确定客户端的身份
当内置的过滤器或标签无法满足我们需求时,我们可以自定义,具体操作步骤如下
1、在settings中的INSTALLED_APPS添加当前app的名字,不然django无法找到自定义的过滤器或标签
settings.py
# 在settings.py中找到该列表,然后加以配置 INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01.apps.App01Config', 'app01', # 添加当前app的名字 ]
2、在文件夹app01中创建子文件夹templatetags(文件夹名只能是templatetags)
from django import template register = template.Library() # 注意变量名必须为register,不可改变 #1、自定义过滤器 @register.filter def my_multi_filter(v1 ,v2): # 自定义的过滤器只能定义最多两个参数,针对{{ value1 | filter_multi:value2 }},参数传递为v1=value1,v2=value2 return v1 * v2 #2、自定义标签 @register.simple_tag def my_multi_tag(v1, v2): # 自定义的标签可以定义多个参数 return v1 * v2 #3、自定义标签扩展之mark_safe # 注释:我们可以用内置的标签safe来让标签内容有语法意义,如果我们想让自定义标签处理的结果也有语法意义,则不能使用内置标签safe了,需要使用mark_safe,可以实现与内置标签safe同样的功能 from django.utils.safestring import mark_safe @register.simple_tag def my_input_tag(id, name): res = "<input type='text' id='%s' name='%s' />" % (id, name) return mark_safe(res)
4、自定义过滤器或标签必须重新启动django方可生效
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--必须先加载存有自定义过滤器和标签的文件--> {% load my_tags %} <!--salary的值为10,经过滤器my_multi_filter的处理结果为120--> {{ salary|my_multi_filter:12 }} <!--结果为2--> {% my_multi_tag 1 2 %} <!-- 结果为一个input标签,该表的属性id="inp1" name="username" 注意:input的属性值均为字符串类型,所以my_input_tag后的两个值均为字符串类型 --> {% my_input_tag "inp1" "username" %} </body> </html>
对比自定义标签与自定义过滤器
#1、自定义过滤器只能传两个参数,而自定义标签却可以传多个参数 #2、过滤器可以用于if判断,而标签不能 {% if salary|my_multi_filter:12 > 200 %} <p>优秀</p> {% else %} <p>垃圾</p> {% endif %}
在实际开发中,模板文件彼此之间可能会有大量冗余代码,为此django提供了专门的语法来解决这个问题,主要围绕三种标签的使用:include标签、extends标签、block标签,详解如下
5.1、模板的导入之include标签
#作用:在一个模板文件中,引入/重用另外一个模板文件的内容, {% include '模版名称' %}
案例:
可以把广告栏写到专门的文件里advertise.html
<div class="adv"> <div class="panel panel-default"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-danger"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-warning"> <div class="panel-heading"> <h3 class="panel-title">Panel title</h3> </div> <div class="panel-body"> Panel content </div> </div> </div>
然后在base.html文件中用include标签引入advertise.html文件的内容
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <style> * { margin: 0; padding: 0; } .header { height: 50px; width: 100%; background-color: black; } </style> </head> <body> <div class="header"></div> <div class="container"> <div class="row"> <div class="col-md-3"> <!--在base.html引入advertise.html文件的内容--> {% include "advertise.html" %} </div> <div class="col-md-9"></div> </div> </div> </body> </html>
5.2、模板的继承\派生之extends标签、block标签
#作用:在一个模板文件中,引入/重用另外一个模板文件的内容 {% extends "模版名称" %} # 也就是说include有的功能extends全都有,但是extends可以搭配一个block标签,用于在继承的基础上定制新的内容
Django模版引擎中最复杂且最强大的部分就是模版继承了。我们以先创建一个基本的“骨架”模版,它包含我们站点中的全部元素,并且可以定义多处blocks ,例如我们创建base.html内容如下
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title> {% block title %}自定义title名{% endblock %} </title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <style> * { margin: 0; padding: 0; } .header { height: 50px; width: 100%; background-color: #919191; margin-bottom: 20px; } </style> </head> <body> <div class="header"></div> <div class="container"> <div class="row"> <div class="col-md-3"> <div class="list-group"> {% block sidebar %} <a href="#" class="list-group-item active">服装城</a> <a href="#" class="list-group-item">美妆馆</a> <a href="#" class="list-group-item">超市</a> <a href="#" class="list-group-item">全球购</a> <a href="#" class="list-group-item">闪购</a> <a href="#" class="list-group-item">团购</a> {% endblock %} </div> </div> <div class="col-md-9"> {% block content %} base.html页面内容 {% endblock %} </div> </div> </div> </body> </html>
模板base.html 定义了一个可以用于两列排版页面的简单HTML骨架。我们新建子模板index.html的主要工作就是继承base.html然后填充/覆盖其内部的blocks。
{% extends "base.html" %} <!--用新内容完全覆盖了父模板内容--> {% block title %} index页面 {% endblock %} {% block sidebar %} <!--该变量会将父模板中sidebar中原来的内容继承过来,然后我们可以在此基础上新增,否则就是纯粹地覆盖--> {{ block.super }} <!--在继承父模板内容的基础上新增的标签--> <a href="#" class="list-group-item">拍卖</a> <a href="#" class="list-group-item">金融</a> {% endblock %} {% block content %} <!--用新内容完全覆盖了父模板内容--> <p>index页面内容</p> {% endblock %}
我们通过django访问index.html看到内容如下(block标签的内容都完成了替换或更新)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title> index页面 </title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <style> * { margin: 0; padding: 0; } .header { height: 50px; width: 100%; background-color: #919191; margin-bottom: 20px; } </style> </head> <body> <div class="header"></div> <div class="container"> <div class="row"> <div class="col-md-3"> <div class="list-group"> <!--该变量会将父模板中sidebar中原来的内容继承过来,然后我们可以在此基础上新增,否则就是纯粹地覆盖--> <a href="#" class="list-group-item active">服装城</a> <a href="#" class="list-group-item">美妆馆</a> <a href="#" class="list-group-item">超市</a> <a href="#" class="list-group-item">全球购</a> <a href="#" class="list-group-item">闪购</a> <a href="#" class="list-group-item">团购</a> <!--在继承父模板内容的基础上新增的标签--> <a href="#" class="list-group-item">拍卖</a> <a href="#" class="list-group-item">金融</a> </div> </div> <div class="col-md-9"> <!--用新内容完全覆盖了父模板内容--> <p>index页面内容</p> </div> </div> </div> </body> </html>
总结和注意
#1、标签extends必须放在首行,base.html中block越多可定制性越强 #2、include仅仅只是完全引用其他模板文件,而extends却可以搭配block在引用的基础上进行扩写 #3、变量{{ block.super }} 可以重用父类的内容,然后在父类基础上增加新内容,而不是完全覆盖 #4、为了提升可读性,我们可以给标签{% endblock %} 起一个名字 。例如: {% block content %} ... {% endblock content %} #5、在一个模版中不能出现重名的block标签。
STATIC_URL = '/static/' # 找到这一行,然后新增下述代码 STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'statics'),# 获取静态文件在服务端的绝对路径 ] #STATIC_URL = '/static/'就是为静态文件的绝对路径起了一个别名,以后我们只需要用路径/static/即可
2、在项目根目录下新增文件夹statics,为了更便于管理,可以在statics下新建子文件夹css、js、img等
3、新建模板文件index.html,在该文件中对静态文件的引用如下
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/css/my.css"> </head> <body> <h4>我是红色的,点我就绿</h4> <img src="/static/img/rb.jpeg" alt=""> <script src="/static/js/jquery-3.3.1.min.js"></script> <script src="/static/js/my.js"></script> </body> </html>
request: 用户所有的请求信息,视图函数参数必须要这个 前后台交互的数据必须使用json格式数据 ### 前台发送给后台的方式 第一种方式: 前台HTML发送给后台的方式:(常见的是这两种) get(html不指定发送方式,默认是get) post (常见的是这两种) 后台获取html请求数据 : if request.method == 'GET': pass if request.method == 'POST': pass 第二种方式: 前台ajax发送数据给后台 前提是:有js和jQuery {# 这是模态框(弹出框)添加班级 #} $('#tijiao').click(function () { console.log($('#classid').val()); $.ajax({ {# 这三步是客户在弹出框输入内容之后 将输入的内容向后台提交 #} type: "POST", url : "/classesapp/modal_add_class/", data: {"classname":$('#classname').val()}, {# 这三步是客户在弹出框输入内容之后 将输入的内容向后台提交 #} success: function (data) { {#这是后台收到前台输入的信息之后返货给HTML的数据 也就会data #} if(data == 'ok'){ alert('添加成功!'); window.location.href = '/classesapp/classes/'; // 让浏览器啊界面返回这个classesapp/classes }else{ alert('添加失败!'); $('#error').text(data) } } }) }); 后台接收HTML的ajax的内容 以及传回给html的内容 def modal_add_class(request): classname = request.POST.get('classname') # 接收html的值 if not classname: return HttpResponse('false') # 返回的信息 else: import pymysql conn = pymysql.connect(host='127.0.0.1', user='root', password='', db='s858', charset='utf8') cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) sql = 'insert into classes (name) values (%s)' cursor.execute(sql, (classname,)) conn.commit() return HttpResponse('ok') # 返回的信息 第三种方式: 前台HTML与后台交互的方式: a标签或form表单 <a href="/classesapp/add_class/" type="button" class="btn btn-success" >增加班级</a> # 代表点击增加班级的时候 会运转classesapp/add_class函数 <form action="/teacherapp/update_teacher/" method="post"> {# update_teacher 走这update_teacher函数 #} <input type="hidden" name="teacherid" value="{{ teacherid }}"> 更新后的名称 <input type="text" name="teachername" value="{{ teachername }}"> <input type="submit" value="更新"> </form> ## 表示 页面点击更新按钮时运行/teacherapp/update_teacher这个函数 第四种方式: widnow.location.href = '/classes/'; # 跳转到classes函数 window.location.reload(); # 刷新当前页面 后台正常接收数据
后台发送给html的方式: 第一种:render:渲染页面 return render(request,'classes.html',{"classes":classes}) 代表返回给渲染的是这个HTML: classes.html 和传递的数据是{"classes":classes},要注意的 {"classes":classes}这个数据是传给classes.html这个html页面的 前端可以在html页面中获取这个内容 如果仅仅是传递数据 return render(request,{"classes":classes}),这个是报错的的 因为没有页面渲染 前台获取的后台发送的内容 {% for item in classes %} <tr> <td>{{ item.id }}</td> <td>{{ item.name }}</td> <td> <a href="/classesapp/del_class/?id={{ item.id }}" onclick="return xxxxx()" type="button" class="btn btn-danger">删除</a> <a href="/classesapp/update_class/?id={{ item.id }}" type="button" class="btn btn-primary">更新</a> <button type="button" class="btn btn-success ajax_class_modal">弹出框更新</button> </td> </tr> {% endfor %} 第二种:redirect() 重指向的意思 return redirect('/classesapp/classes/') 代表重新指向函数运行 由/classesapp/classes/函数去执行向前台输入信息等 第三种方式: HttpResponse("xxx") 和 JsonResponse(") ''' HttpResponse 后台返回的格式: res["code"] = 10000 res['data'] = "success" return HttpResponse(json.dumps(res)) 前台js反序列化: // js反序列化: console.log(data); res = JSON.parse(data); 前台js的序列化: JSON.stringify(res) ''' JsonResponse(") 后台返回的格式: res["code"] = 10000 res['data'] = "success" return JsonResponse((res)) ### 相当于使用了json.dumps() 前台: console.log(data) ## data 已经反序列化好的字典 区别: HTTPResponse: 是由Django创造的, 他的返回格式为 HTTPResponse(content=响应体,content_type=响应体数据类型,status=状态码), 可以修改返回的数据类型,适用于返回图片,视频,音频等二进制文件。 JsonResponse: 是HTTPResponse的子类,适用于处理json格式的数据,但是不能返回模板。 帮助我们将数据转换为json字符串 设置响应头Content-Type为application/json
即(HTML与py的数据格式)
前台:(html中的显示) {{name}} 列表: {% for item in mylist%} {{item}} {% endfor %} {{mylist.0}} # 取得列表的首位 直接点语法 {{mylist.1}} # 取得列表的第二位 字典: {% for key, val in mydict.items %} {{key}} --- {{val}} {% endfor %}
1. css 如果想要绑定多个相同的标签的时候, 不要用id选择器, 要用class类别选择器 2. DOM ===》 Document Object Modal DOM0: html: <!--如果onclick是true的话,会执行跳转, 如果是False的 话, 阻止跳转--> <a href="https://www.baidu.com" onclick="return del();">点击</a> js: function del(){ res = window.confirm('是否确认删除?'); return res; // true / false } DOM1: html: <a href="https://www.baidu.com" id="mya">点击</a> js: 原生js: var a = document.getElementById('mya') ; // console.log(a); a.onclick = function () { res = window.confirm('是否确认删除?'); return res; } jquery: $('#mya').click(fucntion(){ res = window.confirm('是否确认删除?'); return res; })