django

django框架

1.创建django项目
django-admin startproject mysite
 目录下会出现
	mysite文件夹
    	manage.py
        mysite文件夹
        	__init__.py
            settings.py   配置
            urls.py       路由 --> URL和函数的对应关系
            wsgi.py       runserver命令就使用wsgiref模块做简单的web server
2.启动
cd  mysite

python3 manage.py runserver

127.0.0.1:8000输入到浏览器中 出现It worked

3.创建应用   
#Next, start your first app by running python manage.py startapp [app_label].

python3 manage.py startapp app01(应用名)
应用名字要见名知意

应用

django 是一款专门用来开发app的web框架

一个app就是一个独立的功能模块

#创建应用一定要去配置文件中注册!!!!

创建文件时可以注册,但是只能注册一个

DEBUG = True     #上线之后改成False

主要文件介绍

--mysite x项目文件夹
	--mysite文件夹
    	--settings.py       配置文件
        --urls.py           路由与视图函数对应关系(路由层)
    	--wsgi.py            wsgiref模块(不考虑)
        --manage.py          django的入口文件
		--db.sqlite3        django自带的sqlite3数据库(小型数据库)
        --admin.py		     django后台管理
        --apps.py             注册使用
        --migrations.py文件夹  数据库迁移记录
        --models.py            数据库相关的模型类(orm)
        --tests.py            测试文件
        --view.py             视图函数(视图层)

命令行与pycharm创建的区别

命令行创建的不会自动有templates

视图函数

视图函数一定要有一个形参

形参就是请求对象

视图函数要有一个返回值

返回值是一个HttpResponse对象

def ab(request):
    return HttpResponse('ab')

三板斧

HttpResponse
	返回字符串
def index(request):
    return HttpResponse('aaaa')

render
	返回html文件的
def index(request):
    return render(request,'bbbbbbb.html')  #自动去tempaltes文件夹下查找文件

 
redirect
	重定向
    	return redirect('https://www.mzitu.com')  别人的网站
    	return redirect('/home/')     自己瞎写的网站,只用写后缀

静态文件配置

html文件默认存在templates文件夹下
网站所使用的静态文件默认都放在static文件夹下
前端已经写好的 能够直接调用使用的文件都可以称之为静态文件
	网站写好的js文件
    网站写好的css文件
    网站用到的图片文件
    第三方前端框架
    
    拿来就能用的
    
static文件夹要自己创建,文件夹下最好对css js等进行分类

在浏览器中输入url能够看到对应的资源
是因为后端提前开设了该资源的接口

如果访问不到资源 说明后端没有开设该资源的接口 


静态文件配置(需要记忆)

STATIC_URL = '/static/'   #类似于访问静态文件的令牌,想要访问静态文件,必须以static开头

# 静态文件配置
STATICFILES_DIRS = [
    os.path.join(BASE_DIR,'static'),] #静态文件夹的路径


#动态解析
{% load static %}
    <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>

#写django项目时,可能会出现后端代码改了但是前端页面没变化的情况
1.同一个端口开了好几个django项目
 一直在跑的是第一个django项目
    
2.浏览器缓存的问题
 settings
    network
    	disable cache 打钩

form表单默认是get请求数据
http://127.0.0.1:8000/login/?username=&password=
        
from表单action参数
	1.不写  默认朝当前所在的url提交数据
    2.全写 指名道姓
    3.<form action="">只写后缀<form action=" login">
    
    
报错403就把下面的注释掉
MIDDLEWARE 
    'django.middleware.csrf.CsrfViewMiddleware',

request对象方法

书写方式:
    # print(request.method)  以全大写字符串的形式返回请求方式
    # request.POST           获取用户post请求提交的普通数据不包含文件
	# request.POST.get       只会获取列表最后一个元素
	# request.POST.getlist   获取列表全部
    # request.GET            获取用户get请求提交的普通数据不包含文件
    # request.GET.get        获取用户列表最后一个元素
    # request.GET.getlist    获取列表全部 
    
    ##  get请求携带的数据是有限制的 4kb左右   
    ##  post没限制  ###post可以加密
常规书写:
def login(request):
    if request.method == 'POST':
        return HttpResponse('sss')
    return render(request,'login.html')

获取列表元素:
username = request.POST.getlist('username')
print(usrname,type(username))
hobby = request.POST.getlist('hobby')
print(hobby,type(hobby))

django链接mysql

## settings当中的数据需要修改
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'day51',
        'USER': 'root',
        'PASSWORD':961123,
        'HOST': '127.0.0.1',
        'PORT': 3306,
        'CHARSET': 'utf8'
    }
}

2.代码声明
django默认用的是mysqldb模块链接MySQL
但是该模块的兼容性不好 需要手动改为用pymysql链接

## 项目名下的init 或者任意的应用名下的init文件中书写以下代码都可以

import pymysql
pymysql.install_as_MySQLdb()


没有装pymysql就输入  pip install 模块名字  安装模块


Django ORM

ORM.   对象关系映射
作用: 能够让一个不用sql语句的小白也能够操作数据库
弊端: 封装程度太高 有时候sql语句的效率偏低 需要自己写SQL语句

类            表

对象          记录

对象属性      记录某个字段对应的值



先去models.py中书写一个类
class User(models.Model):
    #创建一个自增的主键 等价于 id int primary_key uto_increment
    id = models.AutoField(primary_key=True)

    #username varchar(32)
    username = models.CharField(max_length=32,verbose_name='用户名')
    CharField必须要有参数 
    verbose_name所有字段都可以有也可以没有这个参数,它是对字段的解释
    #username int
    psaaword = models.IntegerField()
    
2.数据库迁移命令
python3 manage.py makemigrations  将操作记录记录在migration里,数据库迁移记录

python3 manage.py migrate 将操作真正的同步到数据库中

字段的增删改查

#字段的增加
1.控制台设置默认值
2.info = models.CharField(max_length=32,verbose_name='个人简介',null=True)#默认为空
3.hobby = models.CharField(max_length=32,verbose_name='兴趣',default='study')#设置默认值

#字段的删

直接注释  执行数据库迁移命令

数据的增删改查

查  res = models.User.objects.filter(username = username)
						类似于where
支持索引取值  支持切片 不支持负数  不推荐索引取值,推荐如下
user_obj = models.User.objects.filter(username = username).first()
(心机boy,源代码就是用的索引)

from app01 import models
res = models.User.objects.filter(username = username)
user_obj = res[0]
print(user_obj)
print(user_obj.username)    #得到用户名
print(user_obj.password) 	#得到密码      不可思议

数据的增加

models中需要有以下代码,返回值就可以返回对象本身
def __str__(self):
    return '%s'%self.username

