Django

jinja2模块

# 第三方模块需要下载
pip3 install jinja2


from jinja2 import Template


def get_dict_func(request):
    user_dict = {'name': 'jason', 'age': 18, 'person_list': ['阿珍', '阿强', '阿香', '阿红']}
    with open(r'templates/get_dict_page.html', 'r', encoding='utf8') as f:
        data = f.read()
    temp_obj = Template(data)  # 将页面数据交给模板处理
    res = temp_obj.render({'d1': user_dict})  # 给页面传了一个 变量名是d1值是字典数据的数据
    return res

<p>{{ d1 }}</p>
<p>{{ d1.name }}</p>
<p>{{ d1['age'] }}</p>
<p>{{ d1.get('person_list') }}</p>

django

版本与注意

1.版本问题
        django1.X:同步		1.11
        django2.X:同步		2.2
        django3.X:支持异步    3.2
        django4.X:支持异步	   4.2
	ps:版本之间的差异其实不大 主要是添加了额外的功能
        
2.运行django注意事项
	1.django项目中所有的文件名目录名不要出现中文
	2.计算机名称尽量也不要出现中文
 	3.一个pycharm尽量就是一个完整的项目(不要嵌套 不要叠加)
	4.不同版本的python解释器与不同版本的django可能会出现小问题

django的常见命令

1.下载
	pip3 install django 				默认最新版
 	pip3 install django==版本号		  指定版本
    	pip3 install django==2.2.22
	pip下载模块会自动解决依赖问题(会把关联需要用到的模块一起下了)
2.验证
	django-admin
3.常见命令
	1.创建django项目
    	django-admin startproject 项目名
 	2.启动django项目
    	cd 项目名
    	python38 manage.py runserver ip:port	
4.pycharm自动创建django项目
	会自动创建templates文件夹 但是配置文件中可能会报错
 		[ os.path.join(BASE_DIR,'templates') ]

django app的概念

django类似于是一所大学 app类似于大学里面的各个学院

django里面的app类似于某个具体的功能模块
	user	app 所有用户相关的都写在user app下
 	goods	app 所有商品相关的都写在goods app下
 
命令行创建应用
	python38 manage.py startapp 应用名
pycharm创建应用
	新建django项目可以默认创建一个 并且自动注册
"""
创建的app一定要去settings.py中注册
	INSTALLED_APPS = [
    	'app01.apps.App01Config',
		'app02'
	]
"""

django主要目录结构

django项目目录名
	django项目同名目录
    	settings.py		 配置文件
    	urls.py			存储网址后缀与函数名对应关系(不严谨)
   		wsgi.py			wsgiref网关文件
	db.sqlite3文件	   django自带的小型数据库(项目启动之后才会出现)
	manage.py			 入口文件(命令提供)
 	应用目录
    	migrations目录	存储数据库相关记录
    	admin.py		 django内置的admin后台管理功能
       apps.py			注册app相关
   		models.py		 与数据库打交道的(非常重要)
    	tests.py		 测试文件
    	views.py		 存储功能函数(不严谨)
   	templates目录			存储html文件(命令行不会自动创建 pycharm会)
    	配置文件中还需要配置路径
        	[os.path.join(BASE_DIR,'templates'),]
 
"""
	网址后缀			路由
	函数				 视图函数
	类				  视图类
重要名词讲解
	urls.py				 路由层	
	views.py			 视图层
	models.py			 模型层
	templates			 模板层
"""

django小白必会三板斧

from django.shortcuts import render,HttpResponse,redirect

HttpResponse		 返回字符串类型的数据

render				返回html页面并且支持传值

redirect			重定向

静态文件配置

# 静态文件
不经常发生变化的文件 主要针对html文件所使用到的各种资源
	css文件、js文件、img文件
    
   
# 静态文件配置
# 访问静态文件资源的通行证
STATIC_URL = '/static/'

# 存储静态文件资源的目录名称
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),
]


# 接口前缀动态匹配
    {% load static %}
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.css' %}">
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.js' %}"></script>

form表单

# action  控制数据的提交地址

'''
	action = ' ' 数据提交给当前页面所在的默认地址
	action = 'https://www.baidu.com' 
	action = '/index/' 朝当前服务器的index地址提交
'''

# method 控制数据提交的方法
 get: 朝服务器索要数据
 post: 朝服务器提交数据


# 使用post请求需要更改配置文件
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',

Django连接数据库

# 默认配置
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

# 自定义
DATABASES = {
    #     'default': {
    #         'ENGINE': 'django.db.backends.sqlite3',
    #         'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    #     }
    # }
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'db3',
        'HOST': '127.0.0.1',
        'PORT': 3306,
        'USER': 'root',
        'PASSWORD': '123',
        'CHARSET': 'utf8'
    }
}

# 自定义报错,需要指定模块
'''
	Django1.x版本需要在项目目录下或APP目录下的__init__.py中变形代码
        import pymysql
        pymysql.install_as_MYSQLdb()
	Django2.x以上版本都可以直接通过下载mysqlclient模块解决
	pip install mysqlclient
'''

ORM简介

# ORM: 对象关系映射
	能够让不会SQL语句的Python程序员 使用Python面向对象的语法来操作数据库

ORM基本操作

# 在models里配置
class GInfo(models.Model):
    # id int primary_key auto_increment
    id = models.AutoField(primary_key=True)
    # name varchar(32)
    name = models.CharField(max_length=32)
    # age int
    age = models.IntegerField()
    # auto_now_add:首次创建数据添加时间,后续修改数据时间不会变
    # auto_now:只要修改数据时间就会改变
    publish_time = models.DateField(auto_now_add=True)
    
# 执行数据库迁移相关命令
	python38 manage.py makemigrations 
    python38 manage.py migrate

ORM基本语句

from app01 import models

'''创建数据'''
# models.GInfo.objects.create(name=name, age=pwd)
'''查询数据'''
# res = models.GInfo.objects.filter(name=name)
# print(res)
# print(res[0].name)
'''修改数据'''
# models.GInfo.objects.filter(id=1).update(name='gg', age=18)
'''删除数据'''
# models.GInfo.objects.filter(id=1).delete()

Django路由层

# 路由匹配
django2.x及以上 path第一个参数写什么就匹配什么
django1.x第一个参数是正则匹配

'''
	无论什么版本的Django都自带加斜杠功能,配置文件中可以配置,默认为true
					APPEND_SLASH = False
'''

# 转换器

urlpatterns = [
    # 转换器 将对应位置匹配到的数据转换成固定的数据类型
    # path('index/<str:info>/', views.index_func),  # (实参request对象,info='转换器匹配到的类型转换之后的内容'')
    path('index/<str:info>/<int:id>/', views.index_func) # index_func(实参request对象,info='转换器匹配到的类型转换之后的内容',id='转换器匹配到的类型转换之后的内容')
]


def index_func(request, info, id):
    print(info)
    print(id)
    return HttpResponse('index func')


'''
	使用转换器,对应的视图函数需要加上对应的关键字形参
	Django路由动态匹配的五种转换器
        str:匹配除路径分隔符外的任何非空字符串。
        int:匹配0或者任意正整数。
        slug:匹配任意一个由字母或数字组成的字符串。
        uuid:匹配格式化后的UUID。
        path:能够匹配完整的URL路径
'''


# 正则匹配
	django2.x及以上 path第一个参数是正则
只要第一个正则表达式能够从用户输入的路由中匹配到数据就算匹配成功,会立刻停止路由层其他的匹配,直接执行对应的视图函数
	re_path('^test$', views.test),
    
 Django1.x路由匹配使用的是url(),功能与Django2.x以上的路由匹配re_path()功能一致


# 正则匹配的无名分组与有名分组
	# 无名分组:
	    re_path('^test/(\d{4})/', views.test)  # (实参request对象,括号内正则匹配到的内容当做位置参数传给视图函数)
    
    # 有名分组:
    	re_path('^test/(?P<test>\d{4})/', views.test)  # (实参request对象,test='正则匹配到的内容当做关键字参数传给视图函数')
            def test(request, func):
            print(func)
            return HttpResponse('test')

Django路由层反向解析

# 通过一个名字可以反向解析出一个结果 该结果可以访问到某个固定的路由
	# 路由起别名
	path('test/', views.test, name='test.view')
    
    # 反向解析语法
	HTML页面 <a href="{% url 'test.view' %}">xxxx</a>
    
    后端需要导入reverse模块,跟三板斧同一个模块
    	reverse('test.view')
        
# 动态路由的反向解析
path('index/<str:info>/', views.index_func )

# 反向解析语法
 HTML页面 <a href="{% url 'test.view' 'xxxx' %}">xxxx</a>
    
 后端需要导入reverse模块,跟三板斧同一个模块
    	reverse('test.view', args=(xxx,))

路由分发

# Django支持每个应用都可以有自己独立的路由层、静态文件、模版层,基于这个特性就可以多人开发项目,之后利用路由分发就氪以整合到一起

# 每个app里面建立urls.py文件
from django.urls import path

urlpatterns = [
    path('show/', views.show_func, name='show_view'),
]


# 最后再进行路由分发
from django.urls import path,include
urlpatterns = [
    path('app01/', include('app01.urls')),
]

JsonResponse对象

# 不同地方的序列化
'''
	python中:
        序列化:   json.dumps()
        反序列化:  json.loads()
    js中:
    	序列化:   JSON.stringify()
        反序列化:   JSON.parse()
''''

