Django框架


django文档https://docs.djangoproject.com/en/4.2/
下面内容基本上可以在该链接中搜索

Django流程

Django是用python语言写的开源web开发框架,并遵循MVC设计。Django的主要目的是简便、快速的开发数据库驱动的网站·它强调代码复用,多个组件可以很方便的以”插件”形式服务于整个框架,Django有许多功能强大的第三方插件,你甚至可以很方便的开发出自己的工具包·这使得Django具有很强的可扩展性。它还强调快速开发和DRY(DoNot Repeat YourselD)原则。
重量级框架:对比Flask框架,Django原生提供了众多的功能组件,让开发更简便快速。提供项目工程管理的自动化脚本工具数据库ORM支持( 对象关系映射),模板,表单,Admin管理站点,文件管理,认证权限,session机制,缓存
MVT格式(Model View Template):有一种程序设计模式叫MVC(Model View Controller),其核心思想是分工、解耦,让不同的代码块之间降低耦合,增强代码的可扩展性和可移植性,实现向后兼容。
在这里插入图片描述

在这里插入图片描述

环境配置:

安装django:pip install django

在一台电脑上想开发多个不同的项目,需要用到用一个包的不同版本,如果用pip install django安装,会将最新的覆盖之前的版本,解决该问题的方式是使用虚拟环境
虚拟环境:可以搭建独立的 python运行环境,使得单个项目的运行环境与其它项目互不影响

安装虚拟环境(Linux):pip install virtualenvpip install virtualenvwrapper
创建虚拟环境:mkvirtualenv [-p python3] 虚拟环境名 (加-p指定使用python3,不指定默认使用python2)如:mkvirtualenv py_django
创建会进入虚拟环境,进入虚拟环境后执行指令workon可以查看当前有哪些虚拟环境
切换虚拟环境:workon 虚拟环境名
删除虚拟环境(不能删除正在使用的虚拟环境):rmvirtualenv 虚拟环境名
退出虚拟环境:deactivate
在虚拟环境中可以使用pip安装库:pip install 库名

创建Django项目

创建Django项目django-admin startproject name
在这里插入图片描述

运行指令后多了如下文件,manage.py是一个管理工具/脚本(如创建子应用时会用到),settings.py设置相关,urls.py路由相关,wsgi.py程序入口
在这里插入图片描述

运行项目python manage.py runserver [127.0.0.1:8001](中括号内可指定)
刚开始运行没反应,在file>settings中进行如下设置,然后重启pycharm运行指令即可
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

创建子应用

创建子应用python manager.py startapp name
运行指令后多了如下几个文件
view.py视图相关,tests.py测试相关,models.py模型相关,migrations迁移相关,admin.py后台相关,apps.py当前子应用相关
在这里插入图片描述

在这里插入图片描述

然后在主应用中将该其与子应用关联起来,有两种方式(子应用名或者子应用名.apps.子应用名Config
在这里插入图片描述

模型的迁移:Model用于和关系型数据库交互
模型:当前项目的开发,都是数据驱动的;以下为书籍信息管理的数据关系: 书籍和人物是 :一对多关系;要先分析出项目中所需要的数据,然后设计数据库表
在这里插入图片描述

书籍信息表

字段名字段类型字段说明
idAutoField主键
nameCharField书名
idname
1西游记
2三国演义

人物信息表

字段名字段类型字段说明
idAutoField主键
nameCharField人名
genderBooleanField性别
bookForeignKey外键
idnamegenderbook
1孙情空False1
2白骨精True1
3曹操False2
4貂蝉True2

使用Django进行数据库开发的提示

MVT 设计模式中的 Model,专门负责和数据库交互。对应 (models.py)
由于 Model 中内了 ORM框架,所以不需要直接面向数据库编程
而是定义模型类,通过 模型类和对象 完成数据库表的 增制改查
ORM框架(如下图) 就是把数据库表的行与相应的对象建立联。互相转换,使得数据库的操作面向对象
在这里插入图片描述

使用Django进行数据库开发的步骤 :

1.定义模型类
2.模型迁移
3.操作数据库

表与表之间关联的时候,必须要写on_delete参数,否则会报异常models.ForeignKey(BookInfo, on_delete=models.CASCADE)

on_delete=None, # 删除关联表中的数据时,当前表与其关联的field的行为
on_delete=models.CASCADE, # 删除关联数据,与之关联也删除
on_delete=models.DO_NOTHING, # 删除关联数据,什么也不做
on_delete=models.PROTECT, # 删除关联数据,引发错误ProtectedError
# models.ForeignKey(‘关联表’, on_delete=models.SET_NULL, blank=True, null=True)
on_delete=models.SET_NULL, # 删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空,一对一同理)
# models.ForeignKey(‘关联表’, on_delete=models.SET_DEFAULT, default=‘默认值’)
on_delete=models.SET_DEFAULT, # 删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值,一对一同理)
on_delete=models.SET, # 删除关联数据,
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)

编写子应用login中的models.py,内容如下
在这里插入图片描述

from django.db import models

# Create your models here.
'''
1.定义模型类
2.模型迁移
    2.1 先生成迁移文件(不会在数据库中生成表,只会创建一个数据表和模型的对应关系)
        python manage.py makemigrations
    2.2 再迁移(会在数据库中生成表)
        python manage.py migrate
3.操作数据库

在哪里定义模型
模型继承自谁就可以
ORM对应的关系
    表-->类
    字段-->属性
'''
class BookInfo(models.Model):
    '''
    1.主键 当前会自动生成
    2.属性复制过来就可以
    '''
    name = models.CharField(max_length=10)

class PeopleInfo(models.Model):
    name = models.CharField(max_length=10)
    # 性别
    gender = models.BooleanField()
    # 外键
    book = models.ForeignKey(BookInfo, on_delete=models.CASCADE)

编写之后,在pycharm下面的terminal中运行python manage.py makemigrations
在这里插入图片描述

如果上面运行失败,是因为没有注册子应用(关联子应用),需要在主应用的settings.py中加上如下内容
在这里插入图片描述
python manage.py makemigrations运行成功后,会在迁移文件中(子应用)生成如下文件
在这里插入图片描述

内容如下

# Generated by Django 4.2 on 2023-04-22 14:16

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='BookInfo',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=10)),
            ],
        ),
        migrations.CreateModel(
            name='PeopleInfo',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=10)),
                ('gender', models.BooleanField()),
                ('book', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='login.bookinfo')),
            ],
        ),
    ]

然后再terminal中继续执行python manage.py migrate,结果如下即表示迁移成功
在这里插入图片描述

在主应用的settings.py中可以查看使用的是什么数据库
在这里插入图片描述
由上面可以知道这个文件为保存的数据库
在这里插入图片描述

打开该数据库,显示没有内容时,需要按如下步骤下载对应数据库的驱动

在这里插入图片描述
在这里插入图片描述

下载之后,上面的指令执行完,该数据库里面会多两个表格(login_bookinfo,login_peopleinfo)
在这里插入图片描述

上面创建的项目中,自带了登入界面,即,运行python manage.py runserver之后,浏览页面http://127.0.0.1:8000/admin会自动跳到登入页面,页面如下
在这里插入图片描述

接着我们将上面的英文修改为中文,settings.py中将LANGUAGE_CODE = 'en-us'改为LANGUAGE_CODE = 'zh-Hans',然后刷新页面,显示的内容就是中文格式

# LANGUAGE_CODE = 'en-us'
# 语言
LANGUAGE_CODE = 'zh-Hans'

在这里插入图片描述

还可以修改时区,将TIME_ZONE = 'UTC'修改为TIME_ZONE = 'Asia/Shanghai',这里看不出效果

# LANGUAGE_CODE = 'en-us'
# 语言
LANGUAGE_CODE = 'zh-Hans'

# TIME_ZONE = 'UTC'
# 时区
TIME_ZONE = 'Asia/Shanghai'

接着查看auto_user数据库没有数据
在这里插入图片描述
接着使用指令创建一个后台管理员用户:python manage.py createsuperuser,运行指令后输入用户名(admin),邮箱(admin@itcast.cn),密码(abc123456),执行指令后,查看auto_user数据库中有一条数据

在这里插入图片描述
创建之后,从新运行项目,然后浏览http://127.0.0.1:8000/admin页面,输入账户(admin)和密码(abc123456)可以登入
在这里插入图片描述

在这里插入图片描述

在子应用的admin.py中输入如下内容,然后刷新页面可以看到多了如下内容

from django.contrib import admin
from login.models import BookInfo

# Register your models here.
# 注册模型
# admin.site.register(模型类)
admin.site.register(BookInfo)

在这里插入图片描述
点击进去就可操作添加数据
在这里插入图片描述
在这里插入图片描述

保存了两条数据,就有两条数据内容,数据库中也可以查看的到
在这里插入图片描述
这是不知道哪个是西游记,哪个是三国演义,需要在子应用的models.py中添加个def __str__(self):函数返回self.name,然后浏览页面就会显示该内容

from django.db import models

# Create your models here.

class BookInfo(models.Model):
    '''
    1.主键 当前会自动生成
    2.属性复制过来就可以
    '''
    name = models.CharField(max_length=10)

    def __str__(self):
        return self.name