view中
def reg(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        # 第一种增加
        # 直接获取用户数据存入数据库  
    	from app01 import models
        res = models.User.objects.create(username=username, password=password)
        # 返回值就是当前被创建对象本身
        print(res,res.username,res.password)
        
        #第二种 
        from app01 import models
        user_obj = models.User(username=username,password=password)
        user_obj.save()
        
#查:
第一种:
    data = models.User.objects.filter()
第二种:  
    data = models.User.objects.all()
    
# 手动拼接路径
<a href="/edit_user/?user_id={{user_obj.id}}" class="btn btn-primary btn-xs">编辑</a>
    
#改    
# 修改数据方式一:批量更新
models.User.objects.filter(id=edit_id).update(username=username,password=password)
只修改被修改的字段
# 修改数据方式二:单独更新
edit_obj=models.User.objects.filter(id=edit_id).first()
edit_obj.username = username
edit_obj.password = password
edit_obj.save()
当字段特别多的时候,效率特别低
从头到尾将数据的所有字段全部更新一遍 无论字段是否被修改


#删
删除数据内部并不是真正的删除,会给数据添加一个表示字段用来表示当前数据是否被删除,如果数据被删了仅仅是字段修改一个状态
#批量删除
models.User.objects.filter(id=delete_id).delete()

orm中如何创建表关系

from django.db import models

# Create your models here.

class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8,decimal_places=2)
    #图书和出版社是一对多 外键放在书表里面 字段对应的是ForeignKey,orm会自动在字段的后面加_id
    publish = models.ForeignKey(to='Publish')
    #作者和图书是多对多的关系,authors是个虚拟字段,主要用来告诉orm
    # 书籍表和作者表是多对多关系。orm会自动创建第三张表关系
    authors = models.ManyToManyField(to='Author')

class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)

class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
#作者与作者详情是一对一关系  字段OneToOneField,字段也会自动加_id
    author_detail =models.OneToOneField(to='AuthorDetail')

class AuthorDetail(models.Model):
    phone = models.BigIntegerField()
    addr = models.CharField(max_length=32)


    
总结:
orm如何定义三种关系
1.一对多
publish = models.ForeignKey(to='Publish')
2.多对多
authors = models.ManyToManyField(to='Author')
3.一对一
author_detail =models.OneToOneField(to='AuthorDetail')

ForeignKey和OneToOneField会自动在字段后面加_id后缀
# 外键在django1.x版本中默认都是级联更新的
# 多对多的表关系可以有好几种创建方式 

django请求生命周期流程图

缓存数据库
	提前已经将想要的数据准备好  拿来就用
    提高效率和响应时间

路由层

#路由方法
路由匹配
    url(r'a', views.a),
    url(r'ab', views.aa),
url方法
第一个参数是正则表达式
	只要第一个参数正则表达式能够匹配到内容,就会立刻停止,永远访问不到ab。
    
输入url的时候会默认加/
	django内部会做重定向 匹配不到 就加/再来一次

# 取消自动加/
# APPEND_SLASH = False

无名分组

#正则
url(r'^ab/[0-9]{4}/$', views.ab)
# 分组:小括号括起来
url(r'^a/(\d+)/', views.a)

# 无名分组就是将括号内正则表达式匹配到的内容当做位置参数传递给后面的视图函数
def a(request,xx):
    print(xx)
    return HttpResponse('a')


有名分组

url(r'^ab/(?P<year>\d+)', views.ab)
def a(request,year):
    print(year)
    return HttpResponse('a')
# 有名分组就是将括号内正则表达式匹配到的内容当做关键字参数传递给后面的视图函数

# 无名有名不能混用
# 但是同一个分组可以使用多次
 url(r'^ab/(?P<year>\d+)/(?P<month>\d+)/(?P<day>\d+)', views.ab)
    
def ab(request,*args,**kwargs):
    print(args)
    print(kwargs)
    return HttpResponse('ab')

反向解析

通过一些方法得到一个结果  该结果可以直接访问对应的url触发视图函数

#先给路由器起一个别名
url(r'^func/',views.func,name='000'),
#前端反向解析
<a href="{% url '000' %}">1</a>
#后端反向解析
掉一个模块reverse
def home (request):
    print(reverse('000'))
    return render(request,'home.html')

无名有名分组反向解析

#无名分组
url(r'^index/(\d+)/',views.index,name='xxx'),

#前端
<a href="{% url 'xxx' 123%}">1</a>
					 #A
#后端
 print(reverse('xxx',args=(1,)))

  #A 数字一般放的是数据的主键值 用来做数据的编辑和删除



#有名分组
url(r'^func/(?P<year>\d+)/',views.func,name='ooo')

#前端
<a href="{% url 'xxx' 123 %}">1</a>
#后端
print(reverse('ooo', kwargs={'year':123,'month':10} )

路由分发

每一个应用都可以有自己的templates文件夹  urls.py文件夹  static文件夹 

# 总路由

#正常版本
from app01 import urls as app01_urls
urlpartterns = {
    url(r'^admin/',admin.site.urls),
    url(r'^admin/',include(app01_urls)),
    url(r'^admin/',include(app02_urls))
}
# 简易版本
url(r'^app01/',include('app01.urls')),
url(r'^app02/',include('app01.urls'))
##总路由里的url不可以以$结尾


# 子路由
子路由的urls正常写  调用时的代码不要忘记修改文件名

名称空间(了解)

正常情况下的反向解析是没有办法自动识别前缀的
url(r'^app01/',include('app01.urls',namespace='app01')),url(r'^app02/',include('app02.urls',namespace='app02')),

伪静态

将动态网页伪装成静态
伪装的目的在于增大本网站的seo查询力度
并且增加搜索引擎收藏本网站的几率

虚拟环境

每创建一个虚拟环境就类似于重新下载了一个纯净的python解释器
不要创建太多  太占空间

开发时会配备一个requirements。txt文件 里面书写了该项目所有的模块
一键安装即可

django版本区别

2.x 和3.x中的re_path就是url
path第一个参数不支持正则 但是他的内部支持五种转换器

#
模型层里面1.X外键默认都是级联更新删除的
但是到了2.X和3.X中需要自己手动配置参数

视图层

三板斧

HttpResponse
	返回字符串类型
render
	返回html页面 并且在返回给浏览器之前还可以给html文件传值
redirect
	重定向
    
    
视图函数必须要返回一个HttpResponse对象

JsonResponse对象

json格式的数据的用途:
	前后端数据交互需要用到json作为过度 实现跨语言传输数据
from django.http import JsonResponse
def ab_json(request):
#字典
    user_dict = {'username':'aaaaaa啊啊啊','password':'ee'}
    return JsonResponse(user_dict,json_dumps_params={'ensure_ascii':False})
#列表
    l=[111,22]
    return JsonResponse(l,safe =False) 

默认只能序列化字典,序列化其他需要加上safe参数
(不会看源码,活该加班)

form表单上传及后端如何操作

def ab_file(request):
    if request.method == 'POST':
        #获取文件数据
        # print(request.FILES)
        file_obj = request.FILES.get('file')
        # print(file_obj.name)
        with open(file_obj.name,'wb') as f:
            for line in file_obj:
                f.write(line)
    return render(request,'ab_file.html')


<form action="" method="post" enctype="multipart/form-data">
    <p>username: <input type="text" name="username">   </p>
    <p>file: <input type="file" name="file"> </p>
    <input type="submit">
    
</form>

request对象方法补充

都是获取路由的
print(request.path)            获取完整url
print(request.path_info)	   获取完整url
print(request.get_full_path()) 能够获取完整的url及问号后面的参数

request.body				原生的浏览器发来的二进制数据

FBV与CBV

视图函数既可以是函数也可以是类
CBV和FBV在本质上是一样的
CBV特点
	能够直接根据请求方式的不同直接匹配到对应的方法执行
    @classonlymethod
    def as_view(cls, **initkwargs):
        """
        cls 就是我们自己写的类
        Main entry point for a request-response process.
        """
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
			#self = MyLogin(**initkwargs)
            return self.dispatch(request, *args, **kwargs)
        #看源码只要看到了self,就要去想self是谁
     return view

CBV的精髓
    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)
            #反射:通过字符串来操作对象的属性或者方法   以get为例
            	handler = getattr(自己写的类产生的对象,'get',当找不到get属性或者方法的时候就会用第三个参数)
                handler = 我们自己写的类里面的get方法
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs)
    	#自动调用get方法