# Django中的序列化
from django.http import JsonResponse
def ab_json(request):
    user_dict = {'username': 'tank哈哈哈', 'age': 20}
    json_str = JsonResponse(user_dict, json_dumps_params={'ensure_ascii': False},safe=False)
    return HttpResponse(json_str)

django中如何上传文件

# 表单上传问价需要满足的条件是:
1.请求方式是post
2.enctype---->form-data
<form action="" method="post" enctype="multipart/form-data">
3.通过request.FILES.get获取数据


def ab_file(request):
    # 接收用户提交过来的数据
    # request.POST 只能接收普通参数,不能接收文件数据
    print(request.POST) # <QueryDict: {'username': ['SDASD']}>
    # 如何接收文件数据
    print(request.FILES) # <MultiValueDict: {'myfile': [<InMemoryUploadedFile: 123.png (image/png)>]}>
    file_obj = request.FILES.get('myfile')
    with open(file_obj.name, 'wb') as f:
        # f.write(file_obj)
        for line in file_obj:
            f.write(line)
    return render(request, 'ab_file.html')

request参数

# request.methob 获取请求方式
   GET/POST

# request.POST 获取post请求中携带的数据
	request.POST.get()  获取列表最后一个值
     request.POST.getlist() 获取整个列表     
        
# request.GET 获取域名?后面的数据

# request.path  接收路径的全部内容  /index/
# request.path_info 接收路径的全部内容 /index/

# request.get_full_path()  接收路径的全部内容,连参数也能拿到 /index/?username=kevin

# request.body  浏览器发过的二进制数据

视图层之FBV与CBV

FBV
	基于函数的视图
CBV 
	基于类的视图
    
class MyView(views.View):
    def get(self, request):
        return HttpResponse('from CBV get function')

    def post(self, request):
        return HttpResponse('from CBV post function')

urlpatterns = [
	path('login', views.MyView.as_view()),
]

# CBV会根据请求方法的不同来匹配对应的方法并执行

CBV源码解析

1.从CBV的路由匹配切入
	path('login/', views.MyLoginView.as_view())
        1.类名点名字(名字的查找问题)
        2.类名点名字并加括号调用(静态方法、绑定给类的方法)
2.函数名加括号执行优先级最高 项目一启动就会自动执行as_view方法
	path('login/', views.view)  # CBV路由本质还是FBV
3.浏览器地址栏访问login路由需要执行view函数
	1.产生我们自己编写类的对象
 	2.对象调用dispatch方法(注意查找顺序)
4.研究父类中的dispatch方法
	获取当前请求方法并转小写 之后利用反射获取类中对应的方法并执行
  

path('login/', views.MyLogin.as_view()), # 本质上放的还是函数的内存地址
# 当请求来了,会自定触发view函数的执行
def view(request, *args, **kwargs):
   self = cls(**initkwargs) #  # self = MyLogin(**initkwargs)
   return self.dispatch(request, *args, **kwargs)
    
# view函数执行完毕之后,来到了return self.dispatch(request, *args, **kwargs)
def dispatch(self, request, *args, **kwargs):
    # Try to dispatch to the right method; if a method doesn't exist,
    # defer to the error handler. Also defer to the error handler if the
    # request method isn't on the approved list.
    if request.method.lower() in self.http_method_names:
        # handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        handler = getattr(self, 'get', self.http_method_not_allowed)
        # handler----------->get函数的内存地址
    else:
        handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs)
    

模版层之语法和过滤器

'''
	{{}}:主要与数据值相关
	{% %}: 主要与逻辑相关
'''

# 模版传值语法
1.以k:v键值对的方式把想传的参数传过去
return render(request, 'test.html', {user_dict: user_dict})
2.以字典类型返回当前位置的全部局部变量
return render(request, 'test.html', local())

# 不同数据类型的传输
	1.基本数据类型可以正常展示
     2.文件对象也可以展示并调用
    3.函数名会自动加上括号执行并将返回值展示到页面上(不支持传参)
    4.类名也可以自动加上括号调用
    5.对象不会自动加
    
# 过滤器
语法: {{ value|filter_name:参数 }},使用管道符"|"来应用过滤器。
'''
	注意事项:
        1. 过滤器支持“链式”操作。即一个过滤器的输出作为另一个过滤器的输入。
        2. 过滤器可以接受参数,例如:{{ sss|truncatewords:30 }},这将显示sss的前30个词。
        3. 过滤器参数包含空格的话,必须用引号包裹起来。比如使用逗号和空格去连接一个列表中的元素,如:{{ list|join:', ' }}
        4. '|'左右没有空格没有空格没有空格
'''

# |default: 如果一个变量是false或者为空,使用给定的默认值。 否则,使用变量的值。
	{{ value|default:"nothing"}}
如果value没有传值或者值为空的话就显示nothing
    
# |length:返回值的长度,作用于字符串和列表。
	{{ value|length }}
返回value的长度,如 value=['a', 'b', 'c', 'd']的话,就显示4.

# |filesizeformat:将值格式化为一个 “人类可读的” 文件尺寸 (例如 '13 KB', '4.1 MB', '102 bytes', 等等
	{{ value|filesizeformat }}
如果 value 是 123456789,输出将会是 117.7 MB

# |slice:切片
	{{value|slice:"2:-1"}}
    
# |date:格式化时间
	{{ value|date:"Y-m-d H:i:s"}}
 
# |truncatechars:如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾
	{{ value|truncatechars:9}}
    
# |add:数字加减与字符串的拼接
	{{ value|add:1}}

模版层之便签

{% if 条件1(可以自己写也可以用传递过来的数据) %}
        <p>今天又是周三了</p>
{% elif 条件2(可以自己写也可以用传递过来的数据) %}
        <p>百日冲刺</p>
{% else %}
        <p>没多少时间了!</p>
{% endif %}


{% for k in t1 %}
        {% if forloop.first %}
            <p>这是我的第一次循环{{ k }}</p>
        {% elif forloop.last %}
            <p>这是我的最后一次循环{{ k }}</p>
        {% else %}
            <p>这是中间循环{{ k }}</p>
        {% endif %}
        
        {% empty %}
            <p>你给我传的数据是空的无法循环取值(空字符串、空列表、空字典)</p>
{% endfor %}

# forloop
forloop.counter            The current iteration of the loop (1-indexed) 当前循环的索引值(从1开始)
forloop.counter0           The current iteration of the loop (0-indexed) 当前循环的索引值(从0开始)
forloop.revcounter         The number of iterations from the end of the loop (1-indexed) 当前循环的倒序索引值(从1开始)
forloop.revcounter0        The number of iterations from the end of the loop (0-indexed) 当前循环的倒序索引值(从0开始)
forloop.first              True if this is the first time through the loop 当前循环是不是第一次循环(布尔值)
forloop.last               True if this is the last time through the loop 当前循环是不是最后一次循环(布尔值)
forloop.parentloop         本层循环的外层循环

django模板语法取值操作>>>:只支持句点符
    句点符既可以点索引也可以点键
   d1 = {'name':'gjl','hobby':['read','music',{'a1':111,'a2':222}]}
	 {{ d1.hobby.2.a1 }}
        
{% with d1.hobby.2.a1 as h %}  复杂数据获取之后需要反复使用可以起别名
        <a href="">{{ h }}</a>
{% endwith %}

自定义过滤器、标签及inclusion_tag

"""
如果想要自定义一些模板语法 需要先完成下列的三步走战略
	1.在应用下创建一个名字必须叫templatetags的目录
	2.在上述目录下创建任意名称的py文件	
	3.在上述py文件内先编写两行固定的代码
		from django import template
		register = template.Library()
"""
# 自定义过滤器(最大只能接收两个参数)
@register.filter(name='myadd')
def func1(a, b):
    return a + b
{% load mytags %}
<p>{{ i|myadd:1 }}</p>


# 自定义标签(参数没有限制)
@register.simple_tag(name='mytag')
def func2(a, b, c, d, e):
    return f'{a}-{b}-{c}-{d}-{e}'
{% load mytags %}
{% mytag 'jason' 'kevin' 'oscar' 'tony' 'lili' %}



# 自定义inclusion_tag(局部的html代码)
@register.inclusion_tag('menu.html',name='mymenu')
def func3(n):
    html = []
    for i in range(n):
        html.append('<li>第%s页</li>'%i)
    return locals()
{% load mytags %}
{% mymenu 20 %}

模版的继承与导入

# 模版的继承
多个页面有很多相似的地方 我们可以使用下面的方式
	方法一:复制粘贴
    方法二:
  		方式2:模板的继承
		  1.在模板中使用block划定子板以后可以修改的区域
               {% block 区域名称 %}
               {% endblock %}
       	   2.子板继承模板
        		{% extends 'home.html' %}
            	{% block 区域名称 %}
                	子板自己的内容
                 {% endblock %}
            ps:模板中至少应该有三个区域
                    页面内容区、css样式区、js代码区
            补充:子板也可以继续使用模板的内容 
                    {{ block.super }}

模板的导入(了解)
	将某个html的部分提前写好 之后很多html页面都想使用就可以导入
 		{% include 'myform.html' %}

模型层之前期准备

1.自带的sqlite3数据库对时间字段不敏感 有时候会展示错乱 所以我们习惯切换成常见的数据库比如MySQL django orm并不会自动帮你创建库 所以需要提前准备好

2.单独测试django某个功能层
	默认不允许单独测试某个py文件  如果想要测试某个py文件(主要models.py)
 		测试环境1:pycharm提供的python console
 		测试环境2:自己搭建(自带的test或者自己创建)
            	1.拷贝manage.py前四行
           		2.自己再加两行