class PeopleInfo(models.Model):
    name = models.CharField(max_length=10)
    # 性别
    gender = models.BooleanField()
    # 外键
    book = models.ForeignKey(BookInfo, on_delete=models.CASCADE)

在这里插入图片描述

视图View.py

用户在URL中请求的是视图,视图接收请求后进行处理,并将处理的结果返回给请求者
使用视图时需要进行两步操作:定义视图;配置URLconf

定义视图
视图就是一个 Python 函数,被定义在 应用的 views.py 中
视图的第一个参数是 HttpRequest 类型的对象 reqeust,包含了所有 请求信息
视图必须返回 HttpResponse对象,包含返回给请求者的 响应信息
需要导入 HttpResponse 模块: from django.http import HttpResponse
定义视图函数:响应字符串 OK! 给客户端

子应用的view.py内容如下

from django.shortcuts import render
from django.http import HttpRequest

# Create your views here.
'''
视图
就是python函数
函数的第一个参数就是请求,和请求相关的,是HttpRequest对象
必须要返回一个相应,相应是HttpResponse的实例对象/子类实例对象
'''
def index(request):
    return HttpRequest('index')

URL

在上面的基础上,我们没有定义URL,访问http://127.0.0.1:8000/index得到的是这样的页面
在这里插入图片描述
做如下配置可以正常浏览
在主应用中的settings.py中的如下内容是url的配置,如果有个文件abc.py是URL的文档,可以改为ROOT_URLCONF = ‘abc.py’

# ”ROOT_URICONE 是我们工程的URL的配置的入口
# 默认是 工程名.urls
# 可以修改,默认不改
ROOT_URLCONF = 'book.urls'

在主应用的urls.py中内容添加一项path('', include('login.urls'))

from django.contrib import admin
from django.urls import path, include


'''
1.urlpatterns是固定写法,它的值是列表
2.浏览器中输入的路径会和urlpatterns中的每一项匹配
    如果匹配成功则直接引导到相应的模块,不成功则直接返回404.
3.urlpatterns中的元素是url,url的第一个参数是正则
4.浏览器的路由http://ip:port/path/?key=value中,http://ip:port/和get post不参数正则匹配
5.如果和当前的某项匹配成功,则引导到子应用中继续匹配
    如果匹配成功,则停止匹配返回响应的视图

'''
# 路由匹配
urlpatterns = [
    # 参照:正则;函数或者include函数
    path('admin/', admin.site.urls),
    # 添加一项,上面没匹配,跳转到子应用的urls.py中匹配
    # include导入其他路由文件中
    path('', include('login.urls'))
]

在子应用中创建urls.py,内容如下

from django.urls import path
from login.views import index

# 路由匹配
urlpatterns = [
    # 参照:正则;函数
    path('index/', index),
]

子应用的views.py内容如下

from django.shortcuts import render
from django.http import HttpResponse

# Create your views here.
'''
视图
就是python函数
函数的第一个参数就是请求,和请求相关的,是HttpRequest对象
必须要返回一个相应,相应是HttpResponse的实例对象/子类实例对象
'''
def index(request):
    return HttpResponse('index')

经过上面修改,继续浏览http://127.0.0.1:8000/index/可以得到如下页面
在这里插入图片描述

urls路由匹配
在这里插入图片描述

在这里插入图片描述

模板Template

在项目上创建template目录,在该目录中创建一个index.html文件
在这里插入图片描述

index.html文件内容如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Django模板</title>
</head>
<body>
    <a>Django模板</a>
</body>
</html>

在主应用的settings.py中做以下设置
在这里插入图片描述
然后修改子应用的views.py文件内容,内容如下

from django.shortcuts import render
# from django.http import HttpResponse

# Create your views here.
'''
视图
就是python函数
函数的第一个参数就是请求,和请求相关的,是HttpRequest对象
必须要返回一个相应,相应是HttpResponse的实例对象/子类实例对象
'''
def index(request):
    # request,template name,context=None
    # 参数1: 当前的请求
    # 参数2: 模板文件
    return render(request, 'index.html')
    # return HttpResponse('index')

然后浏览http://127.0.0.1:8000/index/显示页面为index.html的内容,页面如下
在这里插入图片描述