模板层

模板语法传值

{{}}:变量相关

{% %}:逻辑相关


    <p>{{ a }}</p>
    <p>{{ b }}</p>
    <p>{{ c }}</p>
    <p>{{ d }}</p>
    <p>{{ e }}</p>
    <p>{{ f }}</p>
    <p>{{ g }}</p>
    <p>{{ h }}</p>
    <p>{{ func }}</p>      #传递函数会自动加括号调用 有参函数无法调用
    <p>{{ MyClass }}</p>   #传类名也可以自动加括号调用
    <p>{{ obj }}</p>  
    <p>{{ obj.get_func }}</p>
    <p>{{ obj.get_self }}</p>
    <p>{{ obj.fet_class }}</p>

过滤器

# 过滤器就类似于内置的模板语法

{{ 数据|过滤器:参数}}

# 默认值;
# 如果一个变量是false或者为空,使用给定的默认值。 否则,使用变量的值。
# 如果一个变量是false或者为空,使用冒号后面的,否则使用竖杠前面的。
{{ value|default:"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"}}

# safe
Django的模板中会对HTML标签和JS等语法标签进行自动转义,原因显而易见,这样是为了安全。但是有的时候我们可能不希望这些HTML元素被转义,比如我们做一个内容管理系统,后台添加的文章中是经过修饰的,这些修饰可能是通过一个类似于FCKeditor编辑加注了HTML修饰符的文本,如果自动转义的话显示的就是保护HTML标签的源文件。为了在Django中关闭HTML的自动转义有两种方式,如果是一个单独的变量我们可以通过过滤器“|safe”的方式告诉Django这段代码是安全的不必转义。

比如:

value = "<a href='#'>点我</a>"

{{ value|safe}}
# truncatechars
如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾。

参数:截断的字符数

{{ value|truncatechars:9}}
# truncatewords
在一定数量的字后截断字符串。

{{ value|truncatewords:9}}

# cut
移除value中所有的与给出的变量相同的字符串

{{ value|cut:' ' }}
如果value为'i love you',那么将输出'iloveyou'.

# join
使用字符串连接列表,例如Python的str.join(list)

模板语法之标签

# for循环
普通for循环

<ul>
{% for user in user_list %}
    <li>{{ user.name }}</li>
{% endfor %}
</ul>
for循环可用的一些参数:

Variable	           Description
forloop.counter	    当前循环的索引值(从1开始)
forloop.counter0	当前循环的索引值(从0开始)
forloop.revcounter	当前循环的倒序索引值(从1开始)
forloop.revcounter0	当前循环的倒序索引值(从0开始)
forloop.first	    当前循环是不是第一次循环(布尔值)
forloop.last	    当前循环是不是最后一次循环(布尔值)
forloop.parentloop	本层循环的外层循环

# if判断
if,elif和else

复制代码
{% if user_list %}
  用户人数:{{ user_list|length }}
{% elif black_list %}
  黑名单数:{{ black_list|length }}
{% else %}
  没有用户
{% endif %}


# for ... empty

复制代码
<ul>
{% for user in user_list %}
    <li>{{ user.name }}</li>
{% empty %}
    <li>空空如也</li>
{% endfor %}
</ul>


# with
定义一个中间变量,多用于给一个复杂的变量起别名。

注意等号左右不要加空格。

{% with total=business.employees.count %}
    {{ total }} employee{{ total|pluralize }}
{% endwith %}

自定义过滤器 标签 inclusion_tag

三步走
1.现在应用下创建一个名字为templatetags文件夹
2.在该文件夹内创建任意名称的py文件 
3.在该文件内必须书写下面两句话
from jiango import template
register = template.Library()

#定义自定义过滤器
@register.filter(name='baby')
def my_sum(v1,v2):
    return v1 + v2
#定标签
@register.simple_tag(name='plus')
def index(x,y,z):
    return '%s-%s-%s'%(x,y,z)
#自定义过滤器
{% load my %}
<p>{{ a|baby:666 }}</p>

#标签
{% load my %}
<p>{% plus 1 2 3 %}</p>

# 定义inclusion_tag  局部页面
@register.inclusion_tag('left_menu.html')
def left(a):
    data = ['第{}项'.format(i) for i in range(a)]
    # 第一种
    # return {'data':data}
    # 第二种
    return locals()
# html页面
<ul>
    {% for foo in data %}
        <li>{{ foo }}</li>
    {% endfor %}
</ul>
# 局部页面
{% load my %}
{% left 5 %}

模板的继承

#选好模板页面
{% extends 'home.html' %}

#划定区域
{% block content%}
模板内容
{% endblock %}

#子页面修改
{% block content%}
子页面内容
{% endblock %}

#可以被修改的区域
css区域 
html区域
js区域


模板的导入

 {% include 'html页面'%}

单表操作

django自带的sqlite数据对日期格式不敏感
# auto_now无论是你添加还是修改对象,时间为你添加或者修改的时间。
# auto_now_add为添加时的时间,更新对象时不会有变动。  auto_now_add=True
只想测试django中某一个py文件内容的时候,写一个测试脚本即可。


#测试环境去 manage.py中拷贝前四行代码 然后自己写两行
# copy的
import os

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day54.settings")
# 自己写的
    import django
    django.setup()
# 测试代码
    from app01 import models
    models.User.objects.all()