import os
def main():
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoday04.settings')
                	import django
               		django.setup()
main()       
 
3.django orm底层还是SQL语句 我们是可以查看的
	如果我们手上是一个QuerySet对象 那么可以直接点query查看SQL语句
 	如果想查看所有orm底层的SQL语句也可以在配置文件添加日志记录

ORM常用关键字

# create()  创建数据并直接获取当前创建的数据对象
   res = models.User.objects.create(name='阿兵', age=28)

# filter()  根据条件筛选数据  结果是QuerySet [数据对象1,数据对象2]
	res = models.User.objects.filter(name='jason', age=19)  # 括号内支持多个条件但是默认是and关系
    
# first() last()  QuerySet支持索引取值但是只支持正数 并且orm不建议你使用索引
     res = models.User.objects.filter()[1]
     res = models.User.objects.filter(pk=100)[0]  # 数据不存在索引取值会报错
     res = models.User.objects.filter(pk=100).first()  # 数据不存在不会报错而是返回None
     res = models.User.objects.filter().last()  # 数据不存在不会报错而是返回None
        
# update()  更新数据(批量更新)
     models.User.objects.filter().update()     批量更新
     models.User.objects.filter(id=1).update() 单个更新
        
# delete()  删除数据(批量删除)
     models.User.objects.filter().delete()      批量删除
     models.User.objects.filter(id=1).delete()  单个删除
        
# all()     查询所有数据    结果是QuerySet [数据对象1,数据对象2]
 	res = models.User.objects.all()
    
# values()  根据指定字段获取数据    结果是QuerySet [{},{},{},{}]
     res = models.User.objects.all().values('name')
     res = models.User.objects.filter().values()
     res = models.User.objects.values()
    
# values_list()   根据指定字段获取数据  结果是QuerySet [(),(),(),()]
	 res = models.User.objects.all().values_list('name','age')
    
# distinct()   去重 数据一定要一模一样才可以 如果有主键肯定不行
	 res = models.User.objects.values('name','age').distinct()
    
# order_by()  根据指定条件排序  默认是升序 字段前面加负号就是降序
	 res = models.User.objects.all().order_by('age')

# get()   根据条件筛选数据并直接获取到数据对象  一旦条件不存在会直接报错 不建议使用
	 res = models.User.objects.get(pk=1)
	 res = models.User.objects.get(pk=100, name='jason')

# exclude()  取反操作
	 res = models.User.objects.exclude(pk=1)

# reverse()  颠倒顺序(被操作的对象必须是已经排过序的才可以)
     res = models.User.objects.all()
     res = models.User.objects.all().order_by('age')
     res1 = models.User.objects.all().order_by('age').reverse()

# count()  统计结果集中数据的个数
 	res = models.User.objects.all().count()

# exists()  判断结果集中是否含有数据 如果有则返回True 没有则返回False
    res = models.User.objects.all().exists()
    res1 = models.User.objects.filter(pk=100).exists()

ORM执行SQL语句

# 方式一:
	models.User.objects.raw('select * from app01_user')
    
# 方式二:
    from django.db import connection
        cursor = connection.cursor()
        cursor.execute('select name from app01_user;')
        print(cursor.fetchall())

Django终端打印SQL语句

# 在Django项目的settings.py文件中,在最后复制粘贴如下代码:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

ORM双下划线查询

# __gt:大于
	res = models.Admin.objects.filter(age__gt=18)

# __gte:大于等于
     res = models.Admin.objects.filter(age__gte=18)

# __lt:小于
     res = models.Admin.objects.filter(age__lt=18)

# lte:小于等于    
     res = models.Admin.objects.filter(age__lte=18)

# __in:年龄是10岁,9岁,44岁的    
     res = models.Admin.objects.filter(age__in=(10, 9, 44))

# __range:在一个范围里

     res = models.Admin.objects.filter(age__range=(10, 20))

# __contains:包含(区分大小写)  
     res = models.Admin.objects.filter(name__contains='g')

# __icontains:包含(不区分大小写)      
     res = models.Admin.objects.filter(name__icontains='g')

ORM外键字段创建

class Book(models.Model):
    """图书表"""
    title = models.CharField(max_length=32, verbose_name='书名')
    price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='价格')
    publish_time = models.DateField(auto_now_add=True, verbose_name='出版日期')
    """创建书籍与出版社的一对多关系"""
    """on_delete级联跟新,级联删除"""
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)

    """创建书籍与作者的多对多关系"""
    author = models.ManyToManyField(to='Author')


class Publish(models.Model):
    """出版社表"""
    name = models.CharField(max_length=32, verbose_name='名称')
    address = models.CharField(max_length=64, verbose_name='地址')


class Author(models.Model):
    """作者表"""
    name = models.CharField(max_length=32, verbose_name='姓名')
    age = models.IntegerField(verbose_name='年龄')
    """创建作者与作者详情的一对一"""
    author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)


class AuthorDetail(models.Model):
    """作者详情表"""
    phone = models.BigIntegerField(verbose_name='手机号')
    address = models.CharField(max_length=64, verbose_name='家庭地址')

ORM外键字段的增删改查

# 针对一对多 插入数据可以直接填写表中的实际字段
     models.Book.objects.create(title='三国演义', price=888.88, publish_id=1)
     models.Book.objects.create(title='人性的弱点', price=777.55, publish_id=1)
        
# 针对一对多 插入数据也可以填写表中的类中字段名
     publish_obj = models.Publish.objects.filter(pk=1).first()
     models.Book.objects.create(title='水浒传', price=555.66, publish=publish_obj)
    '''一对一与一对多 一致'''
    既可以传数字也可以传对象
    
    
# 针对多对多关系绑定
     book_obj = models.Book.objects.filter(pk=1).first()
     book_obj.authors.add(1)  # 在第三张关系表中给当前书籍绑定作者
     book_obj.authors.add(2, 3)
     book_obj = models.Book.objects.filter(pk=4).first()
     author_obj1 = models.Author.objects.filter(pk=1).first()
     author_obj2 = models.Author.objects.filter(pk=2).first()
     book_obj.authors.add(author_obj1)
     book_obj.authors.add(author_obj1, author_obj2)
     book_obj = models.Book.objects.filter(pk=1).first()
    
# 修改关系
     book_obj.authors.set((1, 3))  
     book_obj.authors.set([2, ])  
     author_obj1 = models.Author.objects.filter(pk=1).first()
     author_obj2 = models.Author.objects.filter(pk=2).first()
     book_obj.authors.set((author_obj1,))
     book_obj.authors.set((author_obj1, author_obj2))
        
# 删除关系       
     book_obj.authors.remove(2)
     book_obj.authors.remove(1, 3)
     book_obj.authors.remove(author_obj1,)
     book_obj.authors.remove(author_obj1,author_obj2)
     book_obj.authors.clear()
 	
    add()\remove()	   多个位置参数(数字 对象)
    set()			  可迭代对象(元组 列表) 数字 对象 
    clear()			  情况当前数据对象的关系

ORM跨表查询

# 正向查询与反向查询
正向查询:由外键所在的表数据查询关联的表数据 
反向查询:没有外键字段的表数据查询关联的表数据

# 总结:正向查询按外键字段,反向查询按表名

基于对象的跨表查询(子查询)

###########################################正向查询##############################################
# 1.查询主键为1的书籍对应的出版社名称
    # 先根据条件获取数据对象
    # book_obj = models.Book.objects.filter(pk=1).first()
    # 再判断正反向的概念  由书查出版社 外键字段在书所在的表中 所以是正向查询
    # print(book_obj.publish.name)

# 2.查询主键为4的书籍对应的作者姓名
    # 先根据条件获取数据对象
    # book_obj = models.Book.objects.filter(pk=4).first()
    # 再判断正反向的概念  由书查作者 外键字段在书所在的表中 所以是正向查询
     print(book_obj.authors)  # app01.Author.None
     print(book_obj.authors.all())
     print(book_obj.authors.all().values('name'))
    
# 3.查询jason的电话号码
author_obj = models.Author.objects.filter(name='jason').first()
print(author_obj.author_detail.phone)

###########################################反向查询##############################################
# 4.查询北方出版社出版过的书籍
    # publish_obj = models.Publish.objects.filter(name='北方出版社').first()
    # print(publish_obj.book_set)  # app01.Book.None
    # print(publish_obj.book_set.all())

# 5.查询jason写过的书籍
    # author_obj = models.Author.objects.filter(name='jason').first()
    # print(author_obj.book_set)  # app01.Book.None
    # print(author_obj.book_set.all())

# 6.查询电话号码是110的作者姓名
    author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()
    print(author_detail_obj.author)
    print(author_detail_obj.author.name)

基于双下划线的跨表查询

# 1.查询主键为1的书籍对应的出版社名称
	res = models.Book.objects.filter(pk=1).values('publish__name','title')

# 2.查询主键为4的书籍对应的作者姓名
	res = models.Book.objects.filter(pk=4).values('title', 'authors__name')

# 3.查询jason的电话号码
	res = models.Author.objects.filter(name='jason').values('author_detail__phone')

# 4.查询北方出版社出版过的书籍名称和价格
	res = models.Publish.objects.filter(name='北方出版社').values('book__title','book__price','name')

# 5.查询jason写过的书籍名称
	res = models.Author.objects.filter(name='jason').values('book__title', 'name')

# 6.查询电话号码是110的作者姓名
	res = models.AuthorDetail.objects.filter(phone=110).values('phone', 'author__name')