如果要将参数传入该index.html中,可以进行如下操作
修改templatge中的index.html文件,接收views.py传入的数据(这里的name是下面传入字典的键,得到的就是下面传入的字典的值Python
在这里插入图片描述

子应用的views.py内容如下(return中添加context参数传入index.html

from django.shortcuts import render
from django.http import HttpResponse

# Create your views here.
'''
视图
就是python函数
函数的第一个参数就是请求,和请求相关的,是HttpRequest对象
必须要返回一个相应,相应是HttpResponse的实例对象/子类实例对象
'''
def index(request):
    # request,template name,context=None
    # 参数1: 当前的请求
    # 参数2: 模板文件
    # 参数3:context传递参数
    context = {
        'name': 'Python'
    }
    # 将context传递给index.html
    return render(request, 'index.html', context=context)
    # return HttpResponse('index')

浏览页面如下
在这里插入图片描述

综合案例总结

创建项目:django-admin startproject bookmanager
进入项目:cd bookmanager
添加子应用:python manage.py startapp book
在主项目的settings.py中配置
在这里插入图片描述

在子应用的models.py中定义模型

from django.db import models

# Create your models here.
class BookInfo(models.Model):
    name = models.CharField(max_length=10)

运行会生成数据库python manage.py makemigrationspython manage.py migrate
在子应用的views.py中设置

from django.shortcuts import render
from django.http import HttpResponse

# Create your views here.=
def index(request):
    return HttpResponse('index')

在子应用中创建urls.py路由文件

from django.urls import path
from login.views import index


urlpatterns = [
    # 参照:正则;函数
    path('index/', index),
]

在主应用的urls.py中添加引入该路由

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    # 参照:正则;函数
    path('admin/', admin.site.urls),
    # 添加一项
    path('', include('login.urls'))
]

在项目上创建template目录,在该目录中创建一个index.html文件,内容如下
在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Django模板</title>
</head>
<body>
    <a>Django模板{{name}}</a>
</body>
</html>

在主应用的settings.py中做以下设置(BASE_DIR在该文件上面有定义)
在这里插入图片描述
然后修改子应用的views.py文件内容,内容如下(return中添加context参数传入index.html

from django.shortcuts import render
from django.http import HttpResponse

# Create your views here.
def index(request):
    # request,template name,context=None
    # 参数1: 当前的请求   参数2: 模板文件   参数3:context传递参数
    context = {
        'name': 'Python'
    }
    # 将context传递给index.html
    return render(request, 'index.html', context=context)
    # return HttpResponse('index')

浏览页面如下
在这里插入图片描述

如果需要后台账户(admin):python manage.py createsuperuser,然后输入账号邮箱密码等信息
在子应用的admin.py中添加如下内容

from django.contrib import admin
from login.models import BookInfo

# Register your models here.
admin.site.register(BookInfo)

然后在127.0.0.1:8000/admin登入之后可以管理数据库,添加数据
为了能够显示数据,在子应用的models.py中做如下修改即可

from django.db import models

# Create your models here.
class BookInfo(models.Model):
    name = models.CharField(max_length=10)

    def __str__(self):
        return self.name

要将数据库中的数据写入到index.html中,需要做如下修改
template中index.html内容如下(遍历读取数据

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Django模板</title>
</head>
<body>
    <ul>
		<!-- 遍历读取 -->
        {% for book in books %}
            <li>{{ book }}</li>
        {% endfor %}
    </ul>
</body>
</html>

修改views.py,获取数据库数据,传入index.html中

from django.shortcuts import render
from login.models import BookInfo

def index(request):
    books = BookInfo.objects.all()
    context = {
        'books':books
    }
    # 将context传递给index.html
    return render(request, 'index.html', context=context)

Debug

当浏览URL错误的时候,会显示如下页面
在这里插入图片描述
是因为主应用的settings.py里面有这行代码,如果改成False就不会显示

# 开发者进行调试使用,当部署上线的时候需要改为False
# 为True时,浏览页面发生错误,会告诉你那里错了
DEBUG = True

静态文件

静态文件:项目中的CSS、图片、js都是静态文件。一般会将静态文件放到一个单独的目录中,以方便管理。在html页面中调用时,也需要指定静态文件的路径,Diango中提供了一种解析的方式配置静态文件路径,静态文件可以放在项目根目录下,也可以放在应用的目录下,由于有些静态文件在项目中是通用的,所以推荐放在项目的根目录下,方便管理。
为了提供静态文件,否要配置两个参数 :

STATICFILES_DIRS:存放查找静态文件的目录
STATIC_URL:访问静态文件的URL前缀

在项目中创建static文件夹,在文件夹中保存图片
在这里插入图片描述

在主应用的settings.py中进行如下配置

# Django通过STATIC_UR区分静态资源和动态资源
STATIC_URL = 'static/'
# 告诉系统静态文件在哪,BASE_DIR变量在该文件上面有定义
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static')
]

然后访问http://127.0.0.1:8000/static/1.jpeg就会出现static中的图片
在这里插入图片描述

apps配置相关

在子应用的apps.py中添加这一行

然后在主应用settings.py中一定要用这种格式
在这里插入图片描述

运行显示如下
在这里插入图片描述

二、模型(models.py)

1.定义模型类
2.模型迁移
2.1 先生成迁移文件(不会在数据库中生成表,只会创建一个数据表和模型的对应关系)
python manage.py makemigrations
2.2 再迁移(会在数据库中生成表)
python manage.py migrate
3.操作数据库
1、ORM对应的关系
表–>类
字段–>属性
2.模型类需要继承自models.Model
3.模型类会自动为我们添加一个主键
4.属性名=属性类型(选项)charfield类型必须设置max_length

属性类型

类型说明
AutoField自动增长的IntegerField,通常不用指定,不指定时Django会自动创建属性名为id的自动增长属性
BooleanField布尔字段,值为True或False
NullBooleanField支持Null·True、False三种值
CharField字符串,参数max_length表示最大字符个数,charfield类型必须设置max_length
TextField大文本字段,一般超过4000个字符时使用
lntegerField整数
DecimalField十进制浮点数,参数max_digits表示总位数,参数decimal_places表示小数位数
FloatField浮点数
DateField日期,参数auto_now表示每次保存对象时,自动设置该字段为当前时间,用于”最后一次修改”的时间截,它总是使用当前日期,默认为False; 参数auto now add表示当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为False: 参数auto now add和auto now是相互排斥的,组合将会发生错误
TimeField时间,参数同DateField
DateTimeField日期时间,参数同DateField
FileField上传文件字段
lmageField继承于FileField,对上传的内容进行校验,确保是有效的图片

选项

选项说明
null是否为空
unique唯一
default设置默认值
varbose_name主要是admin后台显示名字
from django.db import models

# Create your models here.
class BookInfo(models.Model):
    name = models.CharField(max_length=10, unique=True, verbose_name='名字')  # 唯一
    # 发布日期
    pub_data = models.DateField(null=True)
    # 阅读量
    readcount = models.IntegerField(default=0)
    # 评论量
    commentcount = models.IntegerField(default=0)
    # 是否逻辑删除
    is_delete = models.BooleanField(default=False)

    # def __str__(self):
    #     return self.name

在models.py里面可以修改表名

from django.db import models


class BookInfo(models.Model):
    
    name = models.CharField(max_length=10, unique=True, verbose_name='名字')

    class Meta:
        # 改表名
        db_table = 'bookinfo'
        # 修改后台admin的显示信息的配置
        verbose_name = 'admin'

    def __str__(self):
        return self.name

外键

models.py可以定义外键
数据库的外键级联操作

在设置外键时,需要通过on_delete选项指明主表除数据时,对于外键引用表数据如何处理,在django.db.models中包含了可选常量 :
CASCADE级联,删除主表数据时连通一起删除外键表中数据
PROTECT保护,通过抛出ProtectedError异常,来阻止删除主表中被外键应用的数据
SET_NULL设置为NULL,仅在该字段null=True允许为null时可用
SET_DEFAULT设置为默认值,仅在该字段设置了默认值时可用
SET()设置为特定值或者调用特定方法
DO_NOTHING不做任何操作,如果数据库前置指明级联性,此选项会抛出IntegrityError异常

from django.db import models

class BookInfo(models.Model):
    name = models.CharField(max_length=10, unique=True, verbose_name='名字')  # 唯一
    # 发布日期
    pub_data = models.DateField(null=True)
    # 阅读量
    readcount = models.IntegerField(default=0)
    # 评论量
    commentcount = models.IntegerField(default=0)
    # 是否逻辑删除
    is_delete = models.BooleanField(default=False)

    class Meta:
        # 改表名
        db_table = 'bookinfo'
        # 修改后台admin的显示信息的配置
        verbose_name = 'admin'

    def __str__(self):
        return self.name

class PeopleInfo(models.Model):
    # 有序字典
    GENDER_CHOICES = (
        (0, 'male'),
        (1, 'female')
    )
    name = models.CharField(max_length=10,verbose_name='名称')
    # 性别
    gender = models.BooleanField(choices=GENDER_CHOICES, default=0)
    description = models.CharField(max_length=200, null=True)
    # 外键
    book = models.ForeignKey(BookInfo, on_delete=models.CASCADE)
    is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')

    class Meta:
        db_table = 'peopleinfo'
        verbose_name = '人物信息'

    def __str__(self):
        return self.name

修改数据库(更换为MySQL)

在主应用的__init__.py中输入如下内容

import pymysql
pymysql.install_as_MySQLdb()

修改主应用的settings.py修改这段代码进行配置

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'book',
]

# 小型数据库:Sqlite
# 中型数据库:MySQL,Sqlserver
# 大型数据库:oracle,DB2
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'HOST': '127.0.0.1', # 主机
        'PORT': '3306',      # 端口
        'USER': 'root',      # 用户名
        'PASSWORD': 'mysql', # 密码
        'NAME': 'book',      # 指定数据库
    }
}

写入models.py内容

from django.db import models

class BookInfo(models.Model):
    name = models.CharField(max_length=10, unique=True, verbose_name='名字')  # 唯一
    # 发布日期
    pub_data = models.DateField(null=True)
    # 阅读量
    readcount = models.IntegerField(default=0)
    # 评论量
    commentcount = models.IntegerField(default=0)
    # 是否逻辑删除
    is_delete = models.BooleanField(default=False)

    class Meta:
        # 改表名
        db_table = 'bookinfo'
        # 修改后台admin的显示信息的配置
        verbose_name = 'admin'

    def __str__(self):
        return self.name

class PeopleInfo(models.Model):
    # 有序字典
    GENDER_CHOICES = (
        (0, 'male'),
        (1, 'female')
    )
    name = models.CharField(max_length=10,verbose_name='名称')
    # 性别
    gender = models.BooleanField(choices=GENDER_CHOICES, default=0)
    description = models.CharField(max_length=200, null=True)
    # 外键
    book = models.ForeignKey(BookInfo, on_delete=models.CASCADE)
    is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')

    class Meta:
        db_table = 'peopleinfo'
        verbose_name = '人物信息'

    def __str__(self):
        return self.name

然后运行python manage.py makemigrations
在这里插入图片描述

现在数据库中还没有该数据库
在这里插入图片描述

再执行python manage.py migrate
在这里插入图片描述
执行之后,数据库中就有该数据
在这里插入图片描述

cmd终端连接数据库之后插入数据

insert into bookinfo(name, pub_data, readcount, commentcount, is_delete) values
('射雕英雄传','1999-1-1','11','22',0),
('天龙八部','1999-11-11','22','33',0),
('笑傲江湖','1999-3-3','55','44',0);

insert into peopleinfo(name, gender, book_id, description, is_delete) values
('郭靖', 1, 1, '降龙十八掌',0),
('黄蓉', 0, 1, '打狗棍法',0),
('乔峰', 1, 2, '降龙十八掌',0),
('段誉', 1, 2, '六脉神剑',0),
('东方不败', 0, 3, '葵花宝典',0);

在这里插入图片描述

在子应用的views.py中编写如下内容

from django.shortcuts import render
from django.http import HttpResponse
from book.models import BookInfo

# Create your views here.

def index(request):
    # 到数据库中查询数据
    books = BookInfo.objects.all()
    # 组织数据
    context ={
        'books': books
    }
    # 传递给模板
    # render(request, '', context)

    return HttpResponse('index')

iPython(python manage.py shell)

类似于 ipython的东西
python manage.py shell
执行之后在执行下面代码
from book.models import BookInfo
BookInfo.objects.all()
然后就会显示数据库中查询到的内容
在这里插入图片描述

使用iPython新增数据

方式一

python manage.py shell

from book.models import BookInfo
book = BookInfo(
    name='python',
    pub_data='2000-01-01'
)
# 需手动调用save方法
book.save()
# 方式二:直接入库
# object模型的管理类
# 对模型的增删改查都找它
python manage.py shell

from book.models import BookInfo
BookInfo.objects.create(
    name='java',
    pub_data='2001-01-02'
)

修改更新数据

方式一

python manage.py shell

from book.models import BookInfo
# 先查询数据
# select * from bookinfo where id=1
book = BookInfo.objects.get(id=1)
# 再更新数据
book.readcount=20
# 调用save保存
book.save()

在这里插入图片描述

方式二

python manage.py shell

from book.models import BookInfo
BookInfo.objects.filter(id=1).update(
    readcount = 100,
    commentcount = 200
)

在这里插入图片描述

删除操作
方式一

python manage.py shell

from book.models import BookInfo
# 先获取数据
book = BookInfo.objects.get(id=5)
# 调用删除方法
book.delete()

方法二

python manage.py shell

from book.models import BookInfo

BookInfo.objects.filter(id=4).delete()

查询操作

get得到某一个数据
all获取所有的
count 个数

python manage.py shell

from book.models import BookInfo

# select * from bookinfo where id=1
book = BookInfo.objects.get(id=1)
book.name
book.readcount

# 不存在会抛出异常
try:
    book = BookInfo.objects.get(id=100)
except Exception:
    pass