# 删
第一种:
res = models.User.objects.filter(pk=2).delete()
print(res)
pk会自动查找主键 就不需要指代当前表的主键字段叫什么
(1, {'app01.User': 1})
返回值表示该主键有几行

第二种:
user_obj = models.User.objects.filter(pk=1).first()
user_obj.delete()

#修改
models.User.objects.filter(pk=1).update(name='husangDSB')
#数据不存在返回none

user_obj = models.User.objects.get(pk=4)
print(user_obj)
#数据不存在会报错

必知必会13条

1.all()    查询所有
2.filter() 带有过滤条件的查询
3.get()    直接拿数据对象 但是条件不存在直接报错
4.first()  拿queryset里面第一个元素 其实是整条数据
5.last()
6.values() 可以指定获取的数据字段
res=models.User.objects.values('name')
print(res)
<QuerySet [{'name': 'husangDSB', 'age': 3}]>
返回结果是列表套字典
7.values_list()
res = models.User.objects.values_list('name','age')
print(res)
<QuerySet [('husangDSB', 3)]>
返回结果是列表套元组
8.distinct()去重   #必须一模一样
res = models.User.objects.values('name','age').distinct()
print(res)
9.order_by()  #排序  默认升序
res = models.User.objects.order_by('age')
print(res)
#降序
res = models.User.objects.order_by('-age')
print(res)
10.reverse()  #反转  前提是已经排过序了
res = models.User.objects.reverse()
print(res)

11.count()  # 统计当前数据的个数
res = models.User.objects.count()
print(res)

12.exclude()  # 排除在外
res = models.User.objects.exclude(name='jason') 
print(res)

13.exists()  # 判断数据是否存在 基本用不到  数据本身自带布尔值
res = models.User.objects.filter(pk=1).exists()
print(res)

Django终端打印SQL语句

#将以下代码放入settings当中
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

神奇的双下划线查询

    # #大于
    # res = models.User.objects.filter(age__gt=35)
    # #小于
    # res = models.User.objects.filter(age__lt=35)
    # #大于等于
    # res = models.User.objects.filter(age__gte=35)
    # #小于等于
    # res = models.User.objects.filter(age__lte=35)
   
    # #年龄为111或22
    # res = models.User.objects.filter(age__in=[111,22])
    
    #年龄在1到20之间  where age between 1 and 20
    # res = models.User.objects.filter(age__range=[1,20])

    # 查询名字中带有字母y的   age like %y%
    # res = models.User.objects.filter(name__contains='y')

    # 忽略大小写    
    # res = models.User.objects.filter(name__icontains='y')

    # 查询以y开头的       age like y%
    # res = models.User.objects.filter(name__startswith='y')

    # 查询末尾为y的    age like %y
    # res = models.User.objects.filter(name__endswith='y')

    #查询出注册时间是2021年七月份的
    # res = models.User.objects.filter(register_time__month='7')

    #年
    # res = models.User.objects.filter(register_time__year='2021')

    #日
    # res = models.User.objects.filter(register_time__day='1')

一对多外键增删改查(跟一对一 一样)

#增
modles.Book.object.create(name='三国',price=123,publish_id=1)
#删
models.Publish.object.filter(pk=1).delect
#改
models.Book.object.filter(pk=1).update(publish_id=2)
#第二种
publish_obj = modles.Publish.object.filter(pk=1).first()
modles.Book.object.filter(pk=1).update(publish=publish_obj)

多对多

给第三张表传数据
# 增
先找到第三张列表的数据
book_obj = models.Book.objects.filter(pk=14).first()
#第一种
book_obj.authors.add(1,2)
#第二种
author_obj = models.Author.objects.filter(pk=1).first()
book_obj.authors.add(author_obj)

# 删
book_obj = models.Book.objects.filter(pk=14).first()
#第一种
book_obj.authors.remove(1,2)
#第二种
author_obj = models.Author.objects.filter(pk=2).first()
author_obj1 = models.Author.objects.filter(pk=3).first()
book_obj.authors.remove(author_obj,author_obj1)

# 改
set用法 括号里必须是可迭代对象
book_obj = models.Book.objects.filter(pk=14).first()
#第一种
book_obj.authors.set([1,2])
#第二种
author_obj = models.Author.objects.filter(pk=2).first()
author_obj1 = models.Author.objects.filter(pk=3).first()
book_obj.authors.set([author_obj,author_obj1])

# 清除
book_obj = models.Book.objects.filter(pk=14).first()
book_obj.authors.clear()

正反向的概念

#正向
看外键字段在谁那里
#反向
正向查询按字段
反向查询按表名小写
		_set等等

多表查询

#子查询(基于对象的跨表查询)
当查询结果可以有多个的时候 就必须加_set.all()
当结果只有一个的时候 不需要加_set.all()


联表查询(基于双下划线的跨表查询)
# 一行解决
#查询ssy的电话号码
res=models.Author.objects.filter(name='ssy').values('author_detail__phone')
print(res)
#反向
res = models.AuthorDetail.objects.filter(author__name='ssy').values('phone','author__name')
print(res)


#查询书籍主键为18的出版社名称和书的名称
res = models.Book.objects.filter(pk=18).values('publish__name','title')
print(res)
#反向
res = models.Publish.objects.filter(book__pk=18).values('name','book__title')
print(res)

#查询书籍主键是18的作者的手机号
# res = models.Book.objects.filter(pk=18).values('authors__author_detail__phone')

聚合查询

一般配合分组一起使用

只要是跟数据库相关的模块
基本都在django.db.models里面 或者django.db

from django.db.models import Max,Min,Sum(总和),Count,Avg(平均)

res = models.Book.objects.aggregate(Max('price'),Min('price'),Sum('price'),Count('pk'),Avg('price'))

{'price__max': Decimal('300.00'), 'price__min': Decimal('100.11'), 'price__sum': Decimal('900.11'), 'pk__count': 4, 'price__avg': 225.0275}

分组查询

分组之后默认只能获取到分组的依据 组内其他字段都无法直接获取

严格模式
	ONLY_FULL_GROUP_BY

models后面点什么就按照什么分组


#统计每一本书作者的个数
												#有没有__id都一样
res = models.Book.objects.annotate(author_num=Count('authors__id')).values('title','author_num')

#统计每个出版社卖的最便宜的书的价格
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price')

#统计不止一个作者的图书
res = models.Book.objects.annotate(author_num=Count('authors')).filter(author_num__gt=1).values('title','author_num')



F与Q查询

from django.db.models import F,Q
from django.db.models import F,Q
# #查询卖出数大于库存数的书籍
#
# res = models.Book.objects.filter(maichu__lt=F('kucun'))
#价格加500
# res =  models.Book.objects.update(price=F('price')+500)

#名字后面加'爆款'两个字
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.update(title=Concat(F('title'),Value('爆款')))

