嘘~ 正在从服务器偷取页面 . . .

Django


基本

  • 安装 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 或任意正整数,返回 int
      • slug:匹配由 ASCII 字母或者数字以及_和-组成的短标签
      • path:匹配非空字段,包括路径分隔符
  • re_path

    • 以正则表达式的形式进行匹配
    • 需要以正则中的分组进行书写
      • page/(?P\<name>\d+) :将会匹配 page/后面带上一位以上数字的页面,并且以 name 为别名的形式传入后面的视图函数

Dango 中的请求

  • request.path_infourl 字符串 去除了 host
  • request.method:一般都是getposthttp请求方法
  • request.GETQueryDict:获取地址中所有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]

Django 中的响应

  • 响应对象 HttpResponse(content=响应体,content_type=响应体数据类型,status=状态码)

  • 常用Content-Type状态类型

  • 常见的媒体格式类型如下:

    • text/html : 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:有关模版的选项
    • 设置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 '别名' 参数名字 = '参数' %} 的方式进行传参

静态文件配置

  • 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')
    • 聚合查询
      • 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=True
    • SET_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 过大会导致访问过慢
  • 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)
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 端口):也是负责去邮件服务器拿取 稍微慢一些因为要下载全部邮件
      POP3 VS IMAP

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

文章作者: 林木木
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 林木木 !
评论
  目录