# 获取所有数据
book = BookInfo.objects.all()

# count
BookInfo.objects.count()

filter,get,exclude查询

filter,get,exclude都是相当于where查询,select * from bookinfo where id=1
filter :筛选/过滤 返回 n个结果(n = 0/1/n)
get : 返回1个结果
exclude :排除掉符合条似剩下的结果,相当于not
语法新式,如filter(字段名__运算符=值)

python manage.py shell

from book.models import BookInfo

# 查询编号为1的图书
# exact精确的,准确的,就是等于
BookInfo.objects.get(id__exact=1)  # 返回单一对象
BookInfo.objects.get(id=1)  # 简写,同上
BookInfo.objects.filter(id__exact=1)   # 返回列表
BookInfo.objects.filter(id__exact=1)[0]
# 查询书名含'笑'的图书
BookInfo.objects.filter(name__contains='笑')
# 查询书名以‘部’结尾的图书
BookInfo.objects.filter(name__endswith='部')
# 查询书名为空的图书
BookInfo.objects.filter(name__isnull=True)
# 查询编号为1或3或5的图书
BookInfo.objects.filter(id__in=[1, 3, 5])
# 查询编号大于3的图书
# gt 大于   gte 大于等于
# lt 小于   lte  小于等于
BookInfo.objects.filter(id__gt=3)
# 查询数据id不为3的图书
BookInfo.objects.exclude(id=3)
BookInfo.objects.exclude(id__exact=3)
# 查询1980年发表的图书
BookInfo.objects.filter(pub_data__year='1980')
# 查询1990年1月1日后发表的图书
BookInfo.objects.filter(pub_data__gt='1990-1-1')

F和Q对象(属性与属性之间的比较,多条件查询)

F对象:用于属性与属性直接的比较,

F对象的语法形式
filter(字段名__运算符=F('字段名'))

如:查询阅读量大于等于评论星的图书。

python manage.py shell

from book.models import BookInfo
from django.db.models import F

# 查询阅读量大于等于评论量的图书
BookInfo.objects.filter(readcount__gt=F('commentcount'))
# 查询阅读量大于等于评论量两倍的图书
BookInfo.objects.filter(readcount__gt=F('commentcount')*2)

Q对象多条件查询,如:需要查询id大于2 并且阅读量大于20的书籍

Q对象语法形式
: Q()|Q()
并且:Q()&Q()
not:~Q()

python manage.py shell

from book.models import BookInfo
# 需要查询id大于2并且阅读量大于20的书籍
# 方式一
BookInfo.objects.filter(id__gt=2).filter(readcount__gt=20)
# 方式二
BookInfo.objects.filter(id__gt=2, readcount__gt=20)

# 方法三,使用Q对象
from django.db.models import Q
BookInfo.objects.filter(Q(id__gt=2)&Q(readcount__gt=20))

# 需要查询id大于2或者阅读量大于20的书籍,这时候需要用到Q对象
BookInfo.objects.filter(Q(id__gt=2)|Q(readcount__gt=20))

聚合函数(计算,累计)

聚合函数需要使用 aggregate
语法形式是: aggregte(Xxx('字段'))

python manage.py shell

from book.models import BookInfo
from django.db.models import Sum,Avg,Max,Min,Count

# 当前数据的阅读总量
BookInfo.objects.aggregate(Sum('readcount'))

排序

python manage.py shell

from book.models import BookInfo
# 按阅读量排序
# 升序
BookInfo.objects.all().order_by('readcount')
# 降序
BookInfo.objects.all().order_by('-readcount')

关联查询(多个表)

书籍和人物的关系是 l:n
书籍 中没有任何关于人物的字段
人物 中有关专书籍的字段 book 外键
语法形式
通过书籍查询人物:主表模型.关联模型类名小写_set.all()
通过人物查询书籍:从表模型(实例对象).外键

通过书籍查询人物(一对多):查询书籍为1的所有人物信息

python manage.py shell

from book.models import BookInfo

# 查询书籍
book = BookInfo.objects.get(id=1)
# 根据书籍关联人物信息
book.peopleinfo_set.all()

通过人物查询书籍(多对一):查询人物为1的书籍信息

python manage.py shell

from book.models import PeopleInfo
# 查询人物
person = PeopleInfo.objects.get(id=1)
# 根据人物查询书籍
person.book.name

关联查询的筛选

书籍和人物的关系是 l:n
书籍 中没有任何关于人物的字段
人物 中有关专书籍的字段 book 外键
语法形式
一对多
需要的是图书信息,已知条件是人物信息filter(关联模型类名小写__字段__运算符=值)
需要的是主表数据,已知条件是从表信息filter(关联模型类名小写__字段__运算符=值)

多对一
需要的是人物信息,已知条件是书籍信息filter(外键__字段__运算符=值)
需要是是从表数据,已知条件是主表信息filter(外键__字段__运算符=值)

# 一对多
python manage.py shell

from book.models import BookInfo
# 查询图书,要求图书人物为郭靖
BookInfo.objects.filter(peopleinfo__name__exact='郭靖')
BookInfo.objects.filter(peopleinfo__name='郭靖')
# 查询图书,要求图书中人物的表述包含‘八’
BookInfo.objects.filter(peopleinfo__description__contains='八')
# 多对一
python manage.py shell

from book.models import PeopleInfo
# 查询书名为“天龙八部”的所有人物
PeopleInfo.objects.filter(book__name='天龙八部')
PeopleInfo.objects.filter(book__name_exact='天龙八部')
# 查询困书阅读量大于30的所有人物
PeopleInfo.objects.filter(book__readcount__gt=30)

查询集

查询集:也称查询结果集、QuerySet,表示从数据库中获取的对象集合当调用如下过滤器方法时,Django会返回查询集( 而不是简单的列表 ) :

all():返回所有数据
filter(): 返回满足条件的数据
exclude()返回满足条件之外的数据
order_by(): 对结果进行排序

对查询集可以再次调用过滤馨进行过滤,如:books = BookInfo.objects.filter(readcount__gt=30).order_by(‘pub_date’)
也就息味着查询集可以含有零个、一个或多个过滤器:过滤器基于所给的参数限制查询的结果
从SQL的角度讲,查询集与select语句等价,过滤器像where、 limitorder by子句。

查询集两大特性

惰性执行:创建查询集不会访问数据库,直到调用数据时,才会访问数据库,调用数据的情况包括送代、序列化、与i合用。例如,当执行books = BookInfo.objects .all()语句时,并未进行数据库查询,只是创建了一个查询集books,继续执行追历选代操作后,才真正的进行了数据库的查询 for book in books : print(book.name)
缓存:使用同一个查询集,第一次使用时会发生数据库的查询,然后Django会把结果缓存下来,再次使用这个查询集时会使用缓存的数据,减少了数据库的查询次数。

# 该方法每次执行都没有缓存,每次执行都会读取数据库内容
[book.id for book in BookInfo.objects.all()]
[book.id for book in BookInfo.objects.all()]

# 优化后,有缓存,重复使用不会从新读取数据库,提升速率
books = BookInfo.objects.all()
[book.id for book in books]
[book.id for book in books]

限制查询集:可以对查询集进行取下标或切片操作。对查询集进行切片后返回一个新的查询集,不会立即执行查询。

# 不支持负数索引
BookInfo.objects.all()[0:2]

分页

链接:https://docs.djangoproject.com/en/4.2/topics/pagination/

python manage.py shell

from book.models import BookInfo
from django.core.paginator import Paginator

books = BookInfo.objects.all()
# 将数据分页
# object_list 结果集/列表
# per_page  每页多少记录
p = Paginator(books, 2)
# 获取第几页数据
books_page = p.page(1)
books_page[0]

三、视图

项目准备工作

创建项目:django-admin startproject bookmanager
进入项目:cd bookmanager
添加子应用:python manage.py startapp book
在主项目的settings.py中配置
在这里插入图片描述

在子应用的models.py中定义模型

from django.db import models

class BookInfo(models.Model):
    name = models.CharField(max_length=10, unique=True, verbose_name='名字')  # 唯一
    # 发布日期
    pub_data = models.DateField(null=True)
    # 阅读量
    readcount = models.IntegerField(default=0)
    # 评论量
    commentcount = models.IntegerField(default=0)
    # 是否逻辑删除
    is_delete = models.BooleanField(default=False)

    class Meta:
        # 改表名
        db_table = 'bookinfo'
        # 修改后台admin的显示信息的配置
        verbose_name = 'admin'

    def __str__(self):
        return self.name

class PeopleInfo(models.Model):
    # 有序字典
    GENDER_CHOICES = (
        (0, 'male'),
        (1, 'female')
    )
    name = models.CharField(max_length=10,verbose_name='名称')
    # 性别
    gender = models.BooleanField(choices=GENDER_CHOICES, default=0)
    description = models.CharField(max_length=200, null=True)
    # 外键
    book = models.ForeignKey(BookInfo, on_delete=models.CASCADE)
    is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')

    class Meta:
        db_table = 'peopleinfo'
        verbose_name = '人物信息'

    def __str__(self):
        return self.name