#Q关系
from django.db.models import Q
res = models.Book.objects.filter(Q(maichu__gt=100),Q(print__lt=600))    #  , ang

res = models.Book.objects.filter(Q(maichu__gt=100)|Q(print__lt=600))   #  |  or关系

res = ~models.Book.objects.filter(Q(maichu__gt=100)|Q(print__lt=600))     #  ~ not关系

#高阶
能够将查询条件的左边也变成字符串的形式
q = Q()
q.connector = 'or'    #默认and关系 在这里进行修改
q.children.append(('maichu__gt',100))
q.children.append(('price__lt',600))
res = models.Book.objects.filter(q)
print(res)


django中如何开启事务

Mysql中的事务
	ACID
    原子性 不可分割的最小单位
    一致性 跟原子性相辅相成
    隔离性	事务之间互相不干扰
    持久性	事务一旦确认永久生效
事务的回滚
	rollback

事务的确认
	commit

django中只需要掌握简单的开启事务

orm中常用字段及参数

auto_now       更新字段的时间
auto_now_add   创建字段的时间
AutoField
	主键字段 primary_key=True  一般不用 系统会自动定义
CharField
	verbose_name  字段的注释
    max_lenth      长度
IntegerField      int

BigIntegerField   bigint

DeciumalField
	max_digits=8
    decimal_places=2
    
EmailFiled         varchar(254)

DateField			date
DateTimeField		datetime
	auto_now:  每次修改数据的时候都会自动更新当前时间
    auto_now_add:只在创建数据的时候记录创建时间后续不会自动修改了
                
BooleanField(Field)  布尔值类型
    该字段传布尔值  数据库里面存0/1
    
TextField(Field)     文本类型
	该字段可以用来存大段文本(文章 博客)没有字数限制
    后面的bbs作业 文章字段用的就是TextField
    
FileField(Field)
    - 字符串,路径保存在数据库,文件上传到指定目录
    - 参数:
    upload_to = "data"    上传文件的保存路径,给该字段传一个文件对象,会自动将文件保存到/data目录下,然后将文件路径保存到数据库中/data/a.txt   bbs会涉及
    
    storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
    

    
#cv 字段合集
AutoField(Field)
        - int自增列,必须填入参数 primary_key=True

    BigAutoField(AutoField)
        - bigint自增列,必须填入参数 primary_key=True

        注:当model中如果没有自增列,则自动会创建一个列名为id的列
        from django.db import models

        class UserInfo(models.Model):
            # 自动创建一个列名为id的且为自增的整数列
            username = models.CharField(max_length=32)

        class Group(models.Model):
            # 自定义自增列
            nid = models.AutoField(primary_key=True)
            name = models.CharField(max_length=32)

    SmallIntegerField(IntegerField):
        - 小整数 -32768 ~ 32767

    PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正小整数 0 ~ 32767
    IntegerField(Field)
        - 整数列(有符号的) -2147483648 ~ 2147483647

    PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正整数 0 ~ 2147483647

    BigIntegerField(IntegerField):
        - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807

    BooleanField(Field)
        - 布尔值类型

    NullBooleanField(Field):
        - 可以为空的布尔值

    CharField(Field)
        - 字符类型
        - 必须提供max_length参数, max_length表示字符长度

    TextField(Field)
        - 文本类型

    EmailField(CharField):
        - 字符串类型,Django Admin以及ModelForm中提供验证机制

    IPAddressField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制

    GenericIPAddressField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
        - 参数:
            protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
            unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both"

    URLField(CharField)
        - 字符串类型,Django Admin以及ModelForm中提供验证 URL

    SlugField(CharField)
        - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)

    CommaSeparatedIntegerField(CharField)
        - 字符串类型,格式必须为逗号分割的数字

    UUIDField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证

    FilePathField(Field)
        - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
        - 参数:
                path,                      文件夹路径
                match=None,                正则匹配
                recursive=False,           递归下面的文件夹
                allow_files=True,          允许文件
                allow_folders=False,       允许文件夹

    FileField(Field)
        - 字符串,路径保存在数据库,文件上传到指定目录
        - 参数:
            upload_to = ""      上传文件的保存路径
            storage = None      存储组件,默认django.core.files.storage.FileSystemStorage

    ImageField(FileField)
        - 字符串,路径保存在数据库,文件上传到指定目录
        - 参数:
            upload_to = ""      上传文件的保存路径
            storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
            width_field=None,   上传图片的高度保存的数据库字段名(字符串)
            height_field=None   上传图片的宽度保存的数据库字段名(字符串)

    DateTimeField(DateField)
        - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]

    DateField(DateTimeCheckMixin, Field)
        - 日期格式      YYYY-MM-DD

    TimeField(DateTimeCheckMixin, Field)
        - 时间格式      HH:MM[:ss[.uuuuuu]]

    DurationField(Field)
        - 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型

    FloatField(Field)
        - 浮点型

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

    BinaryField(Field)
        - 二进制类型


#对应关系:
    'AutoField': 'integer AUTO_INCREMENT',
    'BigAutoField': 'bigint AUTO_INCREMENT',
    'BinaryField': 'longblob',
    'BooleanField': 'bool',
    'CharField': 'varchar(%(max_length)s)',
    'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
    'DateField': 'date',
    'DateTimeField': 'datetime',
    'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
    'DurationField': 'bigint',
    'FileField': 'varchar(%(max_length)s)',
    'FilePathField': 'varchar(%(max_length)s)',
    'FloatField': 'double precision',
    'IntegerField': 'integer',
    'BigIntegerField': 'bigint',
    'IPAddressField': 'char(15)',
    'GenericIPAddressField': 'char(39)',
    'NullBooleanField': 'bool',
    'OneToOneField': 'integer',
    'PositiveIntegerField': 'integer UNSIGNED',
    'PositiveSmallIntegerField': 'smallint UNSIGNED',
    'SlugField': 'varchar(%(max_length)s)',
    'SmallIntegerField': 'smallint',
    'TextField': 'longtext',
    'TimeField': 'time',
    'UUIDField': 'char(32)',

ORM字段与MySQL字段对应关系


django支持自定义字段
    
#自定义字段

class MyCHarField(models.Field):
    def __init__(self,max_length,*args,**kwargs):
        self.max_length = max_length
        
        super().__init__(max_length=max_length,*args,**kwargs)
        
    def db_type(self,connection):

        return 'char(%s)'%self.max_length


#外键字段
ForeignKey(unique=True)   ======   OneToOneField

null
用于表示某个字段可以为空。

unique
如果设置为unique=True 则该字段在此表中必须是唯一的 。

db_index
如果db_index=True 则代表着为此字段设置索引。

default
为该字段设置默认值。

to
设置要关联的表

to_field
设置要关联的表的字段 默认不写关联的就是另外一张的主键字段

on_delete
当删除关联表中的数据时,当前表与其关联的行的行为。

