基本
- 安装
pip install django==2.2.24
- 以下内容都是根据
2.2.24
版本
- 以下内容都是根据
- 运行
python manage.py
列出所有Django
命令
创建项目
- 创建项目:
django-admin startproject name
- 运行项目微型服务器:
python manage.py runserver
- 这服务器只是测试用 不要将他用在正式部署上
- 监听指定端口:
python manage.py runserver 8888
- 监听指定 IP 和端口:
python manage.py runserver 0.0.0.0:8888
配置文件
修改默认为中文:
LANGUAGE_CODE = 'zh-Hans'
修改时区为中国时区:
TIME_ZONE = 'Asia/Shanghai'
要请求
POST
请求需要关闭中间件中scrf
验证
Django 导入配置文件信息
from django.conf import settings
修改默认数据库为 mysql
- 在任意一个 init 中修改默认连接组件为 mysql 组件(一般在主项目 init)
from pymysql import install_as_MySQLdb
install_as_MySQLdb()
- 在配置文件中修改 DATABASES
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'django',
'USER': 'root',
'PASSWORD': 'root',
'HOST': '127.0.0.1',
'PORT': '3306',
'CHARSET': 'utf8',
}
}
修改默认数据库出现错误:
- 报错
AttributeError: 'str' object has no attribute 'decode'
- 打开最后一个文件将内部的
- query = query.decode(errors=’replace’)
- 改为
- query = query.encode(errors=’replace’)
创建应用
- 应用表示单独的功能模块,如博客系统,或者公共记录的数据库
python manage.py startapp AppName
- 项目中创建应用,让每个应用有各自的功能,分布开发 方便维护。
- 创建后的应用要在配置文件中注册。
- 注册直接存储写上
app
名即可
- 注册直接存储写上
应该在应用中创建你的
templates
文件夹- 并且配置文件中的
APP_DIRS
需要为True
- 如果在主
templates
文件夹中存在相同的html
文件Django
会优先拿取主文件夹中的文件 - 如果外层不存在
index
文件夹,查找顺序为配置文件中INSTALLED_APPS
应用注册的顺序- 解决方式 在应用的
templates
文件夹中创建一个同应用名文件夹进行存储,在返回html
文件的时候返回文件夹名/html
文件即可
- 解决方式 在应用的
- 并且配置文件中的
URLS
path('page/\<int:page\>', views.page, name='page')
- 上面的
url
会将page/
后面的int
类型以page
别名的形式传递给视图函数,视图函数接受page
即是URL
传入的值 name
:别名,反向查找用到- 更多类型:
str
:匹配除了/以外的非空字符int
:匹配 0 或任意正整数,返回 intslug
:匹配由 ASCII 字母或者数字以及_和-组成的短标签path
:匹配非空字段,包括路径分隔符
- 上面的
re_path
- 以正则表达式的形式进行匹配
- 需要以正则中的分组进行书写
page/(?P\<name>\d+)
:将会匹配 page/后面带上一位以上数字的页面,并且以 name 为别名的形式传入后面的视图函数
Dango 中的请求
request.path_info
:url
字符串 去除了host
request.method
:一般都是get
和post
:http
请求方法request.GET
:QueryDict
:获取地址中所有get
请求数据:? id = 1
GET.get('参数名','默认值')
:获取参数名,如果没有,返回默认值GET.getlist('a')
:获取 URL 中所有的 a 的参数值列表
request.POST:QueryDict
:获取地址中所有POST
请求数据:? id = 1
request.FILES
:类似字典对象,包含上传的文件信息COOKIES:cookie
session
:当前会话body
:请求体scheme
:请求协议http https
- 完整请求路径:
request.get_full_path()
- 元数据 消息头:
request.META
- 客户端 IP:
request.META[REMOTE_ADDR]
- 客户端 IP:
Django 中的响应
响应对象
HttpResponse(content=响应体,content_type=响应体数据类型,status=状态码)
常用
Content-Type
状态类型常见的媒体格式类型如下:
text/htm
l : HTML 格式text/plain
:纯文本格式text/xml
: XML 格式image/png
:png 图片格式application/json
: JSON 数据格式application/octet-stream
: 二进制流数据(如常见的文件下载)multipart/form-data
: 需要在表单中进行文件上传时,就需要使用该格式- 更多格式百度 Content-Type 对照表
响应状态码:
- 200 请求成功
- 301 永久重定向 - 资源被永久转移到别的 URL
- 302 临时重定向
- 404 资源不存在
- 500 服务器内部错误
- Django 默认提供了各种错误请求的处理模块
Django 三板斧
- 重定向:
from django.http import HttpResponseRedirect
return HttpResponseRedirect('url')
- 返回 html,或者文本:
from django.http import HttpResponse
return HttpResponse(*)
- 返回网页:
from django.shortcuts import render
return render(request, 'bookstore/index.html', locals())
locals()
自动将函数当前的参数打包成一个字典传递给前端
Django 设计模式和模版层 MTV
- 模版
HTML
配置- 创建模版文件夹 项目目录下
/templates
- 专门存储
HTML
页面
- 专门存储
- 在
settings
中的TEMPLATES
配置- 1.
BACKEND
:指定模版引擎 - 2.
DIRS
:模版的搜索目录 - 3.
APP_DIRS
:是否在要应用(APP)中搜索模版文件 - 4.
OPTIONS
:有关模版的选项
- 1.
- 设置
DIRS
'DIRS': [os.path.join(BASE_DIR, 'templates')],
- 创建模版文件夹 项目目录下
模版层的使用
- 当写好 html 后即在视图函数中使用下面语句读入 html 并且返回给前端
方案 1 不推荐*
from django.template import loader
html = loader.get_template('**.html').render()
return HttpResponse(html)
- 方案 2(推荐)
from django.shortcuts import render
return render(request, '***.html', locals())
模版层和视图层的交互
return render(request, '***.html',dict)
- 在上面中最后一个参数传入一个字典
- 在视图层可以使用
{{ 变量名 }}
的语法进行使用变量
- 能传入前端的数据类型
- str int list tuple dict func obj
- 前端模版中调用和
Python
中的却别 - 普通:
{{ 变量名 }}
- 列表,元组:
{{ 变量名.index }}
- 字典:
{{ 变量名.key }}
- 对象 :
{{ 对象.方法 }}
- 函数 :
{{ 函数名 }}
- 传入函数对象,或者方法的时候都不需要加()进行调用
- locals():自动将当前函数中的局部变量打包成一个字典
摸板层的标签 模版语法
- 在模版中实现循环,判断等功能
key | Value |
---|---|
forloop.counter | 循环的当前迭代(1 索引) |
forloop.counter0 | 循环的当前迭代(0 索引) |
forloop.revcounter | 循环结束的迭代次数(1 索引) |
forloop.revcounter0 | 循环结束的迭代次数(0 索引) |
forloop.first | 如果这是第一次通过循环,则为真 |
forloop.last | 如果这是最后一次循环,则为真 |
forloop.parentloop | 对于嵌套循环,这是围绕当前循环的循环 |
模版的过滤器
- 定义:在模版中对变量的值进行改变,通过使用过滤器改变变量的输出和显示
{% for name in name_list %}
{% if forloop.first %} %%% {% endif %}
<p>{{ forloop.counter|add:"2" }}{{ name|upper }}</p>
{% if forloop.last %} &&& {% endif %}
{% empty %}
<P>这是没有数据的</P>
{% endfor %}
- 上面的语法中将
forloop.counter
的值通过过滤器add:"2"
将前面的 value 值增加了2
name
的输出通过upper
全部转为了大写- 部分语法
|lower
:转换为小写|upper
:转换为大写|safe
:默认不对变量内字符串进行 html 转义|add
:’n’:将 value 值增加 n
模版的继承
- 模版的继承可以使父模版的内容从,子继承父模版的内容,覆盖相应的块
- 父模版:
- 在父模版中定义
block
标签 表示可以被子模版修改的部分
- 在父模版中定义
- 子模版:
- 在子模版中继承父模版
(extends)
{% extends '父模版.html' %}
- 重写父模版内容
{% block block_name%}
- 子模版覆盖父模版的内容
{% endblock block_name%}
- 在子模版中继承父模版
- 注意:继承时服务器的动态内容无法继承
反向解析
在模版中进行反向解析
- 相对
url
和绝对url
- 绝对
url
就是全部url
都填上
- 绝对
- 1.相对 url:
/page/1
+ 这种url
会在主域名后面跟上 url 即完整 url 为:http://127.0.0.1:8000/page/1 - 2.相对 url:
page/1
+ 这种url
会搜索 url 的最后一个/在其后面组合 即http://127.0.0.1:8000/index/page/1
- 反向解析:
{% url '别名' '参数' %}
- 将会在路由中查找别名的路由,如果后面有参数,将会传递给参数
- 还可以
{% url '别名' 参数名字 = '参数' %}
的方式进行传参
静态文件配置
STATIC_URL = '/static/'
:默认存在- 然后在配置文件中设置
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
后创建一个文件夹名为 static 即可在内部存储静态文件 (css,js,images)
单独存储 注意路径
静态文件的使用
- 1.直接使用绝对路径
- 2.使用 / 相对路径
- 3.使用模版层访问(推荐)
- 首先加载:
{% load static %}
- 使用:
{% static 'image/name.jpg' %}
- 首先加载:
模型层及 ORM
- 直接在
Python Shell
操作Model
python manage.py shell
- 可以直接导入模型 使用模型进行操作 测试
- 模型主要:用于和数据库进行交互
- ORM:对象关系映射 面向对象的形式操作数据库
- 类 对应 数据库表
- 属性 对应 数据库字段
- 实例对象 对应 数据库行
class Book(models.Model): # ORM:类代表的是一张表
# 字符串类型,最大长度50,默认值空
title = models.CharField(verbose_name='book_name', max_length=50, default='') # ORM:类属性代表的是表中的字段
# 十进制类型,存储价格,最大长度7,小数点两位:00000.00
price = models.DecimalField(verbose_name='price', max_digits=7, decimal_places=2) # ORM:类属性代表的是表中的字段
- 创建完表后,需要将表迁移到数据库中
- 生成迁移文件:python manage.py makemigrations
- 迁移到数据库:python manage.py migrate
常用 ORM 基础字段及属性
- 字段属性
models.BooleanField() # 表示True,False
models.CharField(max_length=66) # 表示字符串,必须指定max_length()
models.DateField() # 表示时间,日期(可保存当前时间,第一次创建时间,自动存储当前时间)
models.DateTimeField() # (和上面的时间一样,更加精确)表示时间,日期(可保存当前时间,第一次创建时间,自动存储当前时间)
models.FloatField() # 浮点型(double)
models.DecimalField() # 十进制类型(一般存储钱)max_digits=7, decimal_places=2 总位数7,和小数点位数2 00000.00
models.EmailField() # 存储邮箱,存储是char类型,但ORM会自动处理
models.IntegerField() # 常规int类型
models.ImageField() # 存储图片的路径,数据库中是用(varchar(100))
models.TextField() # 存储大文本(数据库是longtext类型)表示不定长的文本类型
- 字段选项
# primary_key True:设置主键
# blank True:字段可以为空 False:不能为空(控制在django的admin控制后台是否可以为空提交)
# null True:列值允许为空,默认不为空, 通过default:设置选项默认值
# default 设置选项默认值(如果使用null建议添加此项)(新增字典必须给)
# db_index Trel:为该列增加索引
# unique True: 唯一索引,不能出现重复的值
# db_column 指定列的名称:不指定的话则才用属性名
# verbose_name 设置此字段在admin界面上显示的名称
# auto_now_add 创建或添加对象时的时间, 修改或更新对象时, 不会更改时间
# auto_now 凡是对对象进行操作(创建/添加/修改/更新),时间都会随之改变
修改模型类在数据库中的名字
- 在类中指定 Meta 类 内部修改数据库中的类名
class Author(models.Model):
name = models.CharField(verbose_name='name', max_length=11, blank=False)
age = models.IntegerField(verbose_name='age', default=1)
email = models.EmailField(verbose_name='email', blank=True, default='')
class Meta:
db_table = 'book' # 修改在数据库中存储的数据表名字
verbose_name = '图书管理' # 修改在admin管理后台中显示名
verbose_name_plural = verbose_name # 修改在admin管理后台中显示的复数名 后面自动加S
增加数据
models.Book.objects.create(book_name='Python')
- 执行之后 增加数据会直接增加到数据库
book = models.Book(book_name='Python')
book .save()
- 这里创建的对象需要调用
save
方法才会保存到数据库
- 这里创建的对象需要调用
查询数据
all():查询所有数据
Model.objects.all()
查询模型的所有数据
在 Model 类中定义 __str__ 可以改变
all()
返回的数据可读形式
class Author(models.Model):
name = models.CharField(verbose_name='name', max_length=11)
age = models.IntegerField(verbose_name='age', default=1)
email = models.EmailField(verbose_name='email', null=True)
class Meta:
db_table = 'book' # 修改在数据库中存储的数据表名字
verbose_name = '图书管理' # 修改在admin管理后台中显示名
verbose_name_plural = verbose_name # 修改在admin管理后台中显示的复数名 后面自动加S
def __str__(self):
return f"\n{self.name}\t{self.age}\t{self.email}\n"
- values(‘li1’,’li2’):查询部分数据,并返回 字典
- values_list(‘li1’,’li2’):查询部分数据,并返回 元组
更新数据
- 单个数据的更改:
- 注意 get 的限制 如果没有获取到 就会报错
book = models.Book.objects.get(id=update_id)
book.price = 10
book.save()
- 多个数据的更改
books = models.Book.objects.all()
books.update(market_price = 99)
删除数据
- 实际业务中不会使用删除 一般都是隐藏。
- 提前设置一个字段 来控制数据的显示
单个数据的删除
- 直接找到数据,调用 delete 方法
- book = models.Book.objects.get(id=update_id)
- book.delete()
多个数据的删除
- books = models.Book.objects.values_list()
- books.delete()
F 对象和 Q 对象
F 对象:
- 一个 F 对象代表数据库中某条记录的字段信息
- 通常在不获取数据库中字段值的情况下进行操作
- 这样可以避免高并发的数据丢失问题
- 也可以用于属性
字段
之间的比较
- F 对象的字段更新
from django.db.models import F
models.Book.objects.all().update(price = F('price') + 10)
# 相当于 price += 10
- F 对象的字段比较
- 下面语句查出 price 高于零售价的书
from django.db.models import F
models.Book.objects.filter(price__gt = F('market_price'))
Q 对象:
- 获取结果集中的 逻辑与
&
逻辑或|
逻辑非~
models.Book.objects.filter(Q(price__lt = 50)|Q(pub='人与自然'))
# 查出价格低于50,或者出版社是人与自然的书
models.Book.objects.filter(Q(price__lt = 50)&~Q(pub='人与自然'))
# 查出价格低于50,并且不是人与自然发表的
聚合查询
指的是:对数据表中的一个字段的数据进行部分或者全部进行统计查询等
聚合函数
- sum:求和
- Avg:平均值
- Count:计数
- Max:最大值
- Min:最小值
整表聚合查询
models.Book.objects.aggregate(price=Count('price'))
结果为字典:{'price': 6}
上面中price为结果字典的中的键。
分组聚合查询
- 分组
- 首先通过 values 分组:
bs = models.Book.objects.values('pub')
- 首先通过 values 分组:
- 聚合查询
bs.annotate(rec = Min('price'))
通过出版社分组的信息找到每个出版社中 price 最低的书
- 分组
自带 admin 管理后台
admin
后台提供了较为完善的数据库管理接口- 会搜集所有已经注册的模型类 为这些模型类提供数据管理接口
- 1.创建管理用户
python manage.py createsuperuser
- 2.路由设置
from django.contrib import admin
urlpatterns = [
path('admin/', admin.site.urls),
]
- 在后台中操作自定义数据库(模型类)
- 注册:
admin
中注册要管理的models
类- 首先导入要管理的模型类
- 通过
admin.site.register(ModelName)
进行注册
from django.contrib import admin
# Register your models here.
from bookstore.models import Book
admin.site.register(Book)
- 注册成功后在网页中就可以刷新看到
- 并且网页列表中的信息显示方式为模型类中的 __str__ 返回的数据显示方式
模型管理器类
admin
内部写一个类继承自模型管理器类- 推荐名:Manager(ModelAdmin)
- 功能:为后台界面添加便于操作的新功能
- 必须继承自:
from django.contrib.admin import ModelAdmin
- 必须继承自:
- 绑定注册的模型管理器到模型类
from django.contrib import admin
# Register your models here.
from bookstore.models import Book
class BookstoreManager(ModelAdmin):
pass
admin.site.register(Book,BookstoreManager)
管理器类的几种设置
- 列表显示的表头
# admin后台管理器中部分属性的作用
# 表头显示的列名
list_display = ['id', 'title', 'price', 'info', 'pub', 'market_price', 'is_delete']
# 控制管理也的哪些字段可以链接到修改页(为目标加上超链接,点击即可自动跳转修改)
list_display_links = ['id', 'title']
# 为指定字段在右侧添加过滤器
list_filter = ['pub']
# 添加搜索框
search_fields = ['title', 'pub', 'price']
# 添加可以直接在列表页修改的信息
# 这里输入的属性不能存在于上面的list_display_links中
list_editable = ['price', 'market_price']
Django 的关系映射
级联删除
- 当有数据关联的时候指定如何操作
models.CASCADE
级联删除。模拟SQL
的 删除有关联的一并全部删除models.PROTECT
抛出ProtectedError
阻止被引用对象的删除(无法删除)SET_NULL
删除,但关联数据的外键设置为null
需要设置 models 的 null=TrueSET_DEFAULT
删除,外键设置默认值 必须设置 ForeignKey 的默认值
一对一
class Author(models.Model):
name = models.CharField(verbose_name='name', max_length=100)
class Wife(models.Model):
name = models.CharField(verbose_name='name', max_length=100)
# 这里创建一对一外键
author = models.OneToOneField(to=Author, on_delete=models.CASCADE)
一对一的关联方式
- 在没有外键字段的类中 创建数据是没有区别的
w1 = models.Author.objects.create(name='王先生')
- 在有外键字段的类中有两种创建方式
- 直接在外键字段名中传入绑定的 obj 对象
# 创建对象的时候获取对象
w1 = models.Author.objects.create(name='王先生')
# 创建外键数据的时候给外键名传入关联的对象
models.Wife.objects.create(name='王夫人',author = w1)
# 后面这个王夫人,将和王先生进行关联
- 以字段名_id 的方式传入
# 创建一个龚先生对象
models.Author.objects.create(name= '龚先生')
# 查询到龚先生对象 id为2
# : models.Author.objects.values_list()
# : <QuerySet [(1, '王先生'), (2, '龚先生')]>
# 外键数据并创建id关联
models.Wife.objects.create(name = '龚太太',author_id=2)
一对一的查询方式
- 正向查询 直接通过查询到的对象.外键名字.属性即可获取到绑定的外键对象属性
# 获取到本身的对象
b1 = models.Wife.objects.get(id = 2)
# 通过对象获取绑定外键的属性名
b1.author.name
- 反向查询
- django 在创建外键的时候,会在 被绑定 的对象上创建一个隐藏的属性
- 这个属性名字 就是外键绑定过来的 类名 注意都是小写
# 首先找到没有外键的对象
b3 = models.Author.objects.get(id = 1)
# 外键本身的名字
b3.name
# 通过隐藏属性反向查找(绑定的外键类名的小写)
b3.wife.name
一对多
# 创建一对多
class Publisher(models.Model):
# 出版社名称 [一]
name = models.CharField(verbose_name='清华出版社', max_length=50)
class Book(models.Model):
# 书名 [多]
title = models.CharField(verbose_name='书名', max_length=50)
# 在这创建 一对多
publisher = models.ForeignKey(to=Publisher, on_delete=models.CASCADE)
- 一个出版社可以出多本书,但一本书只能由一个出版社,所以外键是设置在多上,也就是外键设置在书上
- 一对多中,外键设置在多中
一对多的关联方式
- 先创建一 再创建多 因为多的需要绑定一个一的外键,如果一为空就无法绑定
- 第一种方式
# 首先创建一个 一的数据
a1 = models.Publisher.objects.create(name = '清华出版社')
# 创建多数据 绑定上面的创建的a1
models.Book.objects.create(title='新华字典',publisher=a1)
# 新华字典的就绑定了清华出版社,并且可以创建多条数据绑定清华出版社
- 第二种方式
# 先创建一个出版社
models.Publisher.objects.create(name = '工业出版社')
# 查询到出版社的id为2,通过外键名_id的方式进行外键绑定
models.Book.objects.create(title='工业技术',publisher_id = 2)
# 到此绑定完毕,可以创建多个数据进行绑定
一对多的查询方式
- 在有外键的一方查询简单(正向查找)
# 首先获取对象
a1 = models.Book.objects.get(id=5)
# 通过对象.外键字段名.方法即可获取到绑定的外键对象属性值
a1.publisher.name
- 在没有外键的一方查找(反向查找)
# 获取一个被绑定外键的出版社
a2 = models.Publisher.objects.get(id = 3)
# 通过a2.方法反向查找数据
a2.book_set.all()
# 这里的book为关联的另外一个类名小写。
# 并且book_set等价于objects.可以通过.方法调用各种方法
多对多
- 多对多中
mysql
需要创建第三张表 但Django
不需要手动创建 自动创建models.ManyToManyField(MyModel)
参数为指定跟谁是多对的关系,只需要在任意一个类中增加即可
class Author(models.Model):
name = models.CharField(verbose_name='姓名', max_length=11)
class Book(models.Model):
title = models.CharField(verbose_name='书名', max_length=100)
# 这里创建了多对多的表关系
author = models.ManyToManyField(Author)
多对多关联方式
- 创建设置外键的一方
# 先创建一本书
a1 = models.Book.objects.create(title = '基金')
# 然后通过a1的外键字段,创建一个作者
b1 = a1.author.create(name = '龚老师')
# 然后通过外键添加书和老师的绑定
a1.author.add(b1)
- 创建没有设置外键的一方
# 首先创建两个老师对象
author1 = models.Author.objects.create(name= '龚老师')
author2 = models.Author.objects.create(name= '林老师')
# 然后创建一本书
book1 = models.Book.objects.create(title = '课堂记')
# 为两个老师创建和书的关联(两个老师同时写的这本书)
author1.book_set.add(book1)
author2.book_set.add(book1)
多对多的查询方式
- 正向查询 因为两边对的都是多,所以查询是一样的
# 获取到book1绑定的所有老师
book1.author.all()
# 获取到指定条件(年龄大于80岁)
book1.author.filter(age__ge = 80)
- 反向查询 查询老师绑定的书
# 查询老师绑定的所有数
author1.book_set.all()
# 查询老师绑定的书的指定条件
author1.book_set.filter(***)
Cookies 和 Session
Cookies
Cookies
指的是存储在客户端浏览器上的存储空间Cookies
是以键值对的形式存在ASCII码
Cookies
数据有生命周期Cookies
数据是按域网站
存储的,不同的无法访问- Cookies 每次访问都会带上发给服务器
- 所以 Cookies 过大会导致访问过慢
设置 Cookie
- HttpResponse 都有一个 set_cookie 方法
响应三板斧
- key:cookie 名
- value:cookie 值
- max_age:cookie 存活时间
- expires:具体过期时间
- 当不指定两个时间的时候关闭浏览器数据过期
- 重新设置即可更新
r.set_cookie('username', username, max_age=60 * 60 * 24 * 3)
获取 Cookies(通过请求请求)
request.COOKIE
# 获取cookie,没有获取到返回None
request.COOKIES.get('name', None)
删除 Cookie
HttpResponse.delete_cookie(key)
# key不存在什么也不发生
Session
Session
是在服务器上开辟一段空间用于保留用户浏览器交互时的重要数据- 将
Sessionid
的标识发给服务器,设置在cookie
上,每次请求将id
发送过来验证身份 - 保证了重要数据的安全 无须再传到客户端
启动 Session
- 检查 APPS,是否存在
INSTALLED_APPS = [
'django.contrib.sessions',
]
- 在 MIDDLEWARE 中是否存在
MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware',
]
使用 Session
设置 session 到浏览器
request.session['name'] = 'ages'
获取请求的 session
request.session.get('name', None)
删除 session
del request.session['name']
没有 session 会报错
session 配置中的设置
# 指定session在cookie中保存的时间 秒 默认是两周
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2
# 设置浏览器关闭session就失效 默认False
SESSION_EXPIRE_AT_BROWSER_CLOSE = False
清除过期 session
session
过期不会自动删除需要执行以下指令
python manage.py clearsessions
Django 高级
缓存
- 是一类可以更快的读取数据的介质统称,也指其他可以加快数据读取的存储方式
配置缓存在数据库
# 配置缓存在数据库 推荐存储在redis数据库
CACHES = {
'default': {
# 缓存使用的数据库(如果配置了mysql就会在mysql中创建)
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
# 缓存使用的数据库表名
'LOCATION': 'cache_table',
# 缓存保存时间
'TIMEOUT': 300,
'OPTIONS': {
# 缓存最大条数据
'MAX_ENTRIES': 300,
# 缓存达到最大条时删除 1/X的缓存数据
'CULL_FREQUENCY': 2
}
}
}
缓存的使用
全局缓存
- 第一种
from django.shortcuts import render
from django.views.decorators.cache import cache_page
# Create your views here.
# 下面index将被缓存60秒,60秒后才会再次走视图函数
@cache_page(60)
def index(request):
return render(request, 'index/index.html', locals())
- 第二种
from django.urls import path
from django.views.decorators.cache import cache_page
from index import views
# 直接在路由中配置缓存
urlpatterns = [
path('', cache_page(60)(views.index)),
]
中间件
- 中间件是 Django 请求/响应 处理的钩子框架,他是一个轻量级的低级插件,用于改变 Django 的输入或输出
中间件要求
必须继承:
MiddlewareMixin
按要求重写指定方法
在项目目录下创建一个
Middleware
文件夹单独写所有的中间件注意 中间件再进入的时候是 从上到下 当返回给前端 视图函数执行完毕之后 时是从下到上
from django.utils.deprecation import MiddlewareMixin
class MyMiddleware(MiddlewareMixin):
# 接到请求,请求在达到主路由之前被调用
def process_request(self, request):
# 要么返回None,要么返回HttpResponse
# None:请求接着往后交给路由
# HttpResponse:直接截断返回给前端,不会再交给路由
pass
# 当请求通过了上面的request再达到视图函数之前被调用
def process_view(self, request, callback, callback_args, callback_kwargs):
# 要么返回None,要么返回HttpResponse
# None:请求接着往后交给路由
# HttpResponse:直接截断返回给前端,不会再交给路由
pass
# 所有相应返回给浏览器的时候被调用
def process_response(self, request, response):
# 返回HttpResponse
# 返回给前端的相应
pass
# 处理过程中抛出异常的时调用
def process_exception(self, request, exception):
# 返回一个HttpResponse对象
pass
# 在视图函数执行完毕,并且在返回的对象中包含render方法时被调用
def process_template_response(self, request, response):
# 该方法需要返回实现了render方法的响应对象
pass
CSRF 跨站请求攻击
- 如果在配置中开启了
scrf
- 那在网页的
form
发送post
表单请求的时候需要加上{% csrf_token %}
- 将密钥发送给后端,防止
csrf
攻击
<form method="post" action="">
{% csrf_token %}
............
</form>
MIDDLEWARE = [
'django.middleware.csrf.CsrfViewMiddleware',
]
关闭 CSRF 校验
- 1.全局关闭
- 直接禁用
csrf
中间件
- 直接禁用
- 2.局部校验
- 在不需要校验的函数前面加上
csrf_exempt
装饰器
- 在不需要校验的函数前面加上
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def register_view(request):
pass
分页
Django
已经配置好了分页方法,直接调用即可Paginator
属性(分页数据的整体的管理)count
:需要分页数据对象总数num_pages
:分页后的页面总数page_range
:从 1 开始的range
可迭代 对象,per_page
:每页的数据的个数
page
对象 具体某一页数据对象的管理object_list
:当前页上所有数据对象的列表,直接循环page
对象也可以number
:当前页的序号 从1
开始has_next()
:如果有下一页返回True
has_previous()
:如果有上一页返回True
has_other_pages()
:如果有上一页或下一页返回True
next_page_number()
:返回下一页的页码,如果下一页不存在,抛出异常previous_page_number()
:返回上一页页码,不存在抛出异常paginator
:当前 page 对象的相关paginator
对象
视图函数处理
def all_page(request):
# 获取网页的查询字符串:/page?page=1
page_num = request.GET.get('page', 1)
# 所有要分页的数据,这里是模拟五个数据
all_data = ['a', 'b', 'c', 'd', 'e']
# 初始化所有页面的paginator对象
# Paginator第一个参数是分页的数据,第二个是每页的数据量
paginator = Paginator(all_data, 2)
# 初始化具体页码的page对象(传入的是需要数据的页码)
page = paginator.page(int(page_num))
return render(request, 'index/all_page.html', locals())
- 前端页面
<!DOCTYPE html>
<html lang="ch">
<head>
<meta charset="UTF-8">
<title>分页显示</title>
</head>
<body>
{% for p in page %}
<p>{{ p }}</p>
{% endfor %}
{% if page.has_previous %}
<a href="/index/all_page?page={{ page.previous_page_number }}">上一页</a>
{% else %}
<a>上一页</a>
{% endif %}
{% for p in paginator.page_range %}
{% if page.number == p %}
<a>{{ p }}</a>
{% else %}
<a href="/index/all_page?page={{ p }}">{{ p }}</a>
{% endif %}
{% endfor %}
{% if page.has_next %}
<a href="/index/all_page?page={{ page.next_page_number }}">下一页</a>
{% else %}
<a>下一页</a>
{% endif %}
</body>
</html>
内建 自带 用户系统
基本字段
# 模型位置
from django.contrib.auth.models import User
基本字段
username
:用户名password
:密码email
:邮箱first_name
:名last_name
:姓is_superuser
:是否是管理员账号is_staff
:是否可以访问 admin 管理界面is_active
:是否是活跃用户,默认 True,一般不删除用户,将这个值设为 False 表示删除last_login
:上次登录时间date_joined
:用户创建的时间
创建用户
- 创建用户改为:
User.objects.create_user(.)
- 创建超级用户:
User.objects.create_superuser(.)
删除用户
- 一般不会删除用户,所以流程为
get
方法拿到的用户的is_active
属性设置为False
表示删除
校验密码
- 因为是
Django
自带的框架 密码加密方式不同,所以要调用Django
的方法
from django.contrib.auth import authenticate
# 用户名正确返回对应的user,否则返回None
user = authenticate(username=username,password=password)
修改密码
- 因为是
Django
自带的框架,密码加密方式不同 所以要调用Django
的方法 - 通过模块查到用户后即可修改密码
# 找到用户后
from django.contrib.auth.models import User
# 通过自带set方法修改密码
user.set_password('***')
# 保存即可成功修改密码
user.save()
登录状态保持
from django.contrib.auth import authenticate
from django.contrib.auth import login
# 首先验证用户账号密码(账号密码正确返回user,否则返回None)
user = authenticate(username=username,password=password)
# 找到后通过login方法即可设置登录状态
login(request,user)
# 只存session,时间不可控
登录状态取消
from django.contrib.auth import logout
# 下面函数返回后登录状态自动取消
def index(request):
logout(request)
登录状态校验
from django.contrib.auth.decorators import login_required
# 下面这个index函数,只有在登录状态下才可以访问
@login_required() # 当这里验证失败会自动跳转到settings里面配置的链接:LOGIN_URL = 'URL'
def index(request):
return render(request, 'index/index.html', locals())
# 当前登录用户可以通过(上面的视图函数通过了才可以获取)
user = request.user
内建用户表扩展字段
- 新应用定义模型类 继承自
AbstractUser
- 在 settings 中指明:
AUTH_USER_MODEL = ''
自定义的模型类 - 必须在第一次执行
Migrate
进行数据库迁移之前进行
from django.contrib.auth.models import AbstractUser
from django.db import models
# Create your models here.
class UserInfo(AbstractUser):
phone = models.CharField(max_length=22, default='')
# 之后在settings中配置
AUTH_USER_MODEL = 'user.UserInfo'
- 即可正常使用 扩展了新属性
文件上传
form
属性设置为
enctype='multipart/form-data'
前端上传
<form enctype="multipart/form-data" method="post">
{% csrf_token %}
<input type="file" name="文件上传">
<input type="submit">
</form>
配置存储位置 MEDIA
- 用户上传的文件统称为
media
资源
# 指定当 media 请求来的时候是请求用户上传数据的
MEDIA_URL = '/media/'
# 指定当请求用户上传数据的时候在哪个目录进行查找
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
- 在项目主路由中添加用户文件查找路由
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
....
]
# 添加这一行指定用户文件查找路由
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
后端保存
# FILES中的key对应前端的input上传的name
file = request.FILES['key']
# 获取前端文件名
file.name
# 获取文件字节流数据
file.file
文件上传 (不推荐)
# 下面保存文件
file = request.FILES['文件上传']
filename = os.path.join(settings.MEDIA_ROOT, file.name)
with open(filename, 'wb') as fp:
fp.write(file.file.read())
文件上传 Django 自带文件上传处理
- 首先创建模版的时候指定一个文件字段
# upload_to表示子文件夹文件名
class test_file(models.Model):
file = models.FileField(upload_to='file')
- 在创建数据的时候拿到文件后创建数据
# 拿到文件对象
file = request.FILES['文件上传']
# 创建数据,直接传入文件对象
test_file.objects.create(title=title, file=file)
DJnago 发送邮件
- 邮件协议
- SMTP(25 端口):只负责邮件的发送
- IMAP(143 端口):负责去邮件服务器拿取
- POP3(110 端口):也是负责去邮件服务器拿取 稍微慢一些因为要下载全部邮件
Django 主要通过 SMTP 进行发送邮件
- 配置文件
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.qq.com'
EMAIL_PORT = '25'
EMAIL_HOST_USER = 'xx@QQ.com'
EMAIL_HOST_PASSWORD = '得到的smtp密钥'
EMAIL_USE_TLS = False # 服务器通信是否启用TLS安全链接
- 启用
from django.core import mail
mail.send_mail(
subject='测试邮件标题',
message='测试邮件主体内容',
from_email='@qq.com', # 发送者,当前配置文件中的邮箱
recipient_list=['@qq.com'] # 接受者邮箱列表
)
- 错误追溯
from traceback import format_exc
# 输出错误的详细信息
print(format_exc())
发布项目 uWSGI nginx
安装 uWSGI
- 安装
- pip install uwsgi==2.0.18
- Django 项目配置
- 在 settings 同级目录创建一个 uwsgi.ini 配置文件 键入以下内容
[uwsgi]
;在哪个IP和端口监听服务
http = 127.0.0.1:8000
;项目绝对路径
chdir = 项目绝对路径
;相对上面的绝对路径中wsgi.py的路径位置
wsgi-file = blog/wsgi.py
;项目进程个数
process = 2
;项目线程个数
threads = 2
;服务主进程的pid记录文件
pidfile=uwsgi.pid
;项目服务日志文件位置
daemonize=uwsgi.log
;是否开启主进程管理模式
master=true
- 将配置文件中 DEBUG 改为 False
- 在 ALLOWED_HOSTS 中添加网站域名或者服务监听的 IP 地址
启动停止 uwsgi
- cd 到 uwsgi 配置文件所在目录
- 启动 uwsgi –ini uwsgi.ini
- 停止 uwsgi –stop uwsgi.pid