在主应用的settings.py中进行数据库相关配置

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'HOST': '127.0.0.1', # 主机
        'PORT': '3306',      # 端口
        'USER': 'root',      # 用户名
        'PASSWORD': '123456', # 密码
        'NAME': 'msql',      # 指定数据库
    }
}

同时还要在主应用的__init__.py中添加如下代码

import pymysql
pymysql.install_as_MySQLdb()

接着运行python manage.py makemigrationspython manage.py migrate会生成数据库
然后使用指令在cmd中连接数据库并插入数据

insert into bookinfo(name, pub_data, readcount, commentcount, is_delete) values
('射雕英雄传','1999-1-1','11','22',0),
('天龙八部','1999-11-11','22','33',0),
('笑傲江湖','1999-3-3','55','44',0);

insert into peopleinfo(name, gender, book_id, description, is_delete) values
('郭靖', 1, 1, '降龙十八掌',0),
('黄蓉', 0, 1, '打狗棍法',0),
('乔峰', 1, 2, '降龙十八掌',0),
('段誉', 1, 2, '六脉神剑',0),
('东方不败', 0, 3, '葵花宝典',0);

在子应用的views.py中设置

from django.shortcuts import render
from django.http import HttpResponse

# Create your views here.=
def index(request):
    return HttpResponse('index')

在子应用中创建urls.py路由文件

from django.urls import path
from login.views import index


urlpatterns = [
    # 参照:正则;函数
    path('index/', index),
]

运行指令python manage.py runsetver之后,在主应用的urls.py中添加引入该路由

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    # 参照:正则;函数
    path('admin/', admin.site.urls),
    # 添加一项
    path('', include('login.urls'))
]

浏览http://127.0.0.1:8000/就会出现如下页面
在这里插入图片描述

允许IP访问

当运行python manage.py runsetver 192.168.0.107:8000指定IP,浏览器可能会出现如下情况
在这里插入图片描述

这时需要在主应用的settings.py中修改如下内容即可正常访问

# 允许以哪个主机的形式访问后端
# 默认是127.0.0.1,如果改变了需要手动加入
# 如果改变允许方式,需要将运行的ip/域名添加进来
# 安全机制,只能以罗列的来访问
ALLOWED_HOSTS = ['192.168.0.107', '127.0.0.1']

在这里插入图片描述

配置URLconf

在主应用的settings.py中有如下指定url的配置内容

# 指定URL配置
ROOT_URLCONF = 'bookmanager.urls'

在主应用的urls.py中有路由配置,可以引入到子应用的路由中

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    # 引入子应用
    path('', include('book.urls')),
]

在子应用中创建了urls.py,写入了部分路由

from django.contrib import admin
from django.urls import path
from book.views import index

urlpatterns = [
    path('index/', index),
]

reverse路由跳转(利用参数name方便修改URL)

reverse 就是通过 name 来动态获取路径(路由)
如果没有设置namespace 则可以通过name来获取 reverse(name)
如果有设置namespace 则可以通过namespace:name来获取 reverse(namespace:name)

在子应用的urls.py内容如下

from django.contrib import admin
from django.urls import path
from book.views import index

urlpatterns = [
    path('index/', index),
]

在子应用的views.py中内容如下,有几个跳转页面

from django.shortcuts import render, redirect
from django.http import HttpResponse
from book.models import BookInfo

# Create your views here.

def index(request):
    # 登陆成功之后需要跳转到
    return redirect('/index/')
    # 首页注册成功之后需要跳转到首页
    return redirect('/index/')

像上面一样,如果需要修改settings.pyindex路由,则views.py中的index也要一起替换,这时可以用下面的方法
name参数:在路由后面添加个name参数

from django.contrib import admin
from django.urls import path
from book.views import index

urlpatterns = [
    # 可以通过name进行命名,可以通过name找到这个路由
    path('index/', index, name='name1'),
]

views.py中内容如下reverse中的参数为上面name的值

from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse
from book.models import BookInfo

# Create your views here.

def index(request):
	# 路由是动态获取的
    path = reverse('name1')
    # 登陆成功之后需要跳转到
    return redirect(path)
    # 首页注册成功之后需要跳转到首页
    return redirect(path)

防止name重复命名
当上面的name参数有重复的时候,可以在主应用urls.py的include中添加一个参数namespace

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    # 引入子应用
    # 在第二个参数添加namespace,这里的name就变成了namespace:name
    # namespace习惯使用子应用名
    path('', include('book.urls', namespace='book')),
]

上面设置了namespace,views.py中就需要如下

from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse
from book.models import BookInfo

# Create your views here.

def index(request):
	# 如果上面使用了namespace参数,这里需要使用namespace:name来获取路由
    path = reverse('book:name1')
    # 登陆成功之后需要跳转到
    return redirect(path)
    # 首页注册成功之后需要跳转到首页
    return redirect(path)

postman工具下载

下载链接:https://www.postman.com/downloads/
下载之后直接双击运行即可

HttpRequest对象(通过位置获取url参数)

关键字参数(url路径)

将url的参数传入视图函数中

子应用的urls.py内容如下,需要使用<>将需要的数据传入index函数中,<>中的内容一定要与index函数里面的一样

from django.contrib import admin
from django.urls import path
from book.views import index

urlpatterns = [
    # http://127.0.0.1:8000/分类id/书籍id/
    # 分组来获取正则中的数据,前面括号括起来的两个参数会传入index函数中
    path('<category_id>/<book_id>/', index),
]

子应用的views.py中函数,<category_id><book_id>分别对应下面两个参数

def index(request, category_id, book_id):
    return HttpResponse(category_id+'/'+book_id)

浏览页面时,后面可以带一定的数据
在这里插入图片描述

GET方式传递查询字符串

链接号的前面是路由,后面表示以get方式传递的参数,称为查询字符串
在登陆的时候会输入用户名和密码,理论上用户名和密码都应该以POST方式进行传递
为了理解,以get请求传递用户名和密码

这里获取后面的参数访问http://127.0.0.1:8000/1/100/?username=itcast&password=123&username=python时得到对应的输出结果,下面为子应用的views.py内容

from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse
from book.models import BookInfo

# 运行时访问http://127.0.0.1:8000/1/100/?username=itcast&password=123&username=python
def index(request, category_id, book_id):
    query_params = request.GET
    print(query_params)  # <QueryDict: {'username': ['itcast'], 'password': ['123']}>
    # 当一键多值得时候只能获取一个值,要获取一键多值得时使用getlist方法
    print(query_params['username'])   # python
    print(query_params.get('username'))  # 同上
    # 获取一键多值
    print(query_params.getlist('username'))    # ['itcast', 'python']
    return HttpResponse(category_id+'/'+book_id+' ' + str(query_params))

POST表单数据

当带参数进行post请求的时候(postman应用),会出现下面的问题
在这里插入图片描述

在这里插入图片描述

意思是禁止访问,这时需要在主应用的settings.py中注释掉一行代码即可正常浏览

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',
]

子应用的views.py内容

from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse
from book.models import BookInfo

# Create your views here.
def index(request, category_id, book_id):
    data = request.POST
    print(data)  # <QueryDict: {'username': ['itcast'], 'password': ['123']}>
    return HttpResponse(category_id+'/'+book_id)

POST json数据

PostMan工具操作
在这里插入图片描述

子应用的views.py内容

from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse
from book.models import BookInfo
import json

# Create your views here.

def index(request, category_id, book_id):
    data = request.body
    print(data)  # b'{\r\n    "username":"itcast",\r\n    "password":"123"\r\n}'
    print(data.decode())  # 输出JSON形式的字符串
    '''
    {
        "username":"itcast",
        "password":"123"
    }
    '''
    # json.dumps 将字典转化为JSON形式的字符串
    # json.loads 将JSON形式的字符串转化为字典
    data = json.loads(data)
    print(data)  # {'username': 'itcast', 'password': '123'}
    return HttpResponse(category_id+'/'+book_id)

请求头

浏览器访问127.0.0.1:8000/1/100/,然后会有输出

from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse
from book.models import BookInfo

def index(request, category_id, book_id):
    # 打印请求头信息
    print(request.META)
    print(request.META['CONTENT_TYPE'])
    return HttpResponse(category_id+'/'+book_id)

其他常用HttpRequest对象属性

method:一个字符串,表示请求使用的HTTP方法,常用值包括 :GET、POST
user :请求的用户对象。
path : 一个字符串,表示请求的页面的完整路径,不包含域名和参数部分。
encoding :一个字符串,表示提交的数据的编码方式。如果为None则表示使用浏览器的默认设置,一般为utf-8。这个属性是可写的,可以通过修改它来修改访问表单数据使用的编码,接下来对属性的任何访问将使用新的encoding值·
FILES:一个类似于字典的对象,包会所有的上传文件

def index(request, category_id, book_id):
    print(request.method)
    print(request.user)
    print(request.path)
    print(request.encoding)
    return HttpResponse(category_id+'/'+book_id)

HttpResponse对象

HttpResponse

视图在接收请求并处理后,必须返回HttpResponse对象或子对象。HttpRequest对象由Django创建,HttpResponse对象由开发人员创建
可以使用django.http.HttpResponse来构造响应对象
HttpResponse(content-响应体,content_type-响应体数据类型,status-状态码)