models.CASCADE
删除关联数据,与之关联也删除

#db_constraint
是否在数据库中创建外键约束,默认为True。

models.DO_NOTHING
删除关联数据,引发错误IntegrityError


models.PROTECT
删除关联数据,引发错误ProtectedError


models.SET_NULL
删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)


models.SET_DEFAULT
删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)


models.SET
删除关联数据
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)

数据库查询优化

orm语句的特点:
	惰性查询
    	只书写了语句 但是并没用到 系统默认不执行(只写句子 不输出 卵用没有)

#only和all的区别
res = models.Book.objects.only('title')
for i in res:
    print(i.price )
only 查找括号内的字段不需要走数据库,除此以外的要从数据库重新查找
all  直接拿到所有 
#defer
defer与only刚好相反
	defer括号内放的字段不在查询出来的对象里面。查询该字段需要重新走数据库 查询的是非括号内的字段 就不需要走数据库

#select_related与prefetch_related 跟跨表操作有关
res = models.Book.object.select_related('publish')
select_related直接将book与publish连起来 一次性将大表里所有数据全部封装给查询出来的对象 此时再查Book和Publish表里的对象都不需要再走数据库。  就是联表
select_related 括号里只能放外键字段 一对一 一对多 多对多都不行
prefetch_related该方法内部就是子查询 将子查询得到的结果也封装到对象中。

MTV MVC

django是MTV模型 但本质是MVC模型
M:models
T:templetes
V:views
    
M:models
V:xiews
C:controllrr(控制器)

多对多三种创建方式

#全自动:利用orm自动帮我们创建第三张关系表(要会)
class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8,decimal_places=2)
    publish_date = models.DateField(auto_now_add=True)

class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=64)
    email = models.EmailField()
优点:代码不需要自己写 支持orm提供操作第三张关系表的方法
缺点:第三张关系表的扩展性极差(没有办法添加字段)

#纯手动
class Book(models.Model):
    title = models.CharField(max_length=32)
    
class Publish(models.Model):
    name = models.CharField(max_length=32)

class Book2Publish(models.Model):
	book_id = models.ForeignKey(to='Book')
    author_id = models.ForeignKey(to='Author')
    
优点:第三张表完全取决于自己进行额外的扩展
缺点:需要写的代码太多 不能够使用orm提供的简单的方法

#半自动(必会)拓展性更高
class Book(models.Model):
    title = models.CharField(max_length=32)
	Author = models.ManyToManyField(to='Author',
                                     through='Book2Author',
                                     through_fields=('book','publish'))           #表明是由book和author两张表绑定关系的     
    
class Author(models.Model):
    name = models.CharField(max_length=32)
    books = models.ManyToManyField(to='Book',
                                   through='Book2Author',
                                   through_fields=('author','book')) 

class Book2Publish(models.Model):
	book_id = models.ForeignKey(to='Book')
    author_id = models.ForeignKey(to='Author')


through_fields字段先后顺序
	判断的本质:通过第三张表查询的表 需要用到哪个字段就把哪个字段放前面
    		或者说  当前表是谁就把对应的关键字放前面
        
        
半自动可以使用orm正反向查询 但是没法使用add,set,remove,clear这四个方法

Ajax

特点,精髓
局部刷新,异步提交	

Ajax基本语法

<input type="text" id="d1"> +
<input type="text" id="d2"> =
<input type="text" id="d3">

<p>
    <button id="btn">点我</button>
