一、Django项目创建
1.项目结构
1.1. settings.py 文件
"""
Django settings for mydemo project.
Generated by 'django-admin startproject' using Django 3.1.7.《django版本》
For more information on this file, see
https://docs.djangoproject.com/en/3.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.1/ref/settings/
"""
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent """《显示当前项目根目录》"""
SECRET_KEY = 'jb^(bip2g_@%r*5ni-8@#yz%*crzw7sqpq_qcbhw48lgk+ctxo'
DEBUG = True
"""
调试模式开关,默认为True(调试模式)
为True时:1.检测代码改动后立刻重启服务;2.显示报错页面
为False时(正式启动模式/上线模式):项目上线时一定要更改为False
"""
ALLOWED_HOSTS = []
"""
允许的域名,只有列表中添加的域名可以访问服务器,默认会允许127.0.0.1或者localhost访问
设置为"*":表示允许所有域名访问
设置当前局域网内域名可进行访问:
1.项目启动时设置:python manage.py runserver 0.0.0.0:8000
2.添加局域网IP
"""
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'mydemo.urls'
"""
项目主路由位置,默认为项目文件夹下的urls.py
"""
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
"""
模版配置
"""
WSGI_APPLICATION = 'mydemo.wsgi.application'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
LANGUAGE_CODE = 'en-us'
"""
主页语言配置,可设置为中文
LANGUAGE_CODE = 'zh-hans'
"""
TIME_ZONE = 'UTC'
"""
时区设置,主要影响数据库时间显示
TIME_ZONE = 'Asia/shanghai'
"""
USE_I18N = True
USE_L10N = True
USE_TZ = True
STATIC_URL = '/static/'
"""
静态文件路由
"""
1.2 URL
- 根据浏览器传过来的URL,django从配置文件中根据ROOT_URLCONF找到主路由文件;默认情况下该路由文件位于同名目录下的urls.py中
- django加载主路由中的urlpattern变量,urlpattern是一个包含很多路由的列表
- 从上往下依次匹配urlpattern中的path,匹配到第一个满足的path即中断后续匹配
- 匹配成功,调用对应的视图函数处理请求,返回对应的响应
- 匹配失败,返回404响应
1.3 视图函数
-
定义:用于接收浏览器请求( HttpRequest对象),并通过HttpResponse对象返回响应的函数 -
语法:return必须是HttpResponse对象 def demoview(request[,其他参数]):
return HttpResponse('需要返回的数据')
-
示例: 在项目同名目录下创建views.py from django.http import HttpResponse
def page1_view(requet):
html = "<h1>hallo world</h1>"
return HttpResponse(html)
添加路由url.py from django.contrib import admin
from django.urls import path
from . import views
urlpatterns = [
path('admin/', admin.site.urls),
path('page1/', views.page1_view),
]
1.4 路由配置-path
- path()函数
- 导入
from django.urls import path - 语法
path(route,views,name=None) - 参数:
- route:字符串类型,匹配的请求路径
- views:指定路径所对应的视图处理函数的名称
- name:为地址起别名,在模板中进行地址反向解析时使用
- path转换器
- 语法:<转换器类型:自定义名>
- 作用:若转换器类型匹配到对应类型的数据,则将数据按照关键字传参的方式传递给视图函数
- 例:
path('page/<int:page>',views.xxx) ,如果匹配到page路径后面时int类型,则会将接收到的路径中的数据赋值给变量page,然后以关键值传参的方式传递到视图中 -
转换器类型 | 作用 | 样例 |
---|
str | 匹配除了‘/’之外的非空字符串 | ”v1/users/<str:username>“匹配/v1/users/china,匹配结果为:username=china | int | 匹配0和任何正整数,返回一个int | ”v1/users/<int:num>“匹配/v1/users/35,匹配结果为:num=35 | slug | 匹配任意有ASCII字母或数字以及连字符和下划线组成的短标签 | ”v1/users/<slug:sl>“匹配/v1/users/this_is_django,匹配结果为:sl=this_is_django | path | 匹配非空字段,包括路径分隔符”/“ | ”v1/users/<path:ph>“匹配/v1/users/goods/a/b/c,匹配结果为:ph=goods/a/b/c |
- re_path转换器
- 语法:
re_path(reg,view,name=xxx)
- 正则表达式必须为命名分组模式(
?P<name>pattern ;匹配成功后用关键字传参的方式传递给视图函数 - 作用:使用正则进行精确匹配
- 例:
url(r'^weather/(?P<city>[a-z]+)/(?P<year>\d{4})/$', views.weather),
二、请求与响应
2.1.请求和响应
2.1.1 django中的请求
- 视图函数中的第一个参数即为HttpRequest对象
- django接受到http协议的请求后,会根据请求数据报文创建HttpRequest对象
- HttpRequest对象通过属性,描述了请求的所有相关信息
-
HttpRequest属性:
性名 | 说明 |
---|
th_info | URL字符串 | ethod | 字符串,表示Http请求方法:GET、POST、PUT等 | ET | QueryDict查询字典的对象,包含get请求方式的所有数据 | POST | QueryDict查询字典的对象,包含post请求方式的所有数据 | ILES | 类似于字典的对象,包含所有的上传文件信息 | OOKIES | python字典,包含所有的cookie,键和值都为字符串 | ession | 类似于字典对象,表示当前的会话 | ody | 字符串请求体的内容 | cheme | 请求协议(http或者https) | equest.get_full_path() | 请求的完整路径,会将查询字符串一并取出 | equest.META | 请求中的元数据(消息头) | equset.META[‘REMOTE_ADDR’] | 客户端IP地址 |
2.1.2 django中的响应对象
- 构造函数:
- HttpResponse(content=响应体,content_type=响应体数据类型,status=状态码)
- 作用:
- 参数:
- content: 表示返回的内容
- status_code: 返回的HTTP响应状态码(默认为200)
- content_type: 指定返回数据的MIME类型(默认为text/html),浏览器会根据这个属性来显示数据
- 常见content_type:
- ‘text/html’: 默认的,HTML文件
- ‘text/plain’: 纯文本
- ‘text/css’: css文件
- ‘text/javascript’: js文件
- ‘multipart/form-data’: 文件提交
- ‘application/json’: json传输
- ‘application/xml’: xml文件
- HttpResponse子类
类型 | 作用 | 状态码 |
---|
HttpResponseRedirect | 重定向 | 302 | HttpResponseNotModified | 未修改 | 304 | HttpResponseBadRequest | 错误请求 | 400 | HttpResponseNotFound | 没有对应的资源 | 404 | HttpResponseForbidden | 请求被禁止 | 403 | HttpResponseServerError | 服务器错误 | 500 |
2.2 GET请求和POST请求
- 定义:
- GET处理
- GET请求动作一般用于向服务器获取数据
- 能够产生GET请求的场景
- 浏览器地址栏输入URL回车后
<a href = "地址?参数=值&参数=值"> - form表单中的method为get
- GET请求中,如果有数据需要传递给服务器,通常会用查询字符串(Query String)传递,注意不要传递敏感数据
- POST 处理
- POST请求动作一般用于向服务器提交大量/隐私数据
- 客户端通过表单等POST请求将数据传递给服务器端;如:
<form method='post' action="/login"> #action指明post请求发给哪个路由
姓名:<input type="text" name="username">
<input type='submit' value='登陆'>
</form>
- 服务器端接收参数:通过request.method来判断是否为post请求
- 使用post方式接收客户端数据
request.POST['参数名']
request.POST.get('参数名',’默认值‘)
request.POST.getlist(’参数名‘)
- 注意:发送POST请求时,django需要取消csrf验证,否则django会拒绝客户端发来的POST请求,报403响应
- 取消csrf验证:注释掉settings.py文件中的MIDDLEWARE中的csrfviewsmiddleware的中间件
2.3 Django的设计模式(MTV)
- 设计模式:MVC和MTV
- MVC(model-view-controller)模型-视图-控制器模式
- M 模型层,主要用于对数据库层的封装
- V 视图层,用于向用户展示结果
- C 控制层,用于处理请求、获取数据、返回结果
- 优点:降低模块间的耦合度
- MTV (model-template-view) 模型-模版-视图模式
- M 模型层,负责与数据库交互
- T 模板层,负责呈现内容到浏览器(html)
- V 视图层,负责接收请求、获取数据、返回结果
- 优点:降低模块间的耦合度
2.4 模板层
-
模板定义:
- 模板是可以根据字典数据动态变化的HTML网页
- 模板可以根据视图中传递的字典数据动态生成相应的HTML网页
-
模板配置
- 创建模板文件夹<项目名>/templates
- 在settings.py中TEMPLATES配置项
- BACKEND:指定模板的引擎
- DIRS:模板的搜索目录,可以是一个或多个
- APP_DIRS:是否要在子应用中的templates文件夹中搜索模板文件
- OPTIONS:有关模板的选项
- 配置中需修改的部分
- 设置DIRS - ‘DIRS’:[os.path.join(BASE_DIR,‘templates’)],
-
模板的加载方式
- 方案一:通过loader获取模板,通过HttpResponse进行响应,在视图函数中进行如下配置
from django.template import loader
t = loader.get_template("模板文件名")
html = t.render(字典数据)
return HttpResponse(html)
- 方案2:使用render()直接加载并响应模板,在函数视图中进行如下配置
from django.shortcuts import render
return render(request,'模板文件名',字典数据)
-
视图层与模板层之间的交互
-
模板层-变量
类型名 | 注释 |
---|
str | 字符串 | int | 整型 | list | 列表 | tuple | 元组 | dict | 字典 | func | 方法 | obj | 类实例化对象 |
- 在模板中使用变量语法
- {{ 变量名 }}
- {{ 变量名.index }} #传入的变量为列表、元组等,可用.index方法取出其中的元素
- {{ 变量名.key }} #传入的变量为字典,可用.key方法获取key所对应的值
- {{ 对象.方法 }} #调用对象的方法
- {{ 函数名 }} #调用函数
- 模板标签
- 模板过滤器
- 模板的继承
2.5 URL反向解析
- URL:
- 代码中常出现URL的位置:
- 模板【HTML中】:
<a href='url'>超链接</a> #点击后页面跳转至url<form action='URL' method='post'> #form表单中的数据用post方法提交值URL - 视图函数中 - 302跳转 HttpResponseRedirect(‘url’) #将用户地址栏中的地址跳转到URL
- 代码中URL书写规范:
- 绝对地址:http://127.0.0.1:8000/page1 (将协议、域名、端口、path全部写出)
- 相对地址:
- ‘/page/1’ : '/'开头的相对地址,浏览器会把当前地址栏里的协议、IP、端口加上这个path,作为最终访问地址。如:http://127.0.0.1:8000 + /page/1
- ‘page/1’ : 没有’/‘开头的相对地址,浏览器会根据当前URL的最后一个’/'之前的内容加上这个path最为最终访问地址。如:http://127.0.0.1:8000/topic/detail,加上相对地址后的访问地址为:http://127.0.0.1:8000/topic/page/1
- URL反向解析
- 定义:URL反向解析是指在视图或模板中,用path定义的名称来动态查找或计算出相应的路由
- 语法:
- path(route,views,name=‘别名’)
- 例:path(‘page’,views.page_view,name = ‘page_url’)
- 根据path中的name关键字传参给URL确定了唯一的名字,在模板或视图中,可以通过这个名字反向推断出此URL信息
- 模板中 - 通过URL标签实现地址的反向解析
{% url '别名' %} {% url '别名' '参数值1' '参数值2' %} ,参数值1:向路由中传入的参数- 例
{% url 'page1' '100' %} #解析name=page1的路由,并传入参数100 {% url 'page2' key1='1' key2='2' %} #解析name=page2的路由,并关键字传参key1,key2 注意 路由解析时传递的参数均为字符串方式传入 - 在视图函数中 - 通过调用django中的reverse方法进行反向解析
from django.urls import reverse
reverse('别名', args=[ ], kwargs={ })
reverse('page1',args=[100])
reverse('page2',kwargs={key1=1,key2=2})
三、静态文件
- 定义:项目中的css、图片、js、音频、视频等
- 静态文件配置
- 静态文件使用 - 配置(settings.py):
- 配置静态文件的访问路径,该配置创建项目后默认存在
- 通过那个URL地址能找到静态文件
- STATIC_URL = ‘/static/’
- 说明:指定访问静态文件时是需要通过/static/xxx或http://127.0.0.1:8000/static/xxx,其中xxx表示具体的静态资源位置
- 配置静态文件的存储路径 STATICFILES_DIRS,STATICFILES_DIRS保存的是静态文件在服务器中的存储位置
STATICFILES_DIRS = (
os.path.join(BASE_DIR,'static'),
)
- 静态文件访问
<img src="http://127.0.0.1:8000/static/image1.jpg" width="200" height="200">
<img src="static/image2.jpg" width="200" height="200" >
- 通过{% static %}标签访问静态文件 推荐使用此方法进行静态文件加载
- 加载static - {% load static %}
- 使用静态资源 - {% static ‘静态资源路径’ %}
- 样例:
<img src='{% static 'images/lena.jpg' %}' > 访问static文件夹下images文件夹下的lena.jpg
四、应用与路由
- 应用:
- 定义:在django项目中是一个独立的业务模块,可以包含自己的路由、视图、模板、模型
- 创建应用:
- 分布式路由
django中主路由配置文件(urls.py)可以不处理用户的具体路由,主路由配置文件可以做请求的分发(分布式请求处理)。具体的请求可由各自的应用来进行处理,
- 分布式路由配置:
- 主路由中调用include函数
- 语法:include(‘app_name.url模块名’)
- 作用:用于将当前路由转到各个应用的路由配置文件的urlpatterns进行分布式处理
- 以http://127.0.0.1:8000/users/index为例
from django.urls import psth,include
from . import views
urlpatterns = [
path('admin/',admin.site.urls),
path('users/',include('users.urls'))
]
- 应用下配置urls.py
- 应用下的模板
- 应用内部也可配置模板目录,配置方法如下:
- 应用下手动创建templates文件夹
- 在settings.py中开启应用模板功能
- INSTALLED_APPS配置项中的’APP_DIRS’值为True即可
- 注意:应用下templates和外层templates都存在时,django的查找模板规则:
五、ORM模型
5.1 数据库
- 模型层(Models) - 负责与数据库之间进行通信
- django配置MySQL
- 使用mysqlclient[版本mysqlclient 1.3.13以上]
- 安装前确认ubuntu是否已安装python3-dev 和 default-libmysqlclient-dev
- 使用命令检查是否安装sudo apt list --installed|grep -E 'libmysqlclient-dev|python3-dev’若无命令输出则需要安装
- 安装命令:sudo apt-get install python3-dev default-libmysqlclient-dev
- 安装mysqlclient: sudo pip install mysqlclient
- 如出现如下报错:unable to execute ‘x86_64-linux-gnu-gcc’: No such file or directory 则需在linux终端执行
sudo apt-get install gcc - 使用pymysql
- 使用pip install pymysql 安装pymysql
- 对比:mysqlclient是C扩展类,安装麻烦,运行速度快;pymysql是纯python实现,安装简单,运行速度不及mysqlclient
- 创建项目对应的数据库
- 进入mysql数据库执行:
create database 数据库名 default charset utf8 #创建数据库设置默认编码格式为utf8- 通常数据库名保持跟项目名一致
- 在settings.py文件中进行数据库的配置
- 修改DATABASES 配置项的内容,将sqlite3改为mysql
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mysql_demo',
'USER': 'root',
'PASSWORD': 'mysql',
'HOST': '127.0.0.1',
'PORT': '3306',
}
}
ENGINE:常见的引擎还有: ‘django.db.backends.mysql’ ‘django.db.backends.sqllite3’ ‘django.db.backends.oracle’ ‘django.db.backends.postgresql’
5.2 模型类
- 模型
- 定义:是一个python类,它是由
django.db.models.Model 派生出的子类(即它必须继承至django.db.models.Model ) - 一个模型类代表着数据库的一张数据表
- 模型类中的每一个类属性都代表着数据库中的一个字段
- 模型是数据库交互的接口,是表示和操作数据库的方法和方式
5.3 ORM框架
- 定义:ORM(object relational mapping) 对象关系映射,它是一种程序技术,允许使用类和对象对数据库进行操作,避免了通过sql语句操作数据库
- 作用:
- 建立模型类和表之间的对应关系,允许我们通过面向对象的方式来操作数据库
- 根据设计的模型类来生成数据库中的表格
- 通过简单的配置就可进行数据库的切换(切换settings.py文件中配置的数据库名即可)
- 优点:
- 只需要面向对象编程,不需要面向数据库编写代码
- 对数据库的操作都转换成对类属性和方法的操作
- 不用编写各种数据库的sql语句
- 实现了数据模型与数据库的解耦,屏蔽了不同数据库操作上的差异
- 不在关注用的数据库是mysql、oracle…等数据库的内部细节
- 通过简单的配置就可以轻松更换数据库,而不需要修改代码
- 缺点:
- 对于复杂业务,使用成本较高(对于查询很复杂的业务,需要学习如何使用ORM来实现)
- 根据对象的操作转换成sql语句,根据查询的结果转化成对象,在映射的过程中会有性能损失
- ORM 映射图
- 模型示例:
- 在子应用中的模型类中添加模型类及字段
from django.db import models
class Book(models.Model):
book_name = models.CharField(verbose_name='书名',max_length=50,default='')
price = models.DecimalField(verbose_name='价格',max_digits=7,decimal_places=2)
- 数据库迁移
- 迁移是django用来同步对模型所做更改(添加字段,删除模型等)到同步到数据库的方式
- 生成迁移文件 - 执行
python manage.py makemigrations 将应用下的models.py文件生成一个中间文件,并保存在migrations文件夹中 - 执行迁移脚本程序 -
python manage.py migrate 执行迁移程序实现迁移,将每个应用下的migrations目录中的中间文件同步到数据库 - 迁移生成的数据库表名为:
应用名_模型类类名
- 创建模型类流程总结
- 创建应用
- 在应用下的models.py文件中编写模型类
from django.db import models
class ModelName(models.Model):
FieldName = models.FieldType(field-options)
"""
FieldName:字段名,迁移后对应数据表中字段名
FieldType:字段类型
field-options:字段选项
"""
- 迁移同步 makemigrations&migrate
注意: 任何关于表结构的修改,务必在对应模型类上进行修改; 向模型类中添加字段时需要设置默认值; - 字段类型
字段类型 | 数据库类型 | 作用 | 参数 |
---|
BooleanField() | tinyint(1) | 使用True或False来表示值 | - | CharField() | varchar | - | max_length:字符最大长度(必填参数) | DateField() | date | 表示日期 | auto_now:每次保存对象时, 自动设置该字段为当前时间(True/False); auto_now_add:当对象第一次被创建时自动设置当前时间(True/False); default:设置当前默认时间(取值:字符串格式时间,如’2021-7-1’) * 以上三个参数只能多选一 | DateTimeField() | datetime(6) | 表示日期和时间 | 参数通DateField | FloatField() | double | 使用小数表示值 | - | DecimalField() | decimal(x,y) | 使用小数表示值 | max_digits:位数总长度, 包括小数点后的位数, 该值必须大于等于decimal_places; decimal_places:小数点后的数字长度(即小数位数) *以上两个参数必选 | EmailField() | varchar | 表示邮箱 | - | IntegerField() | int | 表示整数 | - | ImageField() | varchar(100) | 在数据库中为了保存图片的路径 | - | TextField() | longtext | 表示不定长的字符数据 | - |
注:更多字段类型说明参考官方文档
- 字段选项
- 字段选项用于指定创建列的额外的信息;允许出现多个字段选项,多个选项之间用逗号隔开
字段选项 | ????说明???? |
---|
primary_key | 如果设置为True,表示该列为主键,如果指定一个字段为主键,则此数据库不会创建id字段 | blank | 设置为True时,该字段可以为空(在管理后台中编辑数据的时候可以为空),设置为False时,该字段时必须填写的 | null | 设置为True时,表示该列值允许为空,默认为False,如果此项为False时建议加入default选项来设置默认值 | default | 设置所在列的默认值,如果字段null=false建议添加此项 | db_index | 设置为True时,表示为该列增加索引 | unique | 设置为True时,表示该字段在数据库中的值必须是唯一不能重复出现的 | db_column | 指定列的名称,如果不指定的话则采用类属性名作为列名 | verbose_name | 设置此字段在admin界面显示的名称,不设置则默认以类属性名显示 |
注:更多字段选项说明参考官方文档
-
模型类 - Meta类(控制表相关属性)
- 定义:使用内部Meta类来给模型赋予属性,Meta类下有很多内建的类属性,可对模型类做一些控制
- 常见Meta类的内建属性:
属性 | 作用 |
---|
db_table=‘数据表名’ | 修改该模型所用的数据表的名称,设置完成后需要立马更新同步数据库 | verbose_name=‘单数名’ | 给模型一个易于理解的名称(单数),用于显示在/admin管理界面中 | verbose_name_plural=‘复数名’ | 该对象复数形式的名称,用于显示在/admin管理界面中 |
-
创建数据 -> 创建数据中每一条记录就是创建一个数据对象
-
查询数据 -> 数据库的查询也需要使用管理器对象进行(可用QuerySet对象.query显示ORM转换为SQL语句的查询方法) -
通过MyModel.objects管理器方法调用查询方法
方法 | 用法 | ?????作用????? | 返回值 |
---|
all() | MyModel.objects.all() | 查询MyModel表中的所有数据,等同于select * from table | QuerySet容器对象,内部存放MyModel实例 <QuerySet [<Book: Book object (1)>, <Book: Book object (3)>]> | values (‘列1’, ’列2‘) | MyModel.objects.values() | 查询部分列的数据并返回,等同于select 列1,列2 from table | QuerySet容器对象,内部存放字典,每个字典代表一条数据;格式为{‘列1’:值1,‘列2’:值2} | values_list (‘列1’, ’列2‘) | MyModel.objects.values_list() | 返回元组形式的查询结果,等同于select 列1,列2 from xxx | QuerySet容器对象,内部存放元组,会将查询出来的数据封装到元组中,再封装到查询集合QuerySet中,如果需要将查询结果取出,需要使用索引的方式取值 | order_by (‘列1’, ’列2‘) | MyModel.objects.order_by() | 与all()方法不同,它会用SQL语句的ORDER BY子句对查询结果进行根据某个字段选择性的进行排序,默认是按照升序排序,降序排序需要在列前面增加’-‘表示 | QuerySet容器对象,内部存放MyModel实例,将实例按指定字段进行排序(等同于MyModel.objects.all().order_by(‘列1’)) | filter(条件) | MyModel.objects.filter(属性1=值1,属性2=值2)多个属性在一起时为”与“关系 | 返回包含此条件的全部的数据集 | QuerySet容器对象,内部存放MyModel实例 | exclude(条件) | MyModel.objects.exclude(条件)多个属性在一起时为”与“关系 | 返回不包含此条件的全部的数据集 | QuerySet容器对象,内部存放MyModel实例 | get(条件) | MyModel.objects.get(条件) | 返回满足条件的唯一一条数据,查询结果多余一条数据则抛出Model.MultipleObjectsReturned异常,查询结果如果没有数据则抛出Model.DoesNotExist异常 | QuerySet容器对象,内部存放MyModel实例 |
- 查询谓词
- 定义:做更灵活的条件查询时需要使用查询谓词
- 说明:每一个查询谓词是一个独立的查询功能
- 语法:类属性__查询谓词
查询谓词 | 说明 | 示例 |
---|
__exact | 等值匹配 | Author.objects.filter(id__exact==1) 等同于select * from author where id = 1 | __contains | 包含指定值 | Author.objects.filter(name__contains='w') 等同于select * from author where name like '%w%' | __startwith | 以xxx开始 | Author.objects.filter(name__startwith='w') 等同于select * from author where name like 'w%' | __endwith | 以xxx结束 | Author.objects.filter(name__endwith='w') 等同于select * from author where name like '%w' | __gt | 大于指定值 | Author.objects.filter(age__gt=50) 等同于select * from author where age > 50 | __gte | 大于等于指定值 | Author.objects.filter(age__gte=50) 等同于select * from author where age >= 50 | __lt | 小于指定值 | Author.objects.filter(age__lt=50) 等同于select * from author where age < 50 | __lte | 小于等于指定值 | Author.objects.filter(age__lte=50) 等同于select * from author where age <= 50 | __in | 查找数据是否在指定范围内 | Author.objects.filter(country__in=['中国','日本','韩国']) 等同于select * from author where country in ['中国','日本','韩国'] | __range | 查找数据是否在指定的区间范围内 | Author.objects.filter(age__range=(30,50)) 等同于select * from author where author between 35 and 50 |
拓展知识
- 自定义QuerySet中的输出格式:
def __str__(self):
return '%s_%s_%s_%s'%(self.title,self.price,self.pub,self.market_price)
>>> from books.models import Book
>>> a1 = Book.objects.all()
>>> a1
<QuerySet [<Book: python_20.00_清华大学出版社_25.00>, <Book: Django_70.00_清华大学出版社_75.00>, <Book: JQuery_90.00_机械工业出版社_85.00>, <B出版社_65.00>, <Book: HTML5_90.00_清华大学出版社_105.00>]>
数据库操作常见问题汇总
- 问题1:当执行python manage.py makemigrations 出现如下迁移错误
You are trying to add a non-nullable field 'pub' to book without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit, and let me add a default in models.py
Select an option:
- 当对模型类新添加一个字段时可出现该错误
- 原理是添加新字段后,数据库不知道原来已有数据对于新建字段该如何赋值,所以新增字段时,必须要添加default默认值
- 如果选择选项1)提供一个临时默认值,这个默认值会初始化到迁移文件中,不会体现在模型类中,别人在代码维护的时候出现黑匣子,导致有些现象与代码不符
- 选择选项2)退出生成迁移文件过程,重新修改models.py,新增加一个default=xxx的默认值(推荐使用第二种方法)
- 问题2:数据库迁移文件混乱,数据库中的django_migrations表记录了migrate的所有记录,当出现多人协作时,每个人的迁移文件版本不同,在提交时就会出现迁移文件混乱
- 问题分析:数据库中的django_migrations表记录了migrate的所有记录,项目各应用中的migrate文件应与之相对应
- 问题改善( 只针对本地测试时):
- 删除所有migrations里所有的000?_xxx.py(init.py文件除外)
- 删除数据库
sql>drop database mywebdb; - 重新创建数据库
sql>create datebase mywebdb default charset=utf8; - 重新生成migrations里所有的000?_xxx.py
python manage.py makemigrations - 重新更新数据库
python manage.py migrate
六、admin管理后台
6.1 admin配置步骤
- 创建后台管理账号 - 该账号为管理后台最高权限账号
6.2 注册自定义模型类
- 注册步骤:
- 在子应用app文件夹中的admin.py文件中导入注册要管理的模型类,如:
from .models import Book - 调用 admin.site.register 方法进行注册 如:
admin.site.register(自定义模型类) - 注册后会在admin首页显示注册的自定义模型类
- 点击第二个Books 进入book管理页面,管理页面中显示的Book信息与在定义Book模型类时,与定义的def__str__(self)方法中的显示样式一致
- 模型管理器类
-
作用:为后台管理界面添加便于操作的新功能 -
说明:后台管理器继承自django.contrib.admin 里的ModelAdmin类 -
使用方法:1.在子应用app下的admin.py文件中定义模型管理器类,模型管理器类的名字可自定义,一般按照一个模型对应一个模型管理器类 class XXXXManager(admin.ModelAdmin):
"自定义模型类属性"
-
绑定注册模型管理器和模型类: from django.contrib import admin
from .models import *
admin.site.register(YYYY,XXXXManager)
-
常用模型管理器类的类属性
类属性 | 说明 | 示例 |
---|
list_display=[field1,field2…] | 用于控制哪些字段会显示在Admin的修改列表页面中 | list_display = ['id','title','pub','price'] | list_display_links=[field1] | 控制list_display中的字段,哪些可以链接到修改页,默认情况下link加在id上 | list_display_links = ['title'] | list_filter=[field1,field2…] | 添加过滤器(指定的字段作为过滤条件),添加后会在页面的右侧显示过滤器 | list_filter = ['pub'] | search_fields=[field1,field2…] | 添加搜索框(指定的字段可进行模糊查询) | search_fields = ['title'] | list_editable=[field1,field2…] | 用于添加可在列表页编辑的字段,添加的字段必须在list_display中,并且该字段不能在list_display_links中 | list_display = ['id','title','pub','price'] |
-
例
from django.contrib import admin
from django.contrib import admin
from .models import Book, Author
admin.site.register(Book)
class AuthorManager(admin.ModelAdmin):
list_display = ['id','name','age']
list_editable = ['age']
class Meta:
verbose_name = '作者'
verbose_name_plural = verbose_name
admin.site.register(Author,AuthorManager)
更多关于模型管理器类的类属性参考官方文档
6.3 关系映射
- 定义:在关系型数据库中,通常不会把所有数据都放在同一张表中,不易于扩展
- 常见的关系映射有:
- 一对一映射:一个身份证对应一个人
- 一对多映射:一个班级可以有多个学生
- 多对多映射:一个学生可以报多个课程,一个课程可以有多个学生学习
-
一对一【创建表】:
- models.CASCADE: 级联删除;Django 模拟了 SQL 约束 ON DELETE CASCADE 的行为,并删除包含 ForeignKey 的对象,。
- models.PROTECT: 保护模式;抛出ProtectedError,以阻止被引用对象的删除;【等同于mysql默认的RESTRICT】
- SET_NULL: 置空模式,删除的时候,外键字段被设置为空,前提就是blank=True, null=True,定义该字段的时候,允许为空
- SET_DEFAULT: 设置默认值,删除的时候,外键字段设置为默认值,所以定义外键的时候必须加上一个默认值。
更多外键删除方式参考官方文档
- 示例:
from django.db import models
class Author(models.Model):
name = models.CharField(verbose_name='作者',max_length=11)
class AuthorWife(models.Model):
name = models.CharField(verbose_name='妻子',max_length=11)
author = models.OneToOneField(Author,verbose_name='妻子的丈夫',on_delete=models.CASCADE)
- 通常把外键类属性名就叫关联的模型类类名小写;在数据库中外键字段名跟类属性名也有区别,默认是以 类属性名 + ‘_’ +id
-
一对一【创建数据】
- 无外键的模型类创建数据
author1 = Author.objects.create(name = '王老师') - 有外键的模型类创建数据
- wife1 = AuthorWife.objects.create(name=‘王夫人’,author=author1) #以类属性名创建数据,必须传入关联外键的实例对象
- wife1 = AuthorWife.objects.create(name=‘王夫人’,author_id=1) #直接用外键字段名,必须传入关联对象对应的主键值
-
一对一【查询数据】
- 正向查询:直接通过外键属性查询 [通过有外键的表查询没有外键的表]
from .models import AuthorWife
wife = Wife.objects.get(name='王夫人')
print(wife.name,'的老公是',wife.author.name)
- 反向查询:没有外键属性的一方,可以调用反向属性(有外键的模型类类名)查询到关联的另一方,反向关联属性为
实例对象.引用类名(小写) ,如:作家的反向引用为’作家对象.wife’;当反向引用不存在时,则会触发异常author1 = Author.objects.get(name='王老师')
author1.wife.name
-
一对多【创建表】
- 定义:表示事物间存在一对多的对应关系(如:一个学校对应多个班级,一个班级对应多个学生);一对多需要明确出具体角色,在多表上设置外键
- 语法:
- 当一个A类对象关联多个B类对象时;Foreignkey必须指定on_delete模式
class A(models.Model):
···
class B(models.Model):
属性 = models.Foreignkey("一"的模型类,on_delete=xxx)
from django.db import models
class Publisher(models.Model):
"""出版社【一】"""
name = models.CharField('名称',max_length=50,unique=True)
class Book(models.Model):
"""书【多】"""
title = models.CharField('书名',max_length=50)
publisher = models.ForeignKey(Publisher,on_delete=models.CASCADE)
-
一对多【创建数据】
-
一对多【查询数据】
-
正向查询:直接通过外键属性查询 [通过有外键的表查询没有外键的表]
abook = Book.objects.get(id=1)
print(abook.title,'的出版社是',abook.publisher.name)
-
反向查询:没有外键属性的一方,可以调用反向属性(有外键的模型类类名(小写)_set )查询到关联的另一方 pub1 = Publisher.objects.get(name='清华大学出版社')
books = pub1.book_set.all()
print("清华大学出版社出版的书有:")
for book in books:
print(book.title)
-
多对多【创建表】
- 定义:多对多表达对象之间多对多复杂关系,如:一个学生可以报名多门课程,一门课程可以对应多名学生
- 实现方式:
- MySQL中创建多对多需要依赖第三张表来实现
- django中无需手动创建第三张表,django自动完成
- 语法:在关联的两个类中的任意一个类增加一个多对多属性(为便于记忆,属性名可用所关联的模型类小写的复数形式)
属性名 = models.ManyToManyField(MyModel) #MyModel:多对多关联的模型类
- 示例
class Author(models.Model):
"""作家模型类"""
name = models.CharField('作家',max_length=20)
def __str__(self):
return self.name
class Book(models.Model):
"""书模型类"""
title = models.CharField('书名',max_length=20)
authors = models.ManyToManyField(Author)
def __str__(self):
return self.title
-
多对多【创建数据】
- 方案一:先创建无多对多属性的数据表在关联有多对多属性的数据表,利用反向属性创建
author1 = Author.objects.create(name='王老师')
author2 = Author.objects.create(name='李老师')
book1 = author1.book_set.create(title='python')
author2.book_set.add(book1)
- 方案二:先创建有多对多属性的表再关联无多对多属性的数据表
book = Book.objects.create(title='python1')
author3 = book.authors.create(name='郭老师')
book.authors.add(author1)
-
多对多【查询数据】
七、会话保持
- 会话
- 定义:从打开浏览器访问一个网站,到关闭浏览器结束此次访问,称之为一次会话
- HTTP协议是无状态的,导致会话状态难以保持
- Cookies和Session就是为了保持会话状态而诞生的两个存储技术
7.1 Cookies
- cookies定义:是保存客户端浏览器上的存储空间
- Cookies特点
- cookies在浏览器上是以键值对的形式进行存储的,键和值都是以ASCII字符串的形式存储(不能是中文字符串)
- 存储的数据带有生命周期
- cookies中的数据是按域存储隔离的,不同的域之间无法访问
- cookies的内部的数据会在每次访问此网址时都会携带到服务器端,如果cookies过大会降低响应速度
- Cookies的使用 - 存储
- 语法:
HttpResponse.set_cookie(key,value='',max_age=None,expires=None)
- key:cookie 的名字
- value:cookie的值
- max_age:cookie存活时间,以秒为单位
- expires:具体的过期时间
- 当不指定max_age和expires时,关闭浏览器时此数据失效
- 创建cookie示例:
response = HttpReponse('已添加名为my_var1的cookie')
response.set_cookie('may_var1','123',3600)
return response
- 修改cookie示例:
response = HttpResponse('已修改名为my_var1的cookie')
response.set_cookie('may_var1','456',3600*2)
return response
- cookies 使用 - 删除&获取
- 删除cookies:通过
HttpResponse.delete_cookie(key) - 删除指定的key的cookie,如果key不存在则什么也不发生
- 获取cookies:通过request.COOKIES绑定的字典(dict)来获取客户端的COOKIES数据;
value = request.COOKIES.get('cookies名','默认值') #可用get()方法获取也可用索引方式获取
7.2 session
7.3 缓存
-
缓存
- 定义:缓存是一类可以更快的读取数据的介质的统称,也指其他可以加快数据读取的存储方式,一般用来存储临时数据,常用介质是读取速度很快的内存
- 意义:视图渲染有一定的成本,数据库的频繁查询过高;所以对于低频变动的页面可以考虑使用缓存技术,减少实际渲染次数,用户拿到响应的时间成本会更低
- 案例分析:
from django.shortcuts import render
def index(request):
book_list = Book.objects.all()
return render(request,'index.html',local())
- 优化思想:(转至官网)
given a URL, try finding that page in the cache
if the page is in the cache:
return the cached page
else:
generate the page
save the generated page in the cache (for next time)
return the generated page
- 缓存应用场景:
- 博客列表页
- 电商商品详情页
- 场景特点:缓存的地方,数据变动频率小
-
django中设置缓存 - 数据库缓存
- 将缓存的数据存储在数据库中
- 说明:尽管存储的介质没有更换,但是当把一次负责查询的结果直接存储到另一张表里,比如多个条件的过滤查询结果,可避免重复进行复杂查询,提升效率。
- 设置settings.py,添加CACHES配置
CACHES = {
'default':{
'BACKEND':'django.core.cache.backends.db.DatabaseCache',
'LOCATION':'my_cache_table',
'TIMEOUT':300
'OPTIONS':{
'MAX_ENTRIES':300,
'CULL_FREQUENCY':2
}
}
}
- 注:数据库缓存所使用的数据表需要手动创建,进入django shell执行
python manage.py createcachetable ,表名为CACHES里面设置的表名 - 数据库迁移后会在数据库中自动生成my_cache_table数据表,数据表中字段:
- cache_key: 缓存的键
- value: 缓存的值
- expires: 缓存的过期时间
-
django中设置缓存 - 本地内存缓存
-
django中设置缓存 - 文件系统缓存
-
django中使用缓存 - 整体缓存策略
- 在视图函数中:将整个视图函数全部缓存到数据库中,如需对某个视图进行增加缓存,直接在该视图上使用cache_page装饰器即可
from django.views.decorators.cache import cache_page
@cache_page(30) -> 单位s,当前视图缓存有效期
def my_view(request):
···
- 逻辑:第一次请求时将该视图的整个response存入到缓存中,下一次请求时就先检查缓存中是否有需要的response,如果有则不再进入视图函数中处理。
- 在路由中:在需要增加缓存的视图调用位置增加缓存
from django.views.decorators.cache import cache_page
urlpatterns = [
path('page/',cache_page(60)(my_view)),
]
- 缺点:1. 当对整个视图函数进行缓存后,下一次请求时,缓存未过期,请求数据会直接走缓存,如果视图函数中有相关权限校验,则无法进行校验(如:博客网站,博主访问后会将私有博客、公有博客一并进入缓存,访客直接访问的话就不会走视图函数进行身份验证,也能访问到博客的私有博客);2. 删除缓存成本过高,几乎无法得知缓存的key,无法进行主动删除,容易导致出现新旧数据不一致的情况(编辑后的数据无法及时更新到缓存中去)
-
django中使用缓存 - 局部缓存
-
浏览器缓存策略
- 强缓存:不会向服务器发送请求,直接从缓存中读取资源
- 响应头 - Expires
- 定义:缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点
- 样例:Expires : Thu,02 Apr 2030 05:14:08 GMT
- 响应头 - Cache-Control
- 作用:在HTTP/1.1 中,Cache-Control主要用于控制网页缓存,比如当
Cache-Control:max-age=120 代表请求创建时间后的120秒,缓存失效 - 说明:目前服务器都会带着这两个头同时响应给浏览器,浏览器优先使用Cache-Control
- 协商缓存 -> 在强缓存的基础上衍生出来的
- 强缓存中的数据一旦过期,还需要跟服务器进行通信,从而获取最新数据;当强缓存的数据是一些静态文件、大图片等这类比较费带宽且不易变化的数据,强缓存到期后,浏览器会去跟服务器协商,当前缓存是否可用,如果可用,服务器不必返回数据,浏览器继续使用原来缓存的数据,如果文件不可用,则返回最新数据
- 响应头:Last-Modified 和 If-Modifield-Since 请求头
- Last-Modified为文件的最近修改时间,浏览器第一次请求静态文件时,服务器如果返回Last-Modified响应头,则代表该资源为需协商的缓存
- 当缓存到期后,浏览器将获取到的Last-Modified值作为请求头If-Modifield-Since的值,与服务器发请求协商,服务端返回304响应码[响应体为空],代表缓存继续使用,200响应码代表缓存不可用[响应体为最新资源]
- 缺点:不够精确,Last-Modified是根据修改时间来进行判断缓存是否可继续使用,单位为秒;如果文件的修改时间极短,Last-Modified就发现不了文件已经更改
- 响应头 ETag响应头和If-None-Match 请求头
- ETag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),只要资源有变化,ETag就会重新生成(唯一标识为哈希值)
- 缓存到期后,浏览器将ETag响应头的值作为If-None-Match请求头的值,给服务器发请求协商;服务器接到请求头后,比对文件标识,不一致则认为资源不可用,返回200响应码代表缓存不可用[响应体为最新资源],可用则返回304响应码
八、中间件
8.1 中间件的定义
- 定义:中间件是django请求/响应处理的钩子框架,它是一个轻量级的、低级的插件系统,用于全局改变django的输入或输出
- 中间件以类的形式体现
- 每个中间件组件负责一些特定的功能,例如:django包含一个中间件组件AuthenticationMiddleware,她使用会话将用户与请求关联起来
8.2 中间件使用
-
编写中间件:
- 中间件类必须继承自
django.utils.deprecation.MiddlewareMixin 类 - 中间件类必须实现下列五个方法中的一个或多个
process_request(self,request) :
- 作用:执行主路由之前被掉用,在每个请求上调用,返回None(请求通过)或者HttpResponse对象(请求不通过)
- 用途:过滤请求
process_view(self,request,callback,callback_args,callback_kwargs) :
- 作用:callback:为视图函数;callback_args:视图函数的位置参数,callback_kwargs:视图函数的关键字参数;调用视图之前被调用,在每个请求上调用,返回None(请求通过)或者HttpResponse对象(请求不通过)
- 用途:用于代码层面的替换和过滤,这个方法可以拿到视图函数的参数
process_response(self,request,response) :
- 作用:response:即是视图函数的响应对象;在所有响应返回浏览器之前被调用,在每个请求上调用,返回HttpResponse对象
process_exception(self,request,exception) :
- 作用:处理过程中抛出异常时被调用,返回一个HttpResponse对象
- 用途:用于一般用于捕获发生的异常,并将其邮件发送给开发人员
process_template_response(self,request,response) :
- 作用:在视图函数执行完毕,且视图函数返回的对象中包含render方法时被调用;该方法需要返回实现了render方法的响应对象
- 注:中间件中的大多数方法在返回None时表示忽略当前操作进入下一项事件,当返回HttpResponse对象时,表示此请求结束,直接返回给客户端
-
注册中间件:
-
示例:
- 添加过滤请求次数中间件
from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin
import re
class MWare(MiddlewareMixin):
count_dict = {}
def process_request(self,request):
request_ip = request.META['REMOTE_ADDR']
request_url = request.path_info
if re.match(r'^/test',request_url):
times = self.count_dict.get(request_ip,0)
self.count_dict[request_ip]= times + 1
if times < 5:
return
else:
return HttpResponse("访问次数超过5次,请求失败")
else:
return
- 注册中间件
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'middleware.mymiddleware.MWare',
]
附django请求流程图
8.3 CSRF - 跨站伪造请求攻击
- 定义:某些恶意网站上包含链接、表单按钮或者JavaScript,它们会利用登陆过的用户在浏览器中的认证信息试图在你的网站上完成某些操作,这就是跨站请求伪造(CSRF:Cross-Site-Request-Forgey)
- CSRF 防范:
- django采用比对 ‘暗号’ 机制防范攻击
- 原理:
- Cookies中存储暗号1,模板中表单里藏着暗号2,用户只有在本网站下提交数据,暗号2 才会随着表单提交给服务器,django对比两个暗号,对比成功,则认为时合法请求,否则是违法请求 - 403响应码
- 配置步骤:
- 在settings.py文件中确认MIDDLEWARE中django.middleware.csrf.CsrfViewMiddleware是否打开
- 在模板中的form标签下添加如下标签
{% csrf_token %} - 示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CSRF-TEXT</title>
</head>
<body>
<form action="/test_csrf" method="post">
{% csrf_token %}
<input type="text" name="test">
<input type="submit" value="提交" name="key">
</form>
</body>
</html>
九、网页分页功能
- 分页定义
- 定义:分页是指在web页面有大量数据需要显示,为了阅读方便,在每页页中只显示部分数据
- 优点:方便阅读,减少数据提取量,减轻服务器压力
- django实现:
- django提供了Paginator类可以方便的实现分页功能
- Paginator类位于:
django.core.paginator 模块中 - Paginator对象
- 功能:负责分页数据整体管理
- 语法:
paginator = Paginator(object_list , per_page)
- 参数:
- object_list:需要分页数据的对象列表(也可以是QuerySet对象)
- per_page:每页数据条数
- 返回值:
- Paginator对象的属性:
- count:需要分页数据的对象总数
- num_pages:分页后的页面总数
- page_range:从1开始的range对象,用于记录当前页码数
- per_page:每页显示数据的个数
- Paginator对象的方法
- paginator对象.page(number):
- 参数number为页码信息(从1开始)
- 返回当前number页对应的页信息
- 如果提供的页不存在,则抛出InvalidPage异常
- InvalidPage异常
- 总的异常基类,包含以下两个异常子类
- PageNotAnInteger:向page()中传入的number不是整数的值时抛出
- EmptyPage:当向page()中传入的number是一个有效值,但是那个页面上没有任何对象时抛出
- page对象
- 定义:负责具体某一页的数据的管理
- 语法:paginator对象的page()方法返回的就是page对象
- page = paginator.page(页码)
- 属性:
- object_list:当前页上所有数据对象的列表
- number:当前页的序号,从1开始
- paginator:当前page对象相关的paginator对象(查看是哪个paginator对象生成的page对象)
- has_next():如果有下一页,返回True
- has_previous():如果有上一页,返回True
- has_other_pages():如果有上一页或下一页返回True
- next_page_number():返回下一页的页码,如果下一页不存在,抛出InvalidPage异常
- previous_page_number():返回上一页的页码,如果上一页不存在,抛出InvalidPage异常
- 示例
from django.urls import path
from . import views
urlpatterns = [
path('test_page', views.test_page),
]
def test_page(request):
p = request.GET.get('page',1)
data_list = [str(i) for i in range(20)]
paginator = Paginator(data_list,per_page=2)
page = paginator.page(int(p))
return render(request,'test_page.html',locals())
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>分页显示</title>
</head>
<body>
{% for p in page.object_list %}
{{ p }}
{% endfor %}
{% if page.has_previous %}
<a href="/test_page?page={{ page.previous_page_number }}"> 上一页</a>
{% else %}
<p>上一页</p>
{% endif %}
{% for pg in paginator.page_range %}
{% if pg == page.number %}
{{ pg }}
{% else %}
<a href="test_page?page={{ pg }}">{{ pg }}</a>
{% endif %}
{% endfor %}
{% if page.has_next %}
<a href="/test_page?page={{ page.next_page_number }}"> 下一页</a>
{% else %}
<p>下一页</p>
{% endif %}
</body>
</html>
十、生成CSV文件
- csv文件定义:
- 定义:逗号分割值(Comman-Separated values),有时也称为字符分割值,因为分割字符也可以不是逗号,其文件以纯文本形式存储表格数据(数字和文本)
- 说明:可被常见制表工具,如Excel等直接读取
- Python中生成csv文件
- Python提供了内建库 - CSV;可直接通过该库操作csv文件
- 案例如下:
import csv
with open('eggs.csv','w',newline='') as csvfile:
writer = csv.writer(csvfile)
writer.writerow(['a','b','c'])
- django中实现
- 在网站中实现下载csv文件,注意如下
- 响应类型Content-Type类型需要修改为text/csv,这告诉浏览器该文档是csv文件,而不是HTML文件(响应类型在HttpResponse对象中修改)
- 响应会获得一个额外的Content-Disposition 标头,其中包含csv文件的名称,它将被浏览器用于开启"另存为···"对话框
- 案例:
import csv
from django.http import HttpResponse
from django.shortcuts import render
from books.models import Book
def make_csv_view(request):
response = HttpResponse(content_type = 'text/csv')
response['Content-Disposition'] = 'attachment;filename="mybook.csv"'
all_book = Book.objects.all()
writer = csv.writer(response)
writer.writerow(['id','title'])
for b in all_book:
writer.writerow([b.id,b.book_name])
return response
十一、用户认证系统
-
定义:django带有一个用户认证系统,用于处理用户账号、组、权限以及基于cookie的用户会话 -
用户可直接使用django自带的用户表 -
详细信息参考官方文档 -
基本字段:模型位置:from django.contrib.auth.models import User
Field | Type | Null | Key | Default | Extra | info |
---|
id | int(11) | NO | PRI | NULL | auto_increment | id | password | varchar(128) | NO | | NULL | | 密码 | last_login | datetime(6) | YES | | NULL | | 上一次的登陆时间 | is_superuser | tinyint(1) | NO | | NULL | | 是否是管理员账号(/admin) | username | varchar(150) | NO | UNI | NULL | | 用户名 | first_name | varchar(150) | NO | | NULL | | 名 | last_name | varchar(150) | NO | | NULL | | 姓 | email | varchar(254) | NO | | NULL | | 邮箱 | is_staff | tinyint(1) | NO | | NULL | | 是否可以访问admin管理界面 | is_active | tinyint(1) | NO | | NULL | | 是否是活跃用户,默认为True, 一般不删除用户,而是将用户的is_active设为False | date_joined | datetime(6) | NO | | NULL | | 用户创建的时间 |
-
模型操作 - 创建用户
- 创建普通用户
from django.contrib.auth.models import User
user = User.objects.create_user(username, email, password)
- 创建超级用户
from django.contrib.auth.models import User
user = User.objects.create_superuser(username, email, password)
-
模型操作 - 删除用户
-
模型操作 - 校验密码
-
模型操作 - 修改密码 from django.contrib.auth.models import User
try:
user = User.objects.get(username='用户名')
user.set_password('654321')
user.save()
return HttpResponse("密码修改成功!")
except:
return HttpResponse("密码修改失败!")
-
模型操作 - 登陆状态保持
-
模型操作 - 登陆状态校验
-
模型操作 - 登陆状态取消 from django.contrib.auth import logout
def logout_view(request):
logout(request)
-
扩展内建用户表的字段
- 方案一:通过建立新表,跟内建表做1对1映射
- 方案二:继承内建的抽象User模型类,重写类属性
- 方案二实现步骤:
- 添加新的应用
- 定义模型类 继承自AbstractUser
- 在settings.py文件中指明AUTH_USER_MODEL = ‘应用名.类名’
- 注意:此操作要在第一次执行migrate之前进行
- 示例:
from django.db import models
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
phone = models.CharField(max_length = 11,default = '')
AUTH_USER_MODEL = 'user.UserInfo'
添加用户(与原User使用方法相同)from user.models import UserInfo
UserInfo.onjects.create_user(username='xxx',password='123456',phone='13344445555')
十二、文件上传功能
- 定义:用户可以通过浏览器将图片等文件传至网站
- 场景:用户上传头像、上传流程性的文件【PDF、TXT等】
- 上传规则 - 前端【HTML】
- 文件上传必须为POST提交方式
- 表单
<form> 中文件上传时必须带有enctype="multipart/form-data" 时才会包含文件内容数据 - 表单中用
<input type="file" name = "xxx"> 标签上传文件 - 上传规则 - 后端【django】
- 获取上传文件的内容:在视图函数中用
request.FILES 获取文件框的内容 - 语法:file = request.FILES[‘XXX’]
- 说明:
- FILES的key对应页面中的file框的name值
- file:为绑定文件流对象
- file.name:获取文件名
- file.file:为文件的字节流数据
- form表单中的文本框内容仍然是用
request.POST 去取
- 配置文件的访问路径和存储路径
- 在settings.py中设置MEDIA相关配置;django把用户上传的文件统称为media资源
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
- 手动绑定MEDIA_URL和MEDIA_ROOT
from django.conf import settings
from django.conf.urls.static import static
urlpatterns += static(settings.MEDIA_URL,document_root = settings.MEDIA_ROOT)
- 说明:使用static将MEDIA_URL与MEDIA_ROOT进行绑定,django接到MEDIA_URL开头的路由请求后,去MEDIA_ROOT路径查找资源
- 文件写入方案1:传统的open方式
@csrf_exmpt
def upload_view(request):
if request.method == 'GET':
return render(request,'test_upload.html')
elif request.methof == 'POST':
a_file = request.FILES['myfile']
print("上传的文件名是:",a_file.name)
filename = os.path.join(settings.MEDIA_ROOT,a_file.name)
with open(filename,'w') as f:
data = a_file.file.read()
f.write(data)
return HttpResponse("接收文件:",a_file.name + "成功")
- 文件写入方案2:diango实现 - 借助ORM
- 用法:借助模型类中FileField(upload=‘子目录名’) ; 该字段用于存储文件的相对路径,如果文件名已存在,django会自动为文件重命名
@csrf_exempt
def upload_view_dj(request):
if request.method == 'GET':
return render(request,'test_upload.html')
elif request.method == 'POST':
title = request.POST['title']
a_file = request.FILES['myfile']
Content.objects.create(desc=title,myfile=a_file)
return HttpResponse("----upload is ok----")
- 上传示例:
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
from django.conf.urls.static import static
from django.urls import path
from test_upload import views
from django.conf import settings
urlpatterns = [
path('test_upload',views.upload_view)
]
urlpatterns += static(settings.MEDIA_URL,document_root = settings.MEDIA_ROOT)
from django.db import models
class test_upload(models.Model):
title = models.CharField(verbose_name='标题',max_length=20)
mfile = models.FileField(verbose_name='media')
from django.http import HttpResponse
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
from .models import test_upload
@csrf_exempt
def upload_view(request):
if request.method == 'GET':
return render(request,'test_upload.html')
elif request.method == 'POST':
title = request.POST['title']
m_file = request.FILES['myfile']
test_upload.objects.create(title=title,mfile=m_file)
return HttpResponse('----文件上传成功----')
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
<form action="test_upload" method="post" enctype="multipart/form-data">
<input type="text" name="title">
<input type="submit" value="提交">
<input type="file" name="myfile">
</form>
</body>
</html>
十三、发送邮件
- 业务场景
- 业务警告(捕捉异常可用traceback.format_exc())、邮箱验证、密码找回
- 邮件相关协议
- SMTP
- 全称是“simple mail transfer protocol”,即简单邮件传输协议(25号端口)
- 功能:它是一组用于从源地址到目的地址传输邮件的规范,通过它来控制邮件的中转 -> 属于推送协议
- IMAP
- 全称是“Internet mail Access protocol”,即交互式邮件访问协议,是一个应用层协议(143端口)
- 功能:用来从本地邮件客户端(Outlook express、Foxmail等)访问远程服务器上的邮件 -> 属于拉取协议
- POP3
- 全称是“Post office protocol 3”,即邮局协议的第三版本,是TCP/IP协议族中的一员(110端口)
- 功能:主要用于支持使用客户端远程管理在服务器上的电子邮件 -> 属于拉取协议
- IMAP VS POP3:两者均为拉取型协议,负责从邮件服务器中下载邮件
- IMAP 具备摘要浏览功能,可预览部分摘要,再下载整个邮件
- IMAP 为双向协议,客户端操作可反馈给服务器
- POP3 必须下载全部邮件,无摘要功能
- POP3 为单向协议,客户端操作无法同步到服务器
- django发送邮件
- django中配置邮件功能,主要为SMTP协议,负责发送邮件
- 原理:
- 给django授权一个邮箱
- django用该邮箱给对应收件人发送邮件
- django.core.mail封装了电子邮件的自动发送SMTP协议
- 授权步骤:
- 登陆用于发送邮件的邮箱
- 获取用于发送邮件的邮箱授权码(IMAP/SMTP、POP3/SMTP均可)
- django中配置:
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.qq.com'
EMAIL_PORT = 25
EMAIL_HOST_USER = 'XXXXXXXX@QQ.COM'
EAMIL_HOST_PASSWORD = '********'
EMAIL_USE_TLS = False
- 函数调用
from django.core import mail
mail.send_mail(
subject,
message,
from_email,
recipient_list=['xxxxx@qq.com','xxxxx@163.com'],
)
十四、项目部署
- 基础概念:项目部署是指在软件开发完毕后,将开发机器上运行的软件实际安装到服务器上进行长期运行
- 部署步骤
- 在安装机器上安装和配置同版本的环境(Python、数据库等)
- django项目迁移
sudo scp /home/path1········/mysite1 root@xx.xx.xx.xx:/home/root/path2 path1 为本地项目路径,path2为迁移到服务器中存放的路径 - 用uWSGI替代python manage.py runserver方法启动服务器
- 配置nginx反向代理服务器
- 用nginx配置静态文件路径,解决静态文件路径问题
- uWSGI
-
定义:是WSGI中的一种,它实现了http协议、WSGI协议以及uwsgi协议,uWSGI功能完善,支持协议众多,主要以学习配置为主 -
uWSGI安装:ubuntu执行 sudo pip install uwsgi==2.0.18 -i https://pypi.tuna.tsinghua.edu.cn/simple/
- 检查是否安装成功:
sudo pip freeze|grep -i 'uwsgi' 如果成功安装则会显示对应的版本 -
配置uWSGI
-
启动uwsgi:切换到uWSGI配置文件所在目录执行uwsgi --ini uwsgi.ini ‘–ini’ 为初始化uwsgi -
停止uwsgi:切换到uWSGI配置文件所在目录执行uwsgi --stop uwsgi.pid -
查看是否启动成功:ps aux|grep 'uwsgi' 查看是否有进程启动,无论是启动还是关闭,都需要执行此命令确认是否符合预期 -
注意:启动成功后,进程在后台执行,所有日志均输出在配置文件所在目录的uwsgi.log中;Django中代码有任何修改,均需要重新启动uwsgi -
uWSGI常见问题汇总
问题 | 原因 | 解决方案 |
---|
启动失败 | 端口被占用,其它进程占用了uWSGI启动的端口 | 执行sudo lsof -i:端口号 查询出具体进程;执行sudo kill -9 端口号 ,杀掉进程,重新启动uWSGI即可 | 停止失败 | 重复启动uWSGI导致pid文件中的进程号失准 | ps 出uWSGI进程,手动kill掉 |
- Nginx
-
定义:nginx是轻量级的高性能web服务器,提供了诸如http代理和反向代理、负载均衡等一系列重要特性;采用c语言编写,执行效率高 -
作用:
- 负载均衡,多台服务器轮流处理请求
- 反向代理:nginx反向代理可以以uwsgi协议、http协议转发给uWSGI
- 正向代理与反向代理介绍
- 正向代理:代理的对象是客户端,隐藏了真实的客户端,服务端不知道真实的客户端是谁
- 反向代理:代理的对象时服务端,隐藏了真实的服务端,客户端不知道真实的服务端是谁
- 负载均衡:nginx将请求任务平衡、分摊到多个服务器上进行处理
-
原理:客户端请求nginx,再由nginx将请求转发uWSGI运行的django -
安装:sudo apt install nginx (更换国内源 : vim /etc/apt/sources.list 更改国内源 sudo apt-get update ) -> 安装完毕后在ubuntu终端中输入nginx -v 会显示nginx相应的版本号;安装完毕后nginx会自动启动,并占用80端口 -
配置:修改nginx的配置文件:/etc/nginx/sites-enabled/default ; sudo vim default server{
location / {
uwsgi_pass 127.0.0.1:8000;
include /etc/nginx/uwsgi_params;
}
}
-
启动/停止 nginx
sudo /etc/init.d/nginx/ start|stop|restart|status 或sudo service nginx start|stop|restart|status - start:启动、stop:停止、restart:重启
- nginx配置只要修改,就需要进行重启,否则配置不生效
-
uWSGI修改配置:nginx负责接收请求,并把请求转发给后面的uWSGI,此模式下,uWSGI需要以socket模式启动;
[uwsgi]
socket = 127.0.0.1:8000
-
常见问题排查:
- 查日志:
- nginx 日志位置:
- 异常日志:/var/log/nginx/error.log
- 正常访问信息:/var/log/nginx/access.log
- uwsgi 日志位置:项目同名目录下/uwsgi.log
- 访问127.0.0.1:80地址,返回502响应;分析
502响应代表nginx反向代理配置成功,但是对应的uWSGI未启动 - 访问127.0.0.1:80/url,返回404响应:1. 路由的确不在django配置中;2. nginx配置错误,未禁止掉try_files
-
静态文件配置
- 原因:使用nginx反向代理后,django不会再进行静态文件管理,
- 创建新路径用于存放django的所有静态文件
/home/···/项目名_static/ - 在django的settings.py中添加新配置
STATIC_ROOT = '/home/···/项目名_static/static'
- 进入项目根目录,执行
python manage.py collectstatic 执行该命令后,django会将项目中的所有静态文件收集到STATIC_ROOT中,包括django内建的静态文件
- 自定义404/500页面
- 在模板文件夹内添加404.html模板,当视图触发Http404异常时将会自动显示;404.html仅在发布版中(即settings.py中的DEBUG=False时)才起作用,当向应处理函数触发Http404异常时就会跳转到404界面
- 邮箱报警配置
- 作用:当正式服务器上代码运行有报错时,可将错误追溯信息发送至指定的邮箱
- 在settings.py中 - 在基础邮箱授权后,添加如下配置
DEBUG = False
ADMINS = [('收件人姓名','收件人邮箱'),('收件人2姓名','收件人2邮箱')]
SERVER_EMAIL = 'email配置中的邮箱'
- 过滤敏感信息:报错邮件中会显示一些错误的追踪,这些错误追踪会出现如password等敏感信息,django已经将配置文件中的敏感信息过滤修改为多个星号,但是用户自定义的视图函数需要用户手动过滤敏感信息;可过滤如下信息(局部变量、POST提交数据)
- 过滤局部变量
from django.views.decorators.debug import sensitive_variables
@sensitive_variables('user','pw','cc')
def process_info(user):
pw = user.password
cc = user.credit_card_number
name = user.name
- 说明
- 若报错邮件中牵扯到user,pw,cc,等局部变量的值,则会将其替换称******,而name变量还显示其真实值
- 多个装饰器时,需要将其放在最顶部
- 若不传参数,则过滤所有局部变量的值
|