聚合查询(aggregate)

聚合函数:Max Min Sum Count Avg
在ORM中支持单独使用聚合函数  aggregate 

from django.db.models import Max, Min, Sum, Count, Avg
res = models.Book.objects.aggregate(Max('price'), Count('pk'), 最小价格=Min('price'), allPrice=Sum('price'),平均价格=Avg('price'))
print(res)

分组查询(annotate)

# group by分组
# 分组之后只能取得分组的依据,其他的字段不能拿到
# 设置一个严格模式:sql_mode='only...' 
# annotate
######################分组查询
    from django.db.models import Count, Sum, Max, Min, Avg
    # 1.统计每一本书的作者个数
    # select *from book group by id
    # res = models.Book.objects.annotate()  # 就是安装书来分组的
    # 正反向:书----》作者-----》正向-----》外键字段
    # annotate就是对models后面的表进行分组
    # 聚合函数一般都是配合分组使用的
    # res = models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num')
    # print(res)

    #  # 2.统计每个出版社卖的最便宜的书的价格
    # 1. 按照出版社分组
    # 2. 聚合查询书的价格
    # 3. 出版社查书------------>反向查询------------》表名小写
    # res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')
    # print(res)

    # 3.统计不止一个作者的图书
    # 1. 统计每一本的作者个数
    # 2. 在过滤出作者个数大于1的就可以了

    # 书----》作者-----》正向-----》外键字段
    # res = models.Book.objects.annotate(author_num=Count('authors__pk')).filter(author_num__gt=1).values('title', 'author_num')
    # res只要返回的结果是queryset,就可以一直往下点 queryset提供的方法
    # res = models.Book.objects.annotate(Count('authors'))
    # print(res)

    # 4.查询每个作者出的书的总价格
    # 1. 按照作者分组
    # 2. 作者查书
    # 总价格
    # res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name', 'sum_price')
    # print(res)

F查询

# 1.查询卖出数大于库存数的书籍
####################F查询
    from django.db.models import F

    # 1.查询卖出数大于库存数的书籍
    # select * from book where sale_num > kucun;
    # kucun

    # res = models.Book.objects.filter(sale_num__gt=F('kucun'))
    # print(res)

    # # 2.将所有书籍的价格提升500块
    # update app01_book set price = price+500;

    # res = models.Book.objects.update(price=F('price')+500)
    # 3.将所有书的名称后面加上爆款两个字
    # update app01_book set title = title + 'haha' ;
    from django.db.models.functions import Concat
    from django.db.models import Value
    # models.Book.objects.update(title=F('title')+'haha') # 不能这样写
    models.Book.objects.update(title=Concat(F('title'), Value('haha'))) 

Q查询

# # 1.查询卖出数大于100或者价格小于600的书籍
    # select * from book where sale_num > 100 or price < 600;
    # res = models.Book.objects.filter(sale_num__gt=100, price__lt=600) # and关系
    # res = models.Book.objects.filter(sale_num__gt=100).filter(price__lt=600) # and关系
    from django.db.models import Q

    # res = models.Book.objects.filter(sale_num__gt=100).filter(price__lt=600) # and关系
    # res = models.Book.objects.filter(Q(sale_num__gt=100), Q(price__lt=600))  # and关系
    # res = models.Book.objects.filter(Q(sale_num__gt=100)|Q(price__lt=600))  # or关系
    # res = models.Book.objects.filter(~Q(sale_num__gt=100)|Q(price__lt=600))  # ~是非的关系
    # print(res)

    # Q补充高级用法
    res = models.Book.objects.filter(Q(sale_num__gt=100) | Q(price__lt=600))
    ''''''
    # 'price'
    # requests.GET.get('sort') # price
    # res = models.Book.objects.filter(price__gt=100)
    # # res = models.Book.objects.filter('price'+'__gt'=100)
    q = Q()
    q.connector = 'or' # 把多个查询条件改为OR关系了
    q.children.append(('maichu__gt', 100))
    q.children.append(('sale_num__lt',100))
    res = models.Book.objects.filter(q)  # and关系 ,,
    print(res)

    # 想学习Q的更高级用法,自行百度

django中如何开启事务

开启事务的步骤
1、 开启事务
	start transaction;
2、提交事务
	commit;
3、回滚事务
	rollback;
# django中如何开启事务
  from django.db import transaction
    try:
        with transaction.atomic():
            models.Book.objects.create()
            models.Publish.objects.update()
            # sql1
            # sql2
            ...
    except Exception as e:
        print(e) # 当sql语句出现异常的时候,可以在这里打印错误信息
        transaction.rollback()

模型层中得常见字段和参数

# AutoField
int自增列,必须填入参数 primary_key=True。

# IntegerField
一个整数类型,范围在 -2147483648 to 2147483647。
#  CharField
# DateField
# DateTimeField

auto_now=True
auto_now_add=True

# BooleanField(Field)
- 布尔值类型
# 在代码里面就是要True/False-------------->在数据库中存的是0/1

# TextField(Field)
- 文本类型------------>可以存大段的文本------------->当字符串比较少的时候,一般用varchar(512)
----------》text----->存一篇文章

# FileField(Field)  ------------> 可以上传文件
- 字符串,路径保存在数据库,文件上传到指定目录
- 参数:
upload_to = ""      上传文件的保存路径

# image = models.FileField(upload_to='文件上传的位置')
image = models.ImageField(upload_to='图片')  # 只能上传图片

# FloatField(Field)
- 浮点型

# DecimalField(Field)
- 10进制小数
- 参数:
max_digits,小数总长度
decimal_places,小数位长度

ORM查询优化

'''
	1.ORM的查询默认都是惰性查询
	2.ORM的查询自带分页处理
'''

# only与defer
	
     res = models.Book.objects.only('title', 'price')
     print(res)  # queryset [数据对象、数据对象]
     for obj in res:
         print(obj.title)  
         print(obj.price)
         print(obj.publish_time)  
'''
	only:对only生成的数据使用点语法时,如果点的字段是括号内含有的字段,就不会走SQL查询,如果点的字段是括号内没有的字段就会走SQL查询
'''    
        
        
    res = models.Book.objects.defer('title', 'price')
    print(res)  # queryset [数据对象、数据对象]
    for obj in res:
         print(obj.title)  
         print(obj.price)
         print(obj.publish_time)  

'''
	defer:对defer生成的数据使用点语法时,如果点的字段是括号内含有的字段,就会走SQL查询,如果点的字段是括号内没有的字段就不会走SQL查询
'''   
        
        
# select_related与prefetch_related
	 res = models.Book.objects.all()
     for obj in res:
        print(obj.publish.name)  # 每次查询都需要走SQL
        
    res = models.Book.objects.select_related('authors')  # 底层是inner join
    res1 = models.Author.objects.select_related('author_detail')  # 括号内不支持多对多字段 其他两个都可以
    print(res1)
    for obj in res:
        print(obj.publish.name)  # 不再走SQL查询

    res = models.Book.objects.prefetch_related('publish')  # 子查询
    for obj in res:
        print(obj.publish.name)

choice参数

class User(models .Model):
username = models.CharField(max length=32)age = models.IntegerField()
gender_choices = (
    (1,'男'),# 使用int类型
    (2,'女'),
    (3,'其他')
)
# 先列举所有的对应关系,元组的形式,第一个参数相当于变量名,第二个参数相当于变量值,可通过变量名找到对应的变量值.
		gender = models.IntegerField(choices=gender_choices)
# 元组内第一个元组的第一个元素是什么类型这里就要定义什么类型,号内填写choices参数,参数的值为定义好的对应关系
'''
	该gender字段存的还是数字 但是如果存的数字在上面元祖列举的范围之内
	那么可以非常轻松的获取到数字对应的真正的内容
'''

score_choices = (
    ("A",'优秀'),#使用字符类型
    ("B",'良好'),
    ('c','及格'),
    ("D",'差')
score = models.CharField(max ength=32,choices=score_choices)
# 存
models.User.objects.create(username='jack',age=19,gender=1,score='A')         models.User.objects.create(username='tom',age=20,gender=2,score='B')
models.User.objects.create(username='kevin',age=31,gender=3 ,score='c')
models.User.objects.create(username='gary',age=25,gender=4,score='D')
models.User.objects.create(username='gary',age=25,gender=5, score='E')

# 取:
user_obj = models.User.objects.filter(pk=1).first()
print(user_obj.gender)  # 1
print(user_obj.get_gender_display()) # 男
# 只要是choices参数的字段 如果你想要获取对应信息 固定法 get_表名_display()
res = models.User.objects .filter(pk=4).first()print(res.gnder)
print(res.get_gender_display()) # 差
# 如果没有对应关系 那么宁是什么还是展示什么res1 = models.User.objects.filter(pk=5).first()print(res1.score)
print(res1.get_score_display()) # 5

多对多建表的三种方式

1.全自动创建
	class Book(models.Model):
        title = models.CharField(max_length=32)
        authors = models.ManyToManyField(to='Author')
	class Author(models.Model):
        name = models.CharField(max_length=32)
	优势:自动创建第三张表 并且提供了add、remove、set、clear四种操作
	劣势:第三张表无法创建更多的字段 扩展性较差
 
2.纯手动创建
	 class Book(models.Model):
        title = models.CharField(max_length=32)
    class Author(models.Model):
        name = models.CharField(max_length=32)
    class Book2Author(models.Model):
        book = models.ForeignKey(to='Book')
        author = models.ForeignKey(to='Author')
        others = models.CharField(max_length=32)
        join_time = models.DateField(auto_now_add=True)
 	 优势:第三张表完全由自己创建 扩展性强
	 劣势:编写繁琐 并且不再支持add、remove、set、clear以及正反向概念
        
3.半自动创建
	 class Book(models.Model):
        title = models.CharField(max_length=32)
        authors = models.ManyToManyField(to='Author',
                          through='Book2Author',      									through_fields=('book','author')
                                        )
    class Author(models.Model):
        name = models.CharField(max_length=32)
    class Book2Author(models.Model):
        book = models.ForeignKey(to='Book', on_delete=models.CASCADE)
        author = models.ForeignKey(to='Author', on_delete=models.CASCADE)
        others = models.CharField(max_length=32)
        join_time = models.DateField(auto_now_add=True)
	 优势:第三张表完全由自己创建 扩展性强 正反向概念依然清晰可用
    劣势:编写繁琐不再支持add、remove、set、clear

图书管理系统

文件配置

DATABASES = {
    #     'default': {
    #         'ENGINE': 'django.db.backends.sqlite3',
    #         'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    #     }
    # }
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'db8',
        'HOST': '127.0.0.1',
        'PORT': 3306,
        'USER': 'root',
        'PASSWORD': '123',
        'CHARSET': 'utf8'
    }
}


STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static')
]

数据准备

from django.db import models


# Create your models here.

class Book(models.Model):
    title = models.CharField(max_length=32, verbose_name='书名')
    price = models.DecimalField(max_digits=8, decimal_places=2, verbose_name='价格')
    publish_time = models.DateField(auto_now_add=True)
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')


class Publish(models.Model):
    name = models.CharField(max_length=32, verbose_name='名字')
    address = models.CharField(max_length=32, verbose_name='地址')


class Author(models.Model):
    name = models.CharField(max_length=32, verbose_name='名字')
    age = models.IntegerField(verbose_name='年龄')
    author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)


class AuthorDetail(models.Model):
    phone = models.BigIntegerField(verbose_name='手机号码')
    address = models.CharField(max_length=32, verbose_name='家庭地址')

Views

from django.shortcuts import render, HttpResponse, redirect, reverse
from app01 import models
from django.http import JsonResponse


# Create your views here.


def home_func(request):
    return render(request, 'home.html')


def book_list(request):
    # 获取所有的图书数据
    book_obj = models.Book.objects.all()
    return render(request, 'book.html', locals())


def book_add(request):
    if request.method == 'POST':
        title = request.POST.get('title')
        price = request.POST.get('price')
        publish_time = request.POST.get('publish_time')
        publish_id = request.POST.get('publish_id')
        author_id_list = request.POST.getlist('author_id_list')
        if len(title) == 0 and len(price) == 0 and len(publish_id) == 0:
            return HttpResponse('请不要输入空')
        book_obj = models.Book.objects.create(title=title, price=price, publish_time=publish_time,
                                              publish_id=publish_id)
        book_obj.authors.add(*author_id_list)
        return redirect('book_list_view')
    publish_obj = models.Publish.objects.all()
    author_obj = models.Author.objects.all()
    return render(request, 'book_add.html', locals())


def book_edit(request, book_pk):
    edit_obj = models.Book.objects.filter(pk=book_pk).first()
    if request.method == 'POST':
        title = request.POST.get('title')
        price = request.POST.get('price')
        publish_time = request.POST.get('publish_time')
        publish_id = request.POST.get('publish_id')
        author_id_list = request.POST.getlist('author_id_list')
        models.Book.objects.filter(pk=book_pk).update(title=title, price=price, publish_time=publish_time,
                                                      publish_id=publish_id)
        edit_obj.authors.set(author_id_list)
        return redirect('book_list_view')
    publish_obj = models.Publish.objects.all()
    author_obj = models.Author.objects.all()
    return render(request, 'book_edit.html', locals())


def book_del(request):
    user_dict = {'code': 200, 'msg': '删除成功'}
    pk = request.POST.get('id')
    del_obj = models.Book.objects.filter(pk=pk).first()
    del_obj.authors.clear()
    models.Book.objects.filter(pk=pk).delete()
    return JsonResponse(user_dict)

urls.py

from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    # 首页
    path('home/', views.home_func, name='home_view'),
    path('book_list/', views.book_list, name='book_list_view'),
    path('book_add/', views.book_add, name='book_add_view'),
    path('book_edit/<int:book_pk>/', views.book_edit, name='book_edit_view'),
    path('book_del/', views.book_del, name='book_del_view')
]

home

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.js"></script>
    {% load static %}
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.css' %}">
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.js' %}"></script>

    {% block css %}

    {% endblock %}
</head>
<body>
<nav class="navbar navbar-inverse">
    <div class="container-fluid">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                    data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">BMS</a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <ul class="nav navbar-nav">
                <li class="active"><a href="#">图书 <span class="sr-only">(current)</span></a></li>
                <li><a href="#">作者 </a></li>
                <li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                       aria-expanded="false">更多 <span class="caret"></span></a>
                    <ul class="dropdown-menu">
                        <li><a href="#">Action</a></li>
                        <li><a href="#">Another action</a></li>
                        <li><a href="#">Something else here</a></li>
                        <li role="separator" class="divider"></li>
                        <li><a href="#">Separated link</a></li>
                        <li role="separator" class="divider"></li>
                        <li><a href="#">One more separated link</a></li>
                    </ul>
                </li>
            </ul>
            <form class="navbar-form navbar-left">
                <div class="form-group">
                    <input type="text" class="form-control" placeholder="书名">
                </div>
                <button type="submit" class="btn btn-default">查询</button>
            </form>
            <ul class="nav navbar-nav navbar-right">
                <li><a href="#">GJL</a></li>
                <li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-toggle="更多操作" role="button" aria-haspopup="true"
                       aria-expanded="false">Dropdown <span class="caret"></span></a>
                    <ul class="dropdown-menu">
                        <li><a href="#">Action</a></li>
                        <li><a href="#">Another action</a></li>
                        <li><a href="#">Something else here</a></li>
                        <li role="separator" class="divider"></li>
                        <li><a href="#">Separated link</a></li>
                    </ul>
                </li>
            </ul>
        </div><!-- /.navbar-collapse -->
    </div><!-- /.container-fluid -->
</nav>
<div class="container-fluid">
    <div class="row">
        <div class="col-lg-2">
            <ul class="list-group">
                <a href="{% url 'home_view' %}">
                    <li class="list-group-item list-group-item-success">首页展示</li>
                </a>
                <a href="{% url 'book_list_view' %}">
                    <li class="list-group-item list-group-item-info">图书列表</li>
                </a>
                <a href="#">
                    <li class="list-group-item list-group-item-warning">出版社列表</li>
                </a>
                <a href="#">
                    <li class="list-group-item list-group-item-warning">作者详情</li>
                </a>
                <a href="#">
                    <li class="list-group-item list-group-item-danger">更多详情</li>
                </a>
            </ul>
            <ul class="list-group">
                            <a href="{% url 'home_view' %}">
                                <li class="list-group-item list-group-item-success">首页展示</li>
                            </a>
                            <a href="{% url 'book_list_view' %}">
                                <li class="list-group-item list-group-item-info">图书列表</li>
                            </a>
                            <a href="#">
                                <li class="list-group-item list-group-item-warning">出版社列表</li>
                            </a>
                            <a href="#">
                                <li class="list-group-item list-group-item-warning">作者详情</li>
                            </a>
                            <a href="#">
                                <li class="list-group-item list-group-item-danger">更多详情</li>
                            </a>
                        </ul>
            <ul class="list-group">
                <a href="{% url 'home_view' %}">
                    <li class="list-group-item list-group-item-success">首页展示</li>
                </a>
                <a href="{% url 'book_list_view' %}">
                    <li class="list-group-item list-group-item-info">图书列表</li>
                </a>
                <a href="#">
                    <li class="list-group-item list-group-item-warning">出版社列表</li>
                </a>
                <a href="#">
                    <li class="list-group-item list-group-item-warning">作者详情</li>
                </a>
                <a href="#">
                    <li class="list-group-item list-group-item-danger">更多详情</li>
                </a>
            </ul>
        </div>
        <div class="col-lg-10">
            <div class="panel panel-success">
                <div class="panel-heading">
                    <h3 class="panel-title">BMS</h3>
                </div>
                <div class="panel-body">
                    {% block content %}
                        <div class="page-header">
                            <h1>图书管理系统 <small>更多详情敬请期待</small></h1>
                        </div>
                        <div class="jumbotron">
                            <h1>轮墓的知识花园</h1>
                            <p>...</p>
                            <p><a class="btn btn-primary btn-lg" href="#" role="button">Learn more</a></p>
                        </div>

                        <div class="row">
                        <div class="col-xs-6 col-md-3">
                            <a href="#" class="thumbnail">
                                <img src="https://img2.baidu.com/it/u=1129841463,2401390066&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=500"
                                     alt="...">
                            </a>
                        </div>
                        <div class="col-xs-6 col-md-3">
                            <a href="#" class="thumbnail">
                                <img src="https://img2.baidu.com/it/u=2619038096,3030384001&fm=253&fmt=auto&app=138&f=JPEG?w=628&h=500"
                                     alt="...">
                            </a>
                        </div>
                        <div class="col-xs-6 col-md-3">
                            <a href="#" class="thumbnail">
                                <img src="https://img2.baidu.com/it/u=3253953072,3458896830&fm=253&fmt=auto&app=138&f=JPEG?w=600&h=500"
                                     alt="...">
                            </a>
                        </div>
                        <div class="col-xs-6 col-md-3">
                            <a href="#" class="thumbnail">
                                <img src="https://img0.baidu.com/it/u=36613263,4180323811&fm=253&fmt=auto&app=138&f=JPEG?w=353&h=500"
                                     alt="...">
                            </a>
                        </div>
                    {% endblock %}
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