from django.shortcuts import render
from django.http import HttpResponse

# Create your views here.

def index(request):
    # 参数content:传递字符串
    # 参数status:只能是系统规定的
    # 参数content_type:是一个MIME类型
    #   语法形式:大类/小类 如:text/html   text/css  text/javascript
    #            application/json   image/png  image/gif  image/jpeg
    return HttpResponse('data', statue=400)
    # return HttpResponse('data', statue=400, content_type='')

JsonResponse

返回字典类型数据
子应用views.py内容如下

from django.http import JsonResponse
def index(request):
    data = {'name':'itcast'}
    return JsonResponse(data)

在这里插入图片描述

重定向(页面跳转)

子应用views.py内容如下,浏览之后会自动跳转到http://www.itcast.cn

from django.shortcuts import render, redirect

# Create your views here.
from django.http import JsonResponse
def index(request):
    # 页面跳转到http://www.itcast.cn
    return redirect('http://www.itcast.cn')
from django.shortcuts import render, redirect, reverse

# Create your views here.
from django.http import JsonResponse
def index(request):
    # 页面跳转到http://www.itcast.cn
    path = reverse('book:index')
    return redirect(path)

状态保存

浏览器请求服务器是无状态的。
无状态:指一次用户请求时,浏览器、服务器无法知道之前这个用户做过什么,每次请求都是一次新的请求·
无状态原因:浏览器与服务器是使用Socket套接字进行通信的,服务器将请求结果返回给浏览器之后会关闭当前的Socket连接,而且服务器也会在处理页面完毕之后销毁页面对象。
有时需要保持下来用户浏览的状态,比如用户是否登录过,浏览过哪些商品等
实现状态保持主要有两种方式 :
——在客户端存储信息使用 cookie
——在服务器端存储信息使用 Session

Cookie

Cookie是保存在客户端
Cookie是基于域名(IP)的

Cookie流程

第一次请求过程
1.我们的浏览器第一次请求服务器的时候,不会携带任何cookie信息
2.服务器接收到请求之后,发现 请求中没有任何cookie信息
3.服务器设置一个cookie.这个cookie设置在响应中
4.我们的浏览器接收到这个相应之后,发现响应中有cookie信息,浏览器会将cookie信息保存起来
第二次及其之后的过程
5当我们的浏览器第二次及其之后的请求都会携带cookie信息
6.我们的服务器接收到请求之后,会发现请求中携带的cookie信息,这样的话就认识是谁的请求了

views.py

from django.http import HttpResponse

def set_cookie(request):
    # 1.先判断有没有Cookie信息
    # 先假设没有
    # 2.获取用户名
    username = request.GET.get('username')
    # 3.因为我们假设没有cookie信息,我们服务器就要设置cookie信息
    response = HttpResponse('set_cookie')
    # key,value
    response.set_cookie('username', username)
    # 4.返回响应
    return response

def get_cookie(request):
    '''
    第二次及其之后请求
    '''
    # 服务器接收cookie
    cookies = request.COOKIES
    # cookies 是一个字典
    username = cookies.get('username')
    # 得到用户信息可以继续其他业务
    
    return HttpResponse('get_cookie')

urls.py

from django.contrib import admin
from django.urls import path
from book.views import index

urlpatterns = [
    path('set_cookie/', set_cookie),
    path('get_cookie/', get_cookie),
]

设置Cookie过期时间:使用参数max_age,单位为秒

def set_cookie(request):
    # 1.先判断有没有Cookie信息
    # 先假设没有
    # 2.获取用户名
    username = request.GET.get('username')
    # 3.因为我们假设没有cookie信息,我们服务器就要设置cookie信息
    response = HttpResponse('set_cookie')
    # key,value
    # max_age为秒数,设置Cookie过期时间
    response.set_cookie('username', username, max_age=600)
    # 4.返回响应
    return response

删除Cookieresponse.delete_cookie(key)或者response.set_cookie(key, value, max_age=0)

def set_cookie(request):
    username = request.GET.get('username')
    response = HttpResponse('set_cookie')
    response.set_cookie('username', username, max_age=600)
    # 删除cookie
    response.delete_cookie('username')
    # 或者
    response.set_cookie('username', username, max_age=0)
    # 4.返回响应
    return response

Session

settings.py中默认启用了session
在这里插入图片描述

Session保存在服务器
session需要依赖于cookie
如果浏览器禁用了cookie,则session不能实现
项目的数据库中会有个django_session表格保存session相关数据(session_key,session_data,expire_date)(设置session的时候将数据保存到数据库中
在这里插入图片描述

session流程

第一次请求:
1.我们第一次请求的时候可以携带一些信息(用户名/密码) cookie中没有任何信息
2.当我们的服务器接收到这个请求之后,进行用户名和密码的验证,验证没有问题可以设置session信息
3.在设置session信息的同时(session信息保存在服务器端),服务器会在响应头中设置一个session id的cookie信息(由服务器设置)
4.客户端(浏览器)在接收到响应之后,会将cookie信息保存起来(保存 sessionid的信息)
第二次及其之后的请求:
5.第二次及其之后的请求都会携带 session id信息
6.当服务器接收到这个请求之后,会获取到sessionid信息,然后进行验证,验证成功,则可以获取 session信息(session信息保存在服务器端)

第一次请求:
1.我们第一次请求的时候可以携带一些信息(用户名/密码) cookie中没有任何信息
2.当我们的服务器接收到这个请求之后,进行用户名和密码的验证,验证没有问题可以设置session信息
3.在设置session信息的同时(session信息保存在服务器端),服务器会在响应头中设置一个session id的cookie信息(由服务器设置)
4.客户端(浏览器)在接收到响应之后,会将cookie信息保存起来(保存 sessionid的信息)
第二次及其之后的请求:
5.第二次及其之后的请求都会携带 session id信息
6.当服务器接收到这个请求之后,会获取到sessionid信息,然后进行验证,验证成功,则可以获取 session信息(session信息保存在服务器端)

views.py内容如下

def set_session(request):
    '''
    第一次请求:
    1.我们第一次请求的时候可以携带一些信息(用户名/密码) cookie中没有任何信息
    2.当我们的服务器接收到这个请求之后,进行用户名和密码的验证,验证没有问题可以设置session信息
    3.在设置session信息的同时(session信息保存在服务器端),服务器会在响应头中设置一个session id的cookie信息
    4.客户端(浏览器)在接收到响应之后,会将cookie信息保存起来(保存 sessionid的信息)
    '''
    # 1.cookie中没有任何信息
    print(request.COOKIES)
    # 2.对用户名和密码进行验证
    # 假设认为用户名和密码正确
    user_id = 6666
    # 3.设置session信息(设置的时候将数据保存到数据库中,设置cookie信息是以sessionid为key)
    # request.session 理解为字典
    request.session['user_id'] = user_id
    # 4.返回响应
    return HttpResponse('set_session')

def get_session(request):
    '''
    第二次及其之后的请求**:
    5.第二次及其之后的请求都会携带 session id信息
    6.当服务器接收到这个请求之后,会获取到sessionid信息,然后进行验证,验证成功,则可以获取 session信息(session信息保存在服务器端)
    '''
    # 请求都会携带 session_id信息
    print(request.COOKIJES)
    # 2,会获取到sessionid信息,然后进行验证
    # 验证成功,可以获取 session信息

    # request.session 字典
    user_id = request.session['user_id']
    user_id = request.session.get('user_id')

    # 3.返回响应
    return HttpResponse('get_session')

类视图

将两个视图转化为一个视图

登录页面
GET请求是获取登录的页面
POST请求是验证登录(用户名和密码是否正确)

from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse
# GET 请求获取登入页面
def show_login(request):

    return render(request)

# POST 请求是验证登入(用户名和密码是否正确)
def veri_login(request):
    return redirect('首页')

转化为一个视图如下

from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse

# 由连个视图变为一个视图
def login(request):
    # 区分业务逻辑
    if request.method == 'GET':
        # GET请求获取登入页面
        return render(request)
    else:
        # POST 请求是验证登入(用户名和密码是否正确)
        return redirect('首页')

类视图面向对象

类视图是采用面向对象的思路
一、定义类视图
1、继承自view(from django.views import View)
2、不同的请求方式有不同的业务逻辑
类视图的方法就直接采用http的请求名字作为我们的函数名,例如:get,post,put,patch等(as_view()中)
3、类视图的方法的第二个参数必须是请求实例对象
类视图的方法必须有返回值,返回值是HttpResponse及其子类

from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse
from django.views import View

class LoginView(View):

    def get(self,request):
        return HttpResponse('get')

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

类视图URL引导

from django.urls import path
from book.views import index,LoginView
# as_view函数会根据请求方式的不同调用上面不同的函数,如果是GET请求,则调用上面的get函数
urlpatterns = [
    path('login/', LoginView.as_view()),
]

需求如下

个人中心页面 ————必须登入才能显示
GET方式展示个人中心
POST实现个人中心信息修改
定义类视图

views.py代码如下

from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse


from django.views import View

class CenterView(View):
    def get(self, request):
        return HttpResponse('个人中心展示')

    def post(self, request):
        return HttpResponse('个人中心信息修改')

urls.py

from django.urls import path
from book.views import index,CenterView

urlpatterns = [
    path('center/', CenterView.as_view()),
]

上面浏览后在没有登陆的情况下还是可以正常浏览,效果如下
在这里插入图片描述

由于展示个人中心页面必须登入才能显示所以需要做如下修改,这里可以判断用户是否登录没有登录的情况下,系统框架会自动跳到登入的界面

from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse


from django.views import View
from django.contrib.auth.mixins import LoginRequiredMixin

class CenterView(LoginRequiredMixin, View):
    def get(self, request):
        return HttpResponse('个人中心展示')

    def post(self, request):
        return HttpResponse('个人中心信息修改')

在加了如上代码之后,浏览http://127.0.0.1:8000/center/之后,在没有登入的情况下,会自动跳转到登录界面(这里没有导入登录界面路由),结果如下
在这里插入图片描述

中间件

中间件的作用:每次请求和响应的时候都会调用

Django中的中间件是一个轻量级、底层的插件系统,可以介入Django的请求和响应处理对程,修改Django的输入或输出·中间件的设计为开发者提供了一种无侵入式的开发方式,增强了Django框架的健壮性:
我们可以使用中间件,在Diango处理视图的不同阶段对输入或输出进行干预·

settings.py中有这么一段

# 中间件
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',
]