</p>
<script>
    {#先给按钮绑定一个点击事件#}
    $('#btn').click(function(){
        {#朝后端发送ajax请求#}
        $.ajax({
            {#指定朝哪个后端发送ajax请求#}
            url:'',         //不写就朝当前地址提交
            type:'post',   //2.请求方式    不指定默认就是get 都是小写
            //3.数据
            {#data:{'username':'jason','password':123},#}
            data:{'i1':$('#d1').val(),'i2':$('#d2').val()},
            //4.回调函数:当后端给你返回结果的时候会自动触发 args接收后端的返回结果

            success:function(args){
                alert(args)   //显示弹窗
                $('#d3').val(args)
                {#console.log(typeof args)   //输出括号里的信息#}
            }
        })
    })
</script>

                
# 针对后端 如果是用HttpResponse返回的数据 回调函数不会自动帮你反序列化;
# 如果后端直接用的是JsonResponse返回的数据 回调函数会自动帮你反序列化
HttpResponse解决方式
    1.自己在前端利用JSON.parse()
    2.在ajax里面配置一个参数
                      

前后端传输数据的编码格式(contentType)

#我们主要研究post请求数据的编码格式

get请求数据就是直接放在url后面的
url?xxxxxxxxxxx


可以朝后端发送post请求的方式
1.form表单
2.ajax请求


#前后端传输数据的编码格式

urlencoded

formdata

json


#研究form表单
form表单默认的数据编码格式是urlencoded
数据格式 username=jason&password=123
django后端针对符合urlencoded编码格式的数据会自动解析封装到request.POST中

如果你把编码格式改成formdata,那么针对普通的键值对还是解析到request.POST中而将文件解析到request.FILES中


# 研究ajax

默认的编码格式也是urlencoded
数据格式:username=jason&age=20
django后端针对符合urlencoded编码格式的数据都会自动帮你解析封装到request.POST中
	username=jason&password=123 >>> request.POST
    

ajax发送json格式数据

前后端传输数据的时候一定要确保编码格式跟数据真正的格式是一致的


<script>
    $('#d1').click(function(){
        $.ajax({
            url:'',
            type:'post',
            data:JSON.stringify({username:jason,password:666}),
            contentType:'application/json',
            success:function(){
            }
        })
        })
</script>

django中针对json格式的数据需要手动处理
json_bytes = request.body
#json_str = json_bytes.decode('utf-8')  解码
#json_dict = json.loads(json_str)       反序列化
jsondict = json.loads(json_bytes)

print(json_dict,type(json_dict)) 

#json.loads括号内如果传入了一个二进制格式的数据那么内部自动解码再反序列化

request对象方法补充
	request.is_ajax()
    	判断当前请求是否是ajax请求,返回布尔值

ajax发送文件

需要利用内置对象 FormData
<p>username: <input type="text" id="d1"></p>
<p>password: <input type="password" id="d2"></p>
<p><input type="file"  id="d3"></p>
<button class="btn btn-info" id="d4">点我</button>

<script>
    $('#d4').on('click',function (){
        {#先利用FormData内置对象#}
        let formDataObj = new FormData();
        添加普通键值对
        formDataObj.append('username',$('#d1').val())
        formDataObj.append('password',$('#d2').val())
        {#添加文件#}
        formDataObj.append('myfile',$('#d3')[0].files[0])
        {#基于ajax传给后端#}
        $.ajax({
            url:'',
            type:'post',
            data:formDataObj,
            contentType:false,
            processData:false,   //需要指定的参数(背过)
            success:function (args){

            }
        })
    })
</script>

django后端能够直接识别到formdata对象并且能够将颞部的普通键值自动解析并封装到request.POST中
文件数据自动解析并封装到request.FILES中

django自带的序列化组件(drf做铺垫)serializers

res = serializers.serialize('json',需要被序列化的)

批量插入bulk_create

models.Book.object.bulk_create(列表)

自定义分页器

https://www.cnblogs.com/Dominic-Ji/articles/12035722.html

cookie和session

用户登录后,服务端产生一个随机字符串(在服务端保存数据,用kv键值对的形式)
cookie
	服务端保存在哭护短浏览器上的信息都可以称之为cookie
    他的表现形式一般都是k:v键值对(可以有多个)
session
	数据时保存在服务端的并且它的表现形式一般也是k:v键值对(可以有多个)
token
	session虽然数据时保存在服务端的 但是量大
    服务端不再保存数据
    	登陆成功之后 将一段信息进行加密处理(加密算法只有自己知道)
        将加密之后的结果拼接在信息后面 整体返回给浏览器保存
        浏览器下次访问时带着该信息 服务端自动切去前面一段信息再次使自己的加密算法
        跟浏览器尾部的密文进行比对
    
#总结
1.cookie保存在客户端浏览器上的信息
2.session保存在服务端上的信息
3.session是基于cookie工作的

django操作cookie

obj1 = HttpResponse()
obj2 = render()
obj3 = redirect()

设置cookie
	obj.set_cookie(key,value)
获取cookie
	request.COOKIES.get(key)
设置cookie的时候可以添加一个超时时间

#基于cookie 写登录界面
from django.shortcuts import render,HttpResponse,redirect

# Create your views here.
def login_auth(func):
    def inner(request,*args,**kwargs):
        target_url = request.get_full_path()
        if request.COOKIES.get('username'):
            return func(request,*args,**kwargs)
        else:
            return redirect('/login/?next=%s'%target_url)

    return inner

def  login(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username == 'jason' and password =='123':
            #获取用户上一次想要访问的url
            target_url = request.GET.get('next')  #可能是none
            if target_url:
                obj =  redirect(target_url)
            else:

                #保存用户登录状态
                obj = redirect('/home/')
            #让浏览器记录cookie数据  max_age和expires 都是以秒为单位控制超时时间的  IE浏览器要用expires
            obj.set_cookie('username','jason666')   # max_age=3,expires=3
            #跳转
            return obj

    return render(request,'login.html')

@login_auth
def home(request):
    #获取cookie信息
    # if request.COOKIES.get('username') == 'jason666':
    #
    #     return HttpResponse("登陆了才能进")
    # else:
        # print('好好想想密码吧,二货')
        # return redirect('/login/')
    return HttpResponse("登陆了home才能进")

@login_auth
def home1(request):
    #获取cookie信息
    # if request.COOKIES.get('username') == 'jason666':
    #
    #     return HttpResponse("登陆了才能进")
    # else:
        # print('好好想想密码吧,二货')
        # return redirect('/login/')
    return HttpResponse("登陆了home1才能进")

@login_auth
def home2(request):
    #获取cookie信息
    # if request.COOKIES.get('username') == 'jason666':
    #
    #     return HttpResponse("登陆了才能进")
    # else:
        # print('好好想想密码吧,二货')
        # return redirect('/login/')
    return HttpResponse("登陆了home2才能进")


#注销
@login_auth
def logout(request):
    obj = redirect('/login/')
    obj.delete_cookie('username')
    return obj


session操作

session数据时保存在服务端的,给客户端返回的是一个随机字符串
	sessionid:随机字符串

设置session
request.session['key'] = value

获取session
request.session.get('key')

#设置过期时间
request.session.set_expiry()
括号内可以放四种类型的参数
	1.整数   秒
    2.日期对象   到指定日期失效
    3.0      浏览器窗口关闭立马失效  #改掉了
    4.不写    失效时间取决于django内部全局默认的失效时间(14天)
    
#清除session
request.session.delete() #只删服务端的  客户端不删
request.session.flush()  #浏览器和服务端全部清空

#Django中Session相关设置
Django中默认支持Session,其内部提供了5种类型的Session供开发者使用。
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,默认修改之后才保存(默认)



## django_session表中的数据条数取决于浏览器的,同一个计算机上同一个浏览器只会有一条数据生效
   session过期的时候可能出现多条数据对应一个浏览器,但是该现象不会持续很久,内部会将过期的数据清除 也可以通过代码清除

    
    
def sett_session(request):
    request.session['hobby'] = 'girl'
    #内部发生了什么
    # 1.django内部会自动帮你生成一个随机字符串
    # 2.django内部自动将随机字符串和对应的数据存储到django_session表中(不是智慧而生效de)
    #    2.1先在内存中产生操作数据的缓存
    #    2.2在响应结果django中间件的时候才真正的操作数据库
    # 3.将产生的随机字符串返回给客户端浏览器保存
    request.session.set_expiry()   #失效时间
    return HttpResponse('xxx')

def gett_session(request):
    print(request.session.get('hobby'))
    '''
    获取session的时候
    1.自动从浏览器请求中获取session对应的随机字符串
    2.拿着该随机字符串去django_session表中查找对应的数据
    3.如果对比上了 则将对应的数据取出并以字典的形式封装到request.sessionzhong
      如果比对不上 则request.session.get()返回的是None
    '''
    return HttpResponse('哈哈哈')



CBV如何添加装饰器

from django.views import View
from django.utils.decorators import method_decorator


@method_decorator(login_auth,name='get')   #方式二  可以添加多个
@method_decorator(login_auth,name='post')
class MyLogin(View):
    @method_decorator(login_auth)          #方式三:他会直接作用于当前类里面的所有方法
    def dispatch(self, request, *args, **kwargs):
        pass

    @method_decorator(login_auth)   #方式一
    def get(self,request):
        return HttpResponse("get请求")

    def post(self,request):
        return HttpResponse('post请求')

django中间件

'''
复习http特性 
1.四大特性
2.数据格式
3.状态码

django请求生命周期图
'''
DJango中间件类似于 django的保安
1.请求来的时候需要先经过中间件才能到达ur1s.py继续匹配
2.响应走的时候最后也需要经过中间件才能真正离开 django后端
Dango中间件能够做的事情
只要是涉及到项目全局的功能,你一定要想到中间件
1.全局身份校验
2.全局访问频率校验
3,全局校限校验
'''


1.django中间件是django的门户
2.请求来的时候需要经过中间件才能到达真正的django后端
3.响应走的时候最后也需要经过中间件才能发送出去

django中间件代码规律

django支持程序员自定义中间件并且暴露给程序员五个可以自定义的方法
1.必须掌握
process_request

process_response

2.了解
process_view

process_template_response

process_exception

自定义中间件

1.在项目名或者应用名下创建一个任意名称的文件夹
2.在该文件夹内创建一个任意名称的py文件
3.在该朋友文件内需要书写类(这个类必须继承MiddlewareMixin)
  然后再这个类里面就可以自定义五个方法
 (并不是全部都需要写 用几个写几个)
4.需要将类的路径以字符串的形式注册到配置文件中才能生效
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',
    ]

#1.必须掌握
1.process_request
1.1请求来的时候需要经过每一个中间件里面的process_request方法
   结果的顺序是按照配置文件中注册的中间件从上往下的顺序依次执行
1.2如果中间件里面没有定义该方法,那么直接跳过执行下一个
1.3如果该方法反悔了HttpResponse对象,那么请求将不再继续往后执行,而是直接原路返回
	(类似于校验失败不允许访问)
    process_request方法就是用来做全局相关的所有限制功能
    
2.process_response
2.1响应走的时候需要经过每一个中间件里面的process_response方法
   该方法有两个额外的参数request,response
2.2该方法必须返回一个HttpResponse对象
   1.默认返回的就是形参response
   2.也可以返回自己的
   3.顺序是按照配置文件中注册了的中间件从下往上依次经过
    如果没有定义 直接跳过执行下一个

    
response就是django后端返回给浏览器的内容
#2.了解
process_view

process_template_response

process_exception



csrf校验

#form表单如何符合校验

<form action=" "method= "post">
{% csrf_token %} #添加校验
<p>username: <input type=text"name="username"></p>
<p>target user: <input type="text" name=target_user"</p>
<p>money: <input type="text" name="money"></p>
<input type="submit">
</form>

csrf相关装饰器

1.网站整体都不校验csrf,就单单几个视图函数需要校验   csrf_ protect 
2.网站整体都校验csrf,就单单几个视图函数不校验 csrf_ exempt

# views视图函数内 导入模块
csrf_ exempt  忽视校验
csrf_ protect 需要校验

from django.views.decorators,csrf import csrf_protect,csrf_ exempt

@csrf_exempt #装饰器
def transfer(requeset):
	if request.method =="POST":
		username = request.POST.get('useraname')
		target_user = request.POST.get('target_user')
		money = request.POST.get('money')
	return render(request,'transfer.html')

基于django中间件实现功能

# start 模块 用于发送信息数据

import notify

notify.send_all('郭飞傻逼')

# Email.py

class Email(object):
    def __init__(self):
        pass # 发送邮箱需要做的前期准备工作

    def send(self,content):
        print('邮箱通知:%s '%content)
        
#__init__.py

import settings
import importlib

def send_all(content):
    for path_str in settings.NOTIFY_LIST:
        module_path,class_name =path_str.rsplit('.',maxsplit=1)
        # module_path = 'notify.email' class_name = 'Email'
        #利用字符串导入模块
        module = importlib.import_module(module_path) # from notify import email

        # 利用反射获取类名

        cls = getattr(module,class_name)

        # 生成类的对象
        obj =cls()

        # 利用鸭子类型直接调用send方法

        obj.send(content)
# settings.py  添加注销功能只需要注释其中一行即可

NOTIFY_LIST =[
    'notify.email.Email',
    'notify.qq.QQ',
    'notify.wechat.Wechat',
]

Auth模块

创建好一个django项目之后直接执行数据库迁移命令会自动生成很多表
# 两个重要的表
	django_session
	auth_user
django在启动之后就可以直接访问admin路由,需要输入用户名和密码,数据参考的就是auth_user表,并且还必须是管理员用户才能进入

创建超级用户
	python3 manage.py createsuperuser

Auth模块功能

user_obj = auth.authenticate(request,username=username,password=password)
'''
1.自动查找auth_user标签
2.自动给密码加密再比对
该方法注意事项(该方法#必须同时传入用户名和密码)
'''
print(user_obj)
得到的是username,与print(user_obj.username)得到的值是一样的

用户名账号密码数据不符合返回none

from django.contrib.auth.decorators import login_required

#保存用户状态
auth.login(request,user_obj)  #类似于request.session['key'] = value

#获取当前登录用户
使用该方法后可以在任何地方通过request.user#获取到当前登录的用户对象

没有登录对象时 request.user会拿到AnonymousUser(匿名用户)

#判断用户登录状态
print(request.user.is_authenticated())

#登陆之后才能访问的功能
from diango.contrib.auth.decorators import login required
# 装饰器
#@login required(login_url='/login/')  当用户没有登录的时候跳转到login网址,局部配置 必须登录

@login required(login_url='/login/') 
def home(request):
    "用户登录之后才能看到home页面"
    return HttpResponse('home')

#全局配置:没有登录跳转到指定的页面
settings下写以下代码
LOGIN_URL = '/login/'

优先级  局部>全局

#校验密码 check_password
request.user.check_password(old_password)

#修改密码
request.user.set_password(new_password)  括号里填新密码
request.user.save()

#settings 模块中
from django.contrib.auth.decorators import login_required
@login_required
def set_password(request):
    if request.method == 'POST':
        old_password = request.POST.get('old_password')
        new_password = request.POST.get('new_password')
        confirm_password = request.POST.get('confirm_password')
        #先校验两次密码是否一致
        if new_password == confirm_password:
            #校验密码对不对  check_password 自动比对校验密码
            is_right = request.user.check_password(old_password)
            if is_right:
                #修改密码
                request.user.set_password(new_password)#修改对象属性
                request.user.save() #操作数据库密码
    return render(request,'set_password',locals())






#注销
auth.logout(request) #l类似于request.session.flush()

以上都需要加装饰器 login_requied

#注册  create_user 加密
User.objects.create_user(username=username,password=password)
创建超级用户的时候邮箱是必填的
User.objects.create_superuser(username=username,password=password,email='123@qq.com')

auth模块表扩展字段

class UserInfo(AbstractUser):
    如果继承了AbstractUser 在执行数据库迁移命令的时候auth_user表就不会被创建
    UserInfo表中会出现auth_user所有的字段外加自己扩展的字段

前提:
	1.在继承之前没有执行过数据库迁移命令
    	auth_user没有被创建,如果当前库已经创建了那么你就重新换一个库
    2.继承的类里面不要覆盖AbstractUser里面的字段名
    	表里面有的字段都不要动,只扩展额外字段即可
    3.需要在配置文件中声明django你要用UserInfo替代auth_user
    	AUTH_USER_MODEL = "app01.UserInfo"
        			       '应用名.表名'
    #自己添加的字段
    phone = models.BigIntegerField()
   
自己写表替代auth_user后
auth模块的功能还是照常使用,参考的表页由原来的auth_user变成了UserInfo


    
posted @ 2021-08-30 16:50  Aisa  阅读(114)  评论(0)    收藏  举报