{% block js %}

{% endblock %}
</body>
</html>

book

{% extends 'home.html' %}

{% block content %}
    <h2 class="text-center">图书展示</h2>
    <div class="form-group">
        <a href="{% url 'book_add_view' %}" class="btn btn-success"> 图书添加</a>
    </div>
    <table class="table table-striped table-hover table-bordered">
        <thead>
        <tr>
            <th>编号</th>
            <th>书名</th>
            <th>价格</th>
            <th>日期</th>
            <th>出版社</th>
            <th>作者</th>
            <th>操作</th>
        </tr>
        </thead>
        <tbody>
        {% for book in book_obj %}
            <tr>
                <td>{{ forloop.counter }}</td>
                <td>{{ book.title }}</td>
                <td>{{ book.price }}</td>
                <td>{{ book.publish_time|date:'Y-m-d' }}</td>
                <td>{{ book.publish.name }}</td>
                <td>
                    {% for author_obj in book.authors.all %}
                        {% if forloop.last %}
                            {{ author_obj.name }}
                        {% else %}
                            {{ author_obj.name }},
                        {% endif %}
                    {% endfor %}
                </td>
                <td>
                    <a href="{% url 'book_edit_view' book.pk %}" class="btn btn-primary btn-xs">编辑</a>
                    <a href="#" class="btn btn-danger btn-xs delBtn" pk="{{ book.pk }}">删除</a>
                </td>
            </tr>
        {% endfor %}

        </tbody>
    </table>
    <nav aria-label="Page navigation" class="text-center">
        <ul class="pagination">
            <li>
                <a href="#" aria-label="Previous">
                    <span aria-hidden="true">&laquo;</span>
                </a>
            </li>
            <li><a href="#">1</a></li>
            <li><a href="#">2</a></li>
            <li><a href="#">3</a></li>
            <li><a href="#">4</a></li>
            <li><a href="#">5</a></li>
            <li>
                <a href="#" aria-label="Next">
                    <span aria-hidden="true">&raquo;</span>
                </a>
            </li>
        </ul>
    </nav>
{% endblock %}

{% block js %}
    <script src="/static/layer/layer.js"></script>
    <script>
        $('.delBtn').click(function () {
            var id = $(this).attr('pk')
            layer.confirm('您确认是否删除?', {
                    btn: ['确认', '取消'] //按钮
                }, function () {
                    $.ajax({
                        url: '/book_del/',
                        type: 'post',
                        data: {'id': id},
                        dataType: 'json',
                        success: function (res) {
                            if (res.code == 200) {
                                layer.msg(res.msg, {icon: 1}, function () {
                                    location.reload()
                                })
                            }
                        }
                    })
                },
            );
        })
    </script>
{% endblock %}

book_add

{% extends 'home.html' %}

{% block content %}
    <h2 class="text-center">图书添加页</h2>
    <form action="" method="post">
        <p> 书名:<input type="text" name="title" class="form-control"></p>
        <p> 价格:<input type="text" name="price" class="form-control"></p>
        <p> 日期:<input type="date" name="publish_time" class="form-control"></p>
        <p> 出版社名称:
            <select name="publish_id" id="" class="form-control">
                {% for publish in publish_obj %}
                    <option value="{{ publish.pk }}">{{ publish.name }}</option>
                {% endfor %}
            </select>
        </p>
        <p> 作者:
            <select name="author_id_list" id="" multiple class="form-control">
                {% for author in author_obj %}
                    <option value="{{ author.pk }}">{{ author.name }}</option>
                {% endfor %}
            </select>
        </p>
        <input type="submit" value="数据添加" class="btn btn-success btn-block">
    </form>

{% endblock %}

book_edit

{% extends 'home.html' %}

{% block content %}
    <h2 class="text-center">书籍编辑页</h2>
    <form action="" method="post">
        <p> 书名:<input type="text" name="title" class="form-control" value="{{ edit_obj.title }}"></p>
        <p> 价格:<input type="text" name="price" class="form-control" value="{{ edit_obj.price }}"></p>
        <p> 日期:<input type="date" name="publish_time" class="form-control" value="{{ edit_obj.publish_time|date:'Y-m-d' }}"></p>
        <p> 出版社名称:
            <select name="publish_id" id="" class="form-control">
                {% for publish in publish_obj %}
                    {% if edit_obj.publish == publish %}
                             <option value="{{ publish.pk }} " selected>{{ publish.name }} </option>
                    {% else %}
                             <option value="{{ publish.pk }}">{{ publish.name }} </option>
                    {% endif %}

                {% endfor %}
            </select>
        </p>
        <p> 作者:
            <select name="author_id_list" id="" multiple class="form-control">
                {% for author in author_obj %}
                    {% if author in edit_obj.authors.all %}
                            <option value="{{ author.pk }}" selected>{{ author.name }}</option>
                    {% else %}
                            <option value="{{ author.pk }}">{{ author.name }}</option>
                    {% endif %}
                {% endfor %}
            </select>
        </p>
        <input type="submit" value="数据编辑" class="btn btn-danger btn-block">
    </form>
{% endblock %}

Ajax

异步提交 局部刷新
ajax不是一门新的技术并且有很多版本 我们目前学习的是jQuery版本(版本无所谓 本质一样就可以)
	

基本语法
	$.ajax({
        url:'',  // 后端地址 三种填写方式 与form标签的action一致
        type:'post',  // 请求方式 默认也是get
        data:{'v1':v1Val, 'v2':v2Val},  // 发送的数据
        success:function (args) {  // 后端返回结果之后自动触发 args接收后端返回的数据
         	 $('#d3').val(args)
                                }
    })

Ajax提交json格式的数据

1.确保data对应的数据是json格式字符串
	data:JSON.stringify({})
2.修改数据编码格式(默认是urlencoded)
	contentType:'applicaton/json'

$.ajax({
            url: '',
            type: 'post',
           	data: JSON.stringify({'a': 1, 'b': 2, c: 3}),
            // 更改contenttype
    
    	    contentType:'application/json',
            success: function () {

            }
        })

Ajax提交文件数据

// 获取数据
        var username = $("#username").val();
        var password = $("#password").val();
        var myfile = $("#myfile")[0].files[0];

        // ajax提交文件数据,需要借助于form-data
        var myFormDataObj = new FormData();
        myFormDataObj.append('username', username);
        myFormDataObj.append('password', password);


        // 提交文件数据、
        myFormDataObj.append('myfile', myfile);

        $.ajax({
            url: '',
            type: 'post',
            data: myFormDataObj,
            // 更改contenttype
            // 需要加两个参数
            contentType: false, // 告诉浏览器不要给我修改任何的编码格式
            processData:false,// 告诉浏览器不要对我的数据进行任何的修改或者干扰
            success: function () {

            }
        })
            
            
##################################################################################
总结:
       1. var myFormDataObj = new FormData();
       2. 获取数据,然后添加到myFormDataObj
            myFormDataObj.append('password', password);
            // 提交文件数据、
            myFormDataObj.append('myfile', myfile);
       3. 修改参数:
            contentType: false, // 告诉浏览器不要给我修改任何的编码格式
            processData:false,// 告诉浏览器不要对我的数据进行任何的修改或者干扰
       4. 在django后端,普通数据还是在request.POST中,文件数据在
            request.FILES里面
##################################################################################            

django自带的序列化组件(了解)

# 让你从数据库总查询几条数据,然后序序列化给到前端,列表套字段的形式[{},{},{}]

# 传统方式:
def ab_ser(request):
    user_list = models.Book.objects.all()
     print(user_list) # <QuerySet [<Book: Book object (2)>, <Book: Book object (3)>, <Book: Book object (4)>]>
     book_list = []
     for obj in user_list:
         tmp = {
             'pk':obj.pk,
             'username':obj.username,
         }
         book_list.append(tmp)
    return JsonResponse(book_list,safe=False)

# 序列化组件
from django.core import serializers
def ab_ser(request):
    user_list = models.Book.objects.all()
    book_list = serializers.serialize('json', user_list)  # 了解
    return JsonResponse(book_list,safe=False)

批量操作数据

def ab_bk_func(request):

    """直接循环插入 10s 500条左右"""
    book_obj_list = []  # 可以用列表生成式[... for i in ... if ...]     生成器表达式(... for i in ... if ...)
    for i in range(1, 100000):
        book_obj = models.Books01(title='第%s本书' % i)  # 单纯的用类名加括号产生对象
        book_obj_list.append(book_obj)
    # 批量插入数据
    models.Books01.objects.bulk_create(book_obj_list)
    """使用orm提供的批量插入操作 5s 10万条左右"""
    
    
    
    
    # 2.查询出所有的表中并展示到前端页面
    book_queryset = models.Books01.objects.all()
    return render(request, 'BkPage.html', locals())

分页器的使用

封装代码

# 以后再实际项目中,会经常用到非django 的第三方工具,对于这种第三方工具,我们一般会在django中新建一个文件夹叫 utils,以后很多封装的代码都使用用面向对象的写法