中间件的定义:定义一个中间件工厂函数,然后返回一个可以被调用的中间件。中间件工厂函数需要接收一个可以调用的get_response对象·返回的中间件也是一个可以被调用的对象,并且像视图一样需要接收一个request对象参数,返回一个response对象·
在子应用中创建middleware.py文件,内容如下(中间件的定义

def simple_middleware(get_response):
    def middleware(request):
        print('before request')
        # 响应前
        response = get_response(request)
        # 响应后
        print('after request')
        return response
    return middleware

在settings.py中添加middleware文件

# 中间件
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',
    'book.middleware.simple_middleware',   # 添加这行
]

浏览页面,终端就会打印如下内容
在这里插入图片描述

实例:可以判断每次请求是否携带了cookie中的某些信息,通常如果不使用中间件,要在浏览中判断是否有cookie,需要在每个函数中都添加判断的代码,如下
views.py

from django.views import View
from django.contrib.auth.mixins import LoginRequiredMixin

class CenterView(LoginRequiredMixin, View):
    def get(self, request):
        username = request.COOKIES.get('username')
        if username is None:    # 添加判断
            print('username is None')
        return HttpResponse('个人中心展示')

    def post(self, request):
        username = request.COOKIES.get('username')
        if username is None:    # 添加判断
            print('username is None')
        return HttpResponse('个人中心信息修改')

可以利用中间件,建立个中间件文件,在中间件中加入如下代码,则在上面的函数中就不需要添加判断
views.py

from django.views import View
from django.contrib.auth.mixins import LoginRequiredMixin

class CenterView(LoginRequiredMixin, View):
    def get(self, request):
        return HttpResponse('个人中心展示')

    def post(self, request):
        return HttpResponse('个人中心信息修改')

middleware.py

def simple_middleware(get_response):
    def middleware(request):
        # 添加判断
        username = request.COOKIES.get('username')
        if username is None:
            print('username is None')
            
        print('before request')
        # 响应前
        response = get_response(request)
        # 响应后
        print('after request')
        return response
    return middleware

在上面的实例中,如果没有登入,可以直接加个return返回某页面,代码如下

from django.http import HttpResponse
def simple_middleware(get_response):
    def middleware(request):
        username = request.COOKIES.get('username')
        if username is None:
            print('username is None')
            # 没有登入直接返回
            return HttpResponse('没有登入')
        
        print('before request')
        # 响应前
        response = get_response(request)
        # 响应后
        print('after request')
        return response
    return middleware

效果如下
在这里插入图片描述

多个中间件顺序:请求前的执行顺序是按照注册的顺序从上往下(settings.py中),响应后的顺序是从下往上(注册的反顺序)
在middleware.py中定义多个中间件

def simple_middleware1(get_response):
    def middleware(request):
        print('before request 11111')
        # 响应前
        response = get_response(request)
        # 响应后
        print('after request 11111')
        return response
    return middleware

def simple_middleware2(get_response):
    def middleware(request):
        print('before request 22222')
        # 响应前
        response = get_response(request)
        # 响应后
        print('after request 22222')
        return response
    return middleware

在settings.py中引入(注册顺序右上到下

# 中间件
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',
    
    'book.middleware.simple_middleware1',   # 添加这行
    'book.middleware.simple_middleware2',   # 添加这行
]

输出结果如下

before request 11111
before request 22222
after request 22222
after request 11111

模板

Django使用自带模板,在工程中创建模板目录templates,在这个目录里面创建index.html文件,内容额如下,这里写的是固定的,算不撒上是模板,模板需要里面的内容可以灵活变动,如下面的itcast可以根据信息的不同,显示不同内容,需要特殊语法将数据库或程序中的内容导入其中

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<a href="">itcast</a>
</body>
</html>

基本配置

在settings.py中配置文件修改TEMPLATES配置项的DIRS值

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],   # 修改这里
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

子应用views.py

from django.shortcuts import render

class HomeView(View):
    def get(self, request):
        return render(request, 'index.html')

子应用urls.py

from django.urls import path
from book.views import HomeView

urlpatterns = [
    path('index/', HomeView.as_view()),
]

使用模板

views.py

from django.shortcuts import render
from django.views import View

class HomeView(View):
    def get(self,request):
        # 1.获取数据
        username = request.GET.get('username')
        # 2.组织数据
        context = {
            'username': username
        }
        return render(request, 'index.html', context=context)

templates中的index.html内容如下(使用{{ username }}获取上面传入的数据)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<a href="">{{ username }}</a>
</body>
</html>

浏览链接http://127.0.0.1:8000/index/?username=abcd就会显示username后面的数据
在这里插入图片描述

模板语法

下面列举了列表,遍历,判断,索引展示
注意:运算符两侧必须要有空格
views.py

from django.shortcuts import render
from django.views import View

# Create your views here.
class HomeView(View):
    def get(self,request):
        # 1.获取数据
        username = request.GET.get('username')
        # 2.组织数据
        context = {
            'username': username,
            'age': 10,
            'friend': ['toy', 'jack'],
            'scores': {
                'math': 100,
                'english': 90,
            }
        }
        return render(request, 'index.html', context=context)

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<a href="">{{ username }}</a>
<hr>
年龄:{{ age }}
<hr>
朋友:{{ friend }}
<hr>
朋友:{{ friend.1 }}
<hr>
分数:{{ scores.math }}
<hr>
遍历索引朋友:{% for item in friend %}
<li>{{ forloop.counter }}{{ item }}</li>
{% endfor %}
<hr>
{% if age > 10 %}
    大于10岁
{% else %}
    不大于10岁
{% endif %}

</body>
</html>

效果如下
在这里插入图片描述

过滤器
过滤器语法形式:变量|过滤器:参数
过滤器有几个常用的

safe,禁用转义,告诉模板这个变量是安全的,可以解释执行
length,长度,返回字符丰包含字符的个数,或列表、元组、字典的元素个数
default,默认值,如果变量不存在时则返回默认值(date|default:'默认值'
date,日期,用于对日期类型的值进行字符串格式化(value|data:"Y年m月j日 H时i分s秒"),常用的格式化字符如下 :

字符含义解释
Y格式4位(y两位)
m01,02,12
d01,02
j1,2
H24小时制(h12小时制)
i0-59
s0-59

views.py

from django.shortcuts import render
from django.views import View
import datetime

class HomeView(View):
    def get(self,request):
        username = request.GET.get('username')
        context = {
            'datetime': datetime.datetime.now(),
        }
        return render(request, 'index.html', context=context)
    
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
日期:{{ datetime|date:'Y m d' }}
</body>
</html>

在这里插入图片描述

转义字符如下,按如下方式书写,会有转义字符
views.py

class HomeView(View):
    def get(self,request):
        # 1.获取数据
        username = request.GET.get('username')
        # 2.组织数据
        context = {
            'desc': '<script>alert("hot")</script>'
        }
        return render(request, 'index.html', context=context)

index.html

<body>
转义字符:{{ desc }}
</body>

按上面书写会是这种效果,没有弹窗,会将scrpipt转化为字符串显示
在这里插入图片描述

经过如下修改,就会弹出弹框

<body>
转义字符:{{ desc|safe }}
</body>

在这里插入图片描述

无定义的量默认值长度
在views.py中没有定义的变量

<body>
无定义变量:{{ abc|default:'默认值' }}
<hr>
长度:{{ friend|length }}
</body>
class HomeView(View):
    def get(self,request):
        # 1.获取数据
        username = request.GET.get('username')
        # 2.组织数据
        context = {
            'friend': ['toy', 'jack'],
        }
        return render(request, 'index.html', context=context)

在这里插入图片描述

模板继承

模板继承和类的继承含义是一样的,主要是为了提高代码重用,减轻开发人员的工作量
父标签
如果发现在多个模板中某些内容相同,那就应该把这段内容定义到父模板中。
标签block: 用于在父模板中预留区域,留给子模板填充差异性的内容,名字不能相同·为了更好的可读性,建议给endblock标签写上名字,这个名字与对应的block名字相同,父模板中也可以使用上下文中传递过来的数据。
字标签
标签extends: 继承,写在子模板文件的第一行
在这里插入图片描述
子模版不用填充父模版中的所有预留区域,如果子模版没有填充,则使用父模版定义的默认值

这个页面和另一个页面某个部分是一样的模板格式
在template目录中创建一个基础文件base.html,代码如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}Title{% endblock title %}</title>
</head>
<body>
{% block header %}
<h1>顶部</h1>
{% endblock header %}