class Pagination(object):
    def __init__(self, current_page, all_count, per_page_num=2, pager_count=11):
        """
        封装分页相关数据
        :param current_page: 当前页
        :param all_count:    数据库中的数据总条数
        :param per_page_num: 每页显示的数据条数
        :param pager_count:  最多显示的页码个数
        """
        try:
            current_page = int(current_page)
        except Exception as e:
            current_page = 1
 
        if current_page < 1:
            current_page = 1
 
        self.current_page = current_page
 
        self.all_count = all_count
        self.per_page_num = per_page_num
 
        # 总页码
        all_pager, tmp = divmod(all_count, per_page_num)
        if tmp:
            all_pager += 1
        self.all_pager = all_pager
 
        self.pager_count = pager_count
        self.pager_count_half = int((pager_count - 1) / 2)
 
    @property
    def start(self):
        return (self.current_page - 1) * self.per_page_num
 
    @property
    def end(self):
        return self.current_page * self.per_page_num
 
    def page_html(self):
        # 如果总页码 < 11个:
        if self.all_pager <= self.pager_count:
            pager_start = 1
            pager_end = self.all_pager + 1
        # 总页码  > 11
        else:
            # 当前页如果<=页面上最多显示11/2个页码
            if self.current_page <= self.pager_count_half:
                pager_start = 1
                pager_end = self.pager_count + 1
 
            # 当前页大于5
            else:
                # 页码翻到最后
                if (self.current_page + self.pager_count_half) > self.all_pager:
                    pager_end = self.all_pager + 1
                    pager_start = self.all_pager - self.pager_count + 1
                else:
                    pager_start = self.current_page - self.pager_count_half
                    pager_end = self.current_page + self.pager_count_half + 1
 
        page_html_list = []
        # 添加前面的nav和ul标签
        page_html_list.append('''
                    <nav aria-label='Page navigation>'
                    <ul class='pagination'>
                ''')
        first_page = '<li><a href="?page=%s">首页</a></li>' % (1)
        page_html_list.append(first_page)
 
        if self.current_page <= 1:
            prev_page = '<li class="disabled"><a href="#">上一页</a></li>'
        else:
            prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,)
 
        page_html_list.append(prev_page)
 
        for i in range(pager_start, pager_end):
            if i == self.current_page:
                temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,)
            else:
                temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,)
            page_html_list.append(temp)
 
        if self.current_page >= self.all_pager:
            next_page = '<li class="disabled"><a href="#">下一页</a></li>'
        else:
            next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,)
        page_html_list.append(next_page)
 
        last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,)
        page_html_list.append(last_page)
        # 尾部添加标签
        page_html_list.append('''
                                           </nav>
                                           </ul>
                                       ''')
        return ''.join(page_html_list)

后端

def index(request):
    from utils.mypage import Pagination       
    current_page = request.GET.get('page', 1)
    book_queryset = models.Book.objects.all()
    all_count = book_queryset.count()
    per_page_num = settings.PER_PAGE_NUM  # 我们一般把每页显示的数据条数定义在settings里
    page_obj = Pagination(current_page, all_count, per_page_num, pager_count=11)
    book_list = book_queryset[page_obj.start:page_obj.end]
    page_html = page_obj.page_html()

    return render(request, 'Page.html', locals())

前端

{% for book in book_list %}
    <div class="text-center">
        {{ book.title }}
    </div>
{% endfor %}



<nav aria-label="Page navigation" class="text-center">
    <ul class="pagination ">
        <li>
            <a href="#" aria-label="Previous">
                <span aria-hidden="true">&laquo;</span>
            </a>
        </li>
            {{ page_html|safe }}
        <li>
            <a href="#" aria-label="Next">
                <span aria-hidden="true">&raquo;</span>
            </a>
        </li>
    </ul>
</nav>

cookie与session

HTTP协议四大特性:
	1.基于请求响应
	2.基于TCP,IP作用于应用层之上协议
	3.无状态
		服务端无法识别客户端的状态
	4.无连接
'''
cookie
	保存在客户端上跟用户信息(状态)相关的数据
session
	保存在服务端上跟用户信息(状态)相关的数据
ps:session的工作需要依赖于cookie
需要使用cookie(客户端浏览器有权是否保存cookie)

Django操作cookie

如果想让客户端浏览器保存cookie需要HttpResponse对象调用方法
obj = HttpResponse()或者render()或者redirect()或者JsonRepsonse()
obj.操作cookie的方法
return obj
获取Cookie
request.COOKIES['key']
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
参数:
- default: 默认值
- salt: 加密盐
- max_age: 后台控制过期时间

设置Cookie
rep = HttpResponse(...)
rep = render(request, ...)

rep.set_cookie(key,value,...)
rep.set_signed_cookie(key,value,salt='加密盐', max_age=None, ...)
参数:
- key, 键
- value='', 值
- max_age=None, 超时时间
- expires=None, 超时时间(IE requires expires, so set it if hasn't been already.)
- path='/', Cookie生效的路径,/ 表示根路径,特殊的:根路径的cookie可以被任何url的页面访问
- domain=None, Cookie生效的域名
- secure=False, https传输
- httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)

删除Cookie
def logout(request):
    rep = redirect("/login/")
    rep.delete_cookie("user")  # 删除用户浏览器上之前设置的usercookie值
    return rep
                     
## 创建新Django项目day63,应用名app01
需求,当直接访问login,登录成功之后直接跳到home首页;当直接访问index页面,需要先登录,登录成功之后直接跳到index页面
1.urls.py
    path('home/', views.home),
    path('index/', views.index),
    path('login/', views.login),
2.views.py
def index(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username == 'gjl' and password == '123':
            tage_path = request.GET.get('next')
            if tage_path:   # 如果有值,跳转到指定页面
                obj = redirect(tage_path)
            else:
                obj = redirect('home_view') # 没有值跳转登录页面
            obj.set_cookie('username', username)
            return obj
    return render(request, 'index.html')

def login_auth(func):
    def inner(request, *args, **kwargs):
        tage_path = request.path_info
        if request.COOKIES.get('username'):
            res = func(request, *args, **kwargs)
            return res
            # 跳转到登录页面,同时携带要想访问的地址         
        return redirect('{}{}{}'.format(reverse('index_view'), '?next=', tage_path))

    return inner                     
@login_auth
def home(request):
    return HttpResponse('home页面,只有登录才能查看')
@login_auth
def index(request):
    return HttpResponse('index页面,只有登录才能查看')
3.templates创建html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    {% load static %}
    <script src="{% static 'jquery.js' %}"></script>
    <link rel="stylesheet" href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
    <script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</head>
<body>
<form action=""method="post">
    <p>username:
        <input type="text" name="username">
    </p>
    <p>password:
        <input type="text" name="password">
    </p>
    <input type="submit" value="登录">
</form>
</body>
</html>
4.启动Django,浏览器访问http://127.0.0.1:8000/login 和http://127.0.0.1:8000/index

Django操作session

请求来之后服务端产生随机字符串并发送给客户端保存,服务端存储随机字符串与用户信息的对应关系,之后客户端携带随机字符串,服务端自动校验
1.Django默认的session失效时间14天
2.客户端会接收到键值对,键默认是sessionid,值是加密的随机字符串(令牌)

request.session['name'] = 'jason'
	1.Django自动产生一个随机字符串返回给客户端(对name加密)
    2.往Django_session创建数据(对jason加密)
request.session.get('name')
	1.自动从请求中获取sessionid对应的随机字符串
    2.拿着随机字符串去django_session中匹配数据
    3.如果匹配上,还会自动解密数据并展示
    
##################################################################################################    
 
# session的存储位置有5种模式
1. 数据库Session
SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)

2. 缓存Session
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 引擎
SESSION_CACHE_ALIAS = 'default'                            # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置

3. 文件Session
SESSION_ENGINE = 'django.contrib.sessions.backends.file'    # 引擎
SESSION_FILE_PATH = None                                    # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() 

4. 缓存+数据库
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'        # 引擎

5. 加密Cookie Session
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'   # 引擎

# 其他公用设置项:
SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
SESSION_COOKIE_PATH = "/"                               # Session的cookie保存的路径(默认)
SESSION_COOKIE_DOMAIN = None                             # Session的cookie保存的域名(默认)
SESSION_COOKIE_SECURE = False                            # 是否Https传输cookie(默认)
SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支持http传输(默认)
SESSION_COOKIE_AGE = 1209600                             # Session的cookie失效日期(2周)(默认)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否关闭浏览器使得Session过期(默认)
SESSION_SAVE_EVERY_REQUEST = False                       # 是否每次请求都保存Session,默认修改之后才保存(默认)

session的相关方法
# 获取、设置、删除Session中数据
request.session['k1']
request.session.get('k1',None)
request.session['k1'] = 123
request.session.setdefault('k1',123) # 存在则不设置
del request.session['k1']


# 所有 键、值、键值对
request.session.keys()
request.session.values()
request.session.items()
request.session.iterkeys()
request.session.itervalues()
request.session.iteritems()

# 会话session的key
request.session.session_key

# 将所有Session失效日期小于当前日期的数据删除
request.session.clear_expired()

# 检查会话session的key在数据库中是否存在
request.session.exists("session_key")

# 设置会话Session和Cookie的超时时间
request.session.set_expiry(value)
    * 如果value是个整数,session会在些秒数后失效。
    * 如果value是个datatime或timedelta,session就会在这个时间后失效。
    * 如果value是0,用户关闭浏览器session就会失效。
    * 如果value是None,session会依赖全局session失效策略。

# 删除当前会话的所有Session数据,只删除数据库
request.session.delete()
  
# 删除当前的会话数据并删除会话的Cookie,数据库也删
request.session.flush() 
    这用于确保前面的会话数据不可以再次被用户的浏览器访问
    例如,django.contrib.auth.logout() 函数中就会调用它。

案例:
1.urls.py中
    path('setSession/',views.set_session),
    path('getSession/',views.get_session),
2.views.py中
def set_session(request):
    request.session['name'] = 'jason'
    return HttpResponse('设置session')
def get_session(request):
    print(request.session.get('name'))
    return HttpResponse('获取session')
3.启动Django,浏览器访问127.0.0.1:8000/setSession 和 127.0.0.1:8000/getSession

CBV中加装饰器

from django import views
from django.utils.decorators import method_decorator
# @method_decorator(login_auth,name='get')    # 方式二:给这个类的某一个方法加装饰器
# @method_decorator(login_auth,name='post')
class UserList(views.View):
    # @method_decorator(login_auth)  # 方式三:给类的所有方法加
    def dispatch(self, request, *args, **kwargs):
        obj=super().dispatch(request, *args, **kwargs)
        return obj

    @method_decorator(login_auth)   # 方式一:给这个方法加
    def get(self,request):
        return HttpResponse('我是用户列表')

    def post(self,request):
        return HttpResponse('我是用户列表')

django中间件

Django中间件类似于Django的门户,所有的请求来和响应走都必须经过中间件
Django默认自带7个中间件,每个中间件都有各自负责的功能
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',
]
Django中间件出了默认之外,还支持自定义中间件(不限制数量)
Django中间件使用场景
	全局相关的功能:全局用户身份校验,全局用户黑名单校验,全局用户访问频率校验
Django自定义中间件中可以有5个方法
	process_request
    process_response
    process_view
    process_template_response
    process_exception

1.process_request
	1.请求来的时候会按照配置文件中注册了的中间件,从上往下一次执行每一个中间件里面的process_request方法,如果没有直接跳过
    2.该方法如果返回了HttpResponse对象,那么请求不会再往后执行,原路返回
2.process_response
	1.响应走的时候会按照配置文件中注册了的中间件,从下往上一次执行每一个中间件里面的process_response,如果没有直接跳过
    2.该方法有2个形参,reqeust和response,默认情况下应该返回response
    3.该方法也可以自己返回HttpResponse对象,相当于狸猫换太子
补充:如果请求的过程中,process_request方法直接返回了HttpResponse对象,那么会原地执行同级别的process_response方法返回
3.process_view
	当路由匹配成功之后,执行视图函数之前,自动触发
        该方法有四个参数
        request是HttpRequest对象。
        view_func是Django即将使用的视图函数。 (它是实际的函数对象,而不是函数的名称作为字符串。)
        view_args是将传递给视图的位置参数的列表.
        view_kwargs是将传递给视图的关键字参数的字典。 view_args和view_kwargs都不包含第一个视图参数(request)。
        Django会在调用视图函数之前调用process_view方法。
4.process_excption
	当视图函数报错之后,自动触发
        该方法两个参数:
        一个HttpRequest对象
        一个exception是视图函数异常产生的Exception对象。
5.process_template_response
        当视图返回的数据对象中含有rander属性对应的render方法才会触发
		它的参数,一个HttpRequest对象,response是TemplateResponse对象(由视图函数或者中间件产生)。
案例:
    1.app01下创建utils目录,并创建myadd.py
        from django.utils.deprecation import MiddlewareMixin
        from django.shortcuts import HttpResponse


        class MyMdd1(MiddlewareMixin):
            def process_request(self, request):
                print('MyMdd1 process_request')
                # return HttpResponse('我怀疑你有问题')

            def process_response(self, request, response):
  
                return response
                # return HttpResponse('狸猫换太子')

            def process_view(self, request, view_func, view_args, view_kwargs):
                print('MyMdd1 process_view')

            def process_excption(self, request, exception):
                print('MyMdd1 process_exception')

            def process_template_response(self, request, response):
                print('MyMdd1 process_template_response')
                return response


        class MyMdd2(MiddlewareMixin):
            def process_request(self, request):
                print('MyMdd2 process_request')

            def process_response(self, request, response):
                print('MyMdd2 process_response')
                return response

            def process_view(self, request, view_func, view_args, view_kwargs):
                print('MyMdd2 process_view')

            def process_excption(self, request, exception):
                print('MyMdd2 process_exception')

            def process_template_response(self, request, response):
                print('MyMdd2 process_template_response')
                return response
	2.settings的MIDDLEWARE中添加
        'app01.utils.mymdd.MyMdd1',
        'app01.utils.mymdd.MyMdd2',
	3.urls.py中添加
            # Django中间件
        path('getMd',views.get_md)
    4.views.py中添加
        def get_md(request):
            print('from get_md')
            def render():
                return HttpResponse("hahaha")
            obj = HttpResponse('嘿嘿嘿')
            obj.render = render
            # return HttpResponse('get md')
            return obj
	5.启动Django,浏览器访问127.0.0.1:8000/getMd,
        pycharm上打印
            MyMdd1 process_request
            MyMdd2 process_request
            MyMdd1 process_view
            MyMdd2 process_view
            from get_md
            MyMdd2 process_template_response
            MyMdd1 process_template_response
            MyMdd2 process_response
            MyMdd1 process_response
		浏览器上打印
        	hahaha

csrf跨站请求伪造

钓鱼网站:模仿一个正规的网站 让用户在该网站上做操作 但是操作的结果会影响到用户正常的网站账户 但是其中有一些猫腻
	eg:英语四六级考试需要网上先缴费 但是你会发现卡里的钱扣了但是却交到了一个莫名其妙的账户 并不是真正的四六级官方账户
        
模拟钓鱼网站案例:转账案例
	内部隐藏标签
 
思考:如何区分真假网站页面发送的请求

csrf校验策略

在提交数据的位置添加唯一标识 

1.form表单csrf策略
	form表单内部添加 {% csrf_token %}
2. ajax如何规避csrf的验证?
  $('.btn').click(function () {
        $.ajax({
            url: '',
            type: 'post',
            // 方式1:
            {#data: {'username': 'kevin', 'csrfmiddlewaretoken': $("[name='csrfmiddlewaretoken']").val()},#}
            // 方式2
            {#data: {'username': 'kevin', 'csrfmiddlewaretoken': '{{ csrf_token }}'},#}
            data: {'username': 'kevin'},
            // 方式3,使用官方提供的js文件
            success: function (res) {

            }
        })
    })

django官方提供的js文件

function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
var csrftoken = getCookie('csrftoken');


// 每一次都这么写太麻烦了,可以使用$.ajaxSetup()方法为ajax请求统一设置。

function csrfSafeMethod(method) {
  // these HTTP methods do not require CSRF protection
  return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

$.ajaxSetup({
  beforeSend: function (xhr, settings) {
    if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
      xhr.setRequestHeader("X-CSRFToken", csrftoken);
    }
  }
});

auth认证相关模块及操作

# 1.auth模块常用方法
from django.contrib import auth
from django.contrib.auth.models import User

# 2.authenticate():提供了用户认证功能,即验证用户名以及密码是否正确,一般需要username 、password两个关键字参数,如果认证成功(用户名和密码正确有效),便会返回一个 User 对象。
  user_obj = authenticate(username='usernamer',password='password')

3.
'''
	create_user():创建普通用户,需要提供必要参数(username、password)等
	create_superuser():创建管理员,需要提供必要参数(username、password、email='邮箱')等
'''
	User.objects.create_user(username=username, password=password,...)
    user = User.objects.create_superuser(username='用户名',password='密码',email='邮箱',...)

# 4.is_authenticated():判断用户是否登录
	request.user.is_authenticated
    

# 5.login_requierd():用来快捷的给某个视图添加登录校验
   from django.contrib.auth.decorators import login_required
    	    @login_required(login_url='/login/')  局部配置 
            @login_required						全局配置
             配置文件中LOGIN_URL = '/login/'
    
# 6.check_password(password):检查密码是否正确的方法,需要提供当前请求用户的密码,密码正确返回True,否则返回False。
    request.user.check_password(原密码)
    
    
# 7.set_password(password):修改密码的方法,接收 要设置的新密码 作为参数, 设置完一定要调用用户对象的save方法!!!
	request.user.set_password(新密码)
 	request.user.save()
    
# 8.login():实现一个用户登录的功能。它本质上会在后端为该用户生成相关session数据    
        user_obj = auth.authenticate(request, username=username, password=password)
        if user_obj:
        auth.login(request, user_obj)
	'''
		执行过auth.login操作之后,我们就可以通过request.user拿到当前登录的用户对象
	'''      
# logout(request):退出登录        
        auth.logout(request)

扩展auth_user表

还想使用auth模块的功能 并且又想扩展auth_user表的字段
# 替换auth_user表
	步骤1:模型层编写模型类继承AbstractUser
   from django.contrib.auth.models import AbstractUser
       class UserInfo(AbstractUser):
        # 填写AbstractUser表中没有的字段
        phone = models.BigIntegerField()
        desc = models.TextField()
 	步骤2:一定要在配置文件中声明替换关系
        AUTH_USER_MODEL = 'app01.UserInfo'
	ps:替换还有一个前提 就是数据库迁移没有执行过(auth相关表没有创建)
posted @ 2023-05-17 20:31  秋洛尘  阅读(37)  评论(0)    收藏  举报