{% block main %}
<h1>主要部分</h1>
{% endblock main %}

{% block footer %}
<h1>底部</h1>
{% endblock footer %}
</body>
</html>

创建detail.html如下

把继承的模板写在最上面
需要改哪里,直接实现block,进行替换,没写的就还是原先base.html的内容
不需要的部分直接用空白block进行替换
(这里替换了标题,导航栏主题,删除了底部,标题没有变动)

{% extends 'base.html' %}

{% block title %}
替换标题
{% endblock title %}

{% block main %}
<a href="">替换主体</a>
{% endblock main %}

{% block footer %}{% endblock %}

在这里插入图片描述

Django使用jinja2模板

jinja2是 Python 下一个被广泛应用的模板引擎,是由Python实现的模板语言,他的设计思想来源于Django的模板引擎,并扩展了其语法和一系列强大的功能,尤其是Flask框架内置的模板语言
由于django默认模板引擎功能不齐全,速度慢,所以我们也可以在Django中使用jinja2, jinja2宣称比django默认模板引擎快10-20倍
Django主流的第三方APP基本上也都同时支持Diango默认模板及inja2,所以要用inia2也不会有多少障碍。

安装jinja2模板:pip install jinja2
jiaja2配置
在settings.py中配置

TEMPLATES = [
    {
        # 'BACKEND': 'django.template.backends.django.DjangoTemplates',  # 默认引擎
        'BACKEND': 'django.template.backends.jinja2.Jinja2',  # 修改为Jinja2模板
        'DIRS': [os.path.join(BASE_DIR, 'template')],
        'APP_DIRS': True,
        'OPTIONS': {
            'environment': 'jinja2.Environment',  # 添加这行  默认的,可加可不加
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },# 如果报错,原始的代码复制到下面
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',  # 默认引擎
        'DIRS': [os.path.join(BASE_DIR, 'template')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

直接使用上面的index.html会出现错误

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<a href="">{{ username }}</a>
<hr>
年龄:{{ age }}
<hr>
朋友:{{ friend }}
<hr>
朋友:{{ friend.1 }}
<hr>
分数:{{ scores.math }}
<hr>
遍历索引朋友:{% for item in friend %}
<li>{{ forloop.counter }}{{ item }}</li>
{% endfor %}
<hr>
{% if age > 10 %}
    大于10岁
{% else %}
    不大于10岁
{% endif %}
<hr>
日期:{{ datetime|date:'Y m d' }}
转义字符:{{ desc|safe }}
无定义变量:{{ abc|default:'默认值' }}
<hr>
长度:{{ friend|length }}
</body>
</html>

在这里插入图片描述

这是因为jiaja2的语法和上面模板的语法有些区别
区别在于过滤器不同,上面的模板是forloop,jiaja2是loop
jiaja2过滤器
index.html

<body>

日期:{{ date(datetime) }}
</body>
from django.views import View
import datetime

class HomeView(View):
    def get(self,request):
        context = {
            'datetime': datetime.datetime.now(),
        }
        return render(request, 'index.html', context=context)

有上面的还不够,会显示date未定义,这时在子应用中创建jinja2_env.py文件,内容如下

from django.template.defaultfilters import date
from jinja2 import Environment

def environment(**option):
    # 1.创建Environment实例
    env = Environment(**option)
    # 2.指定jinja2的函数指向django的指定过滤器
    env.globals.update({
        'date': date
    })
    # 3.返回Environment实例
    return env

然后修改配置settings.py

TEMPLATES = [
    {
        # 'BACKEND': 'django.template.backends.django.DjangoTemplates',  # 默认引擎
        'BACKEND': 'django.template.backends.jinja2.Jinja2',  # 修改为Jinja2模板
        'DIRS': [os.path.join(BASE_DIR, 'template')],
        'APP_DIRS': True,
        'OPTIONS': {
            # 'environment': 'jinja2.Environment',  # 添加这行  默认的,可加可不加
            'environment': 'book.jinja2_env.environment',  # 指定jinja2的环境
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },# 如果报错,原始的代码复制到下面
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',  # 默认引擎
        'DIRS': [os.path.join(BASE_DIR, 'template')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

然后就会出现内容
在这里插入图片描述
要显示特定的格式,在后面和之前一样添加参数

<body>

日期:{{ date(datetime,'Y m d') }}
</body>

在这里插入图片描述

自定义过滤器

from jinja2 import Environment

def environment(**option):
    # 1.创建Environment实例
    env = Environment(**option)
    # 2.将自定义过滤器添加到环境中
    env.filters['do_listreverse'] = do_listreverse
    # 3.返回Environment实例
    return env

# 自定义过滤器
def do_listreverse(li):
    if li == 'B':
        return "abcd"

CSRF(跨站请求伪造)

CSRF 指攻击者盗用了你的身份,以你的名义发送恶意请求。包括:以你名义发送邮件,发消息山盗取你的账号,甚至于购买商品,虚拟货币转账…
造成的问题 :个人隐私泄露以及财产安全。

在这里插入图片描述
生成随机码
csrf_token放入cookie是同源策略的原因,黑客(钓鱼)网站是用于获取不到我们当前网站的cookie信息的

    def get(self, request):
        from django.middleware.csrf import get_token
        # 生成随机码
        csrf_token = get_token(request)
        response = render(request, 'index.html', context={'csrf_token': csrf_token})
        # 将验证码保存至cookie中
        response.set_cookie('csrftoken', csrf_token)
        return response

获取用户输入的随机码并验证

    def index(self, request):
        # 获取用户数据
        user_token = request.POST.get('csrftoken')
        to_account = request.POST.get('to_account')
        money = request.POST.get("money")
        # 获取系统生成的验证码
        server_token = request.COOKIES.get('csrf_token')
        # 输入的验证码和系统生成的验证
        if user_token != server_token:
            return HttpResponse('输入错误')

上面获取数据是通过name属性获取的

<body>

<input type="hidden" name="csrftoken">
<input type="text" name="to_account">
<input type="number" name="money">
</body>

:是协议、域名、端口号组成
同源策略:同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。

session保存至redis中

session数据的保存、获取、删除(views.py)

# 增加数据
class SetSession(View):
    def get(self, request):
        request.session['name'] = 'icast'
        return HttpResponse('abc')

# 获取数据
class GetSession(View):
    def get(self, request):
        name = request.session['name']
        return HttpResponse('abc')

# 删除数据
class DelSession(View):
    def get(self, request):
        # 删除一条数据
        del request.session['name']
        # 删除session所有数据
        # request.session.clear()
        # 把数据库/redis中的key都删除了
        # request.session.flush()
        return HttpResponse('abc')

# session时间,到了时间自动删除
class DelSession(View):
    def get(self, request):
        # 设置session时间,默认两周
        request.session.set_expiry(10)
        return HttpResponse('abc')

session数据存储位置
在settings.py中,可以设置session数据的存储方式,可以保存在数据库、本地缓存等

保存在数据库中,如下设置可以写,也可以不写,这是默认方式

SESSION_ENGINE=‘django.contrib.sessions.backends.db’
如果保存在数据库中,需要在INSTALLED_APPS中安装Session应用
INSTALLED_APPS = [

‘django.contrib.sessions’,

]
在这里插入图片描述

本地缓存:存储在本机内存中,如果丢失则不能找回,比数据库的方式读写更快

SESSION_ENGINE=‘django.contrib.sessions.backends.cache’

混合缓存:优先从本机内存中存取,如果没有则从数据库中存取

SESSION_ENGINE=‘django.contrib.sessions.backends.cached_db’

Redis:在redis中保存session,需要引入第三方扩展,我们可以使用django-redis来解决。
django-redis链接:https://django-redis-chs.readthedocs.io/zh_CN/latest/

安装扩展:pip install django-redis
配置settings.py内容如下

# caches缓存
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/1",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        }
    }
}

SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"

CSRF的django使用

login.html

<body>

<from action="" method="post">
    <input type="text" name="money">
    <input type="submit" value="提交">
</from>
</body>

urls.py

from book.views import LoginView

urlpatterns = [
    path('login/', LoginView.as_view()),
]

views.py

class LoginView(View):
    def get(self, request):
        return render(request, 'login.html')

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

在上面的基础上,浏览链接输入内容后点击提交会报错,需要在login.html中加入如下代码

<body>

<from action="" method="post">
    {% csrf_token %}
    <input type="text" name="money">
    <input type="submit" value="提交">
</from>
</body>

然后点击提交,会跳转到post请求中去

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值