Django学习基础笔记(二)

Django学习基础笔记

  • 中间件
    • 中间件的定义方法
    • 多个中间件
  • 模板
  • CSRF 攻击
  • 数据库
    • ORM 框架
    • 数据库配置
    • 定义模型类
    • Shell 演示
  • 数据库操作
      • 过滤查询
      • F 对象 和 Q 对象
      • 聚合函数和排序
      • 关联查询
  • 查询集
  • 其他补充说明
    • 添加测试数据
    • 字段类型(略)
    • 选项(略)
    • 外键

中间件

Django 的中间件是一个轻量级、底层的插件系统,可以介入请求和响应处理过程,修改 Django 的输入或输出。
在子应用中定义,对全局视图函数有影响。

中间件的定义方法

在子应用中新建一个 middleware.py 文件

def my_middleware(get_response):
    print('init 被调用')
    def middleware(request):
        print('before request 被调用')
        response = get_response(request)
        print('after response 被调用')
        return response
    return middleware

在 settings.py 文件中添加注册中间件

MIDDLEWARE = [
   .....................
     # 添加中间件
    'users.middleware.my_middleware',  ]

多个中间件

MIDDLEWARE = [
    'classview.middleware.my_middleware1',
    'classview.middleware.my_middleware2',]


def my_middleware1(get_response):
    print('init 1被调用')
    def middleware1(request):
        print('before request 1被调用')
        response = get_response(request)
        print('after response 1被调用')
        return response
    return middleware1

def my_middleware2(get_response):
    print("init 2被调用")
    def middleware2(request):
        print("before request 2 被调用")
        response=get_response(request)
        print("after response 2被调用")
        return response
    return middleware2

多个中间件的执行顺序:
在请求视图被处理前,中间件由上至下依次执行
在请求视图被处理后,中间件由下至上依次执行

init 2被调用
init 1被调用
before request 1被调用
before request 2 被调用
view 视图被调用
after response 2被调用
after response 1被调用

模板

第一步: 需要先创建 templates 模板文件夹,项目下建目录。
第二步: 在 settings.py 文件中配置模板文件夹路径。
第三步: 在模板文件夹中添加模板文件。
第四步: 在视图函数中调用 render( ) 函数, 调用模板, 有需要可以传参数。

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

TEMPLATES = [
#......................................
'DIRS': [os.path.join(BASE_DIR, 'templates')],  # 在此处拼接模板路径]
# os.path.join拼接路径,BASE_DIR项目的绝对路径

定义模板,在 templates 目录中新建一个模板文件,如 index.html
渲染模板,Django 提供了一个实现模板渲染的函数 render( )
使用方式: render( request, ‘模板文件名称’, 添加给模板的数据)

from django.shortcuts import render
def index(request):
    # 定义一个变量, 拼接为字典格式: 
    context={'city': '北京'}
    # 返回 render() 函数
    return render(request,'index.html',context)

CSRF 攻击

CSRF(Cross Site Request Forgery)跨站请求伪造。
CSRF指攻击者盗用了的身份,以你的名义发送恶意请求。
CSRF攻击图解
Django学习基础笔记(二)_第1张图片
CSRF防护图解

  1. 在客户端向后端请求界面数据的时候,后端会往响应中的 cookie 中设置 csrf_token 的值
  2. 在 Form 表单中添加一个隐藏的的字段,值也是 csrf_token
  3. 在用户点击提交的时候,会带上这两个值向后台发起请求。
    后端接受到请求,以会以下几件事件:
    从 cookie 中取出 csrf_token
    从 表单数据中取出来隐藏的 csrf_token 的值
  4. Django 进行对比 (这里的对比不要求一定一致, 有时候两个值不一致)如果对比能够通过, 则是正常的请求。如果对比不能够通过,代表不是正常的请求,不执行下一步操作。
    Django学习基础笔记(二)_第2张图片
    在 cookie 中设置值, 在表单中也设置一个隐藏字段, 把两个字段提交进行对比. 判断是否是合理的请求。

数据库

ORM 框架

在 ORM 框架中,它帮我们把类和数据表进行了一个映射,可以让我们通过类和类对象就能操作它所对应的表格中的数据。
Django学习基础笔记(二)_第3张图片
使用 django 进行数据库开发的步骤如下:

  1. 配置数据库连接信息
  2. 在 models.py 中定义模型类
  3. 迁移
  4. 通过类和对象完成数据增删改查操作

数据库配置

  1. 使用 MySQL 数据库首先需要安装驱动程序
#虚拟环境的命令行中执行
pip install PyMySQL
  1. 在 Django 的工程同名子目录的 init.py 文件中添加如下语句,作用是让 Django 的 ORM 能以 mysqldb 的方式来调用 PyMySQL。
from pymysql import install_as_MySQLdb
install_as_MySQLdb()
  1. 修改 DATABASES 配置信息
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'HOST': '127.0.0.1',  # 数据库主机
        'PORT': 3306,  # 数据库端口
        'USER': 'root',  # 数据库用户名
        'PASSWORD': 'mysql',  # 数据库用户密码
        'NAME': 'django_demo'  # 数据库名字
    }
}
  1. 在 MySQL 中创建数据库
create database django_demo default charset=utf8;

定义模型类

注:子应用,需要在setting中注册!INSTALLED_APPS

  1. 模型类被定义在 "应用 / models.py " 文件中。
  2. 定义一个模型类
  3. 该模型类必须继承自 Model 类,位于包 django.db.models 中.

(1) 创建子应用 booktest,setting注册,在 models.py 文件中定义模型类。

from django.db import models

# 定义图书模型类 BookInfo
class BookInfo(models.Model):
    btitle = models.CharField(max_length=20, verbose_name='名称')
    bpub_date = models.DateField(verbose_name='发布日期')
    bread = models.IntegerField(default=0, verbose_name='阅读量')
    bcomment = models.IntegerField(default=0, verbose_name='评论量')
    is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')

    class Meta:
        db_table = 'tb_books'  # 指明数据库表名
        verbose_name = '图书'  # 在admin站点中显示的名称
        verbose_name_plural = verbose_name  # 显示的复数名称

    def __str__(self):
        """定义每个数据对象的显示信息"""
        return self.btitle

# 定义英雄模型类 HeroInfo
class HeroInfo(models.Model):
    GENDER_CHOICES = (
        (0, 'female'),
        (1, 'male')
    )
    hname = models.CharField(max_length=20, verbose_name='名称') 
    hgender = models.SmallIntegerField(choices=GENDER_CHOICES, default=0, verbose_name='性别')  
    hcomment = models.CharField(max_length=200, null=True, verbose_name='描述信息') 
    hbook = models.ForeignKey(BookInfo, on_delete=models.CASCADE, verbose_name='图书')  # 外键
    is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')

    class Meta:
        db_table = 'tb_heros'
        verbose_name = '英雄'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.hname

数据库表名::模型类如果未指明表名,Django 默认以 小写 app应用名_小写模型类名 为数据库表名。
定义属性:需要指定字段类型,通过字段类型的参数指定选项,语法如下:属性=models.字段类型(选项)。
(2)迁移

#1)生成迁移文件
python manage.py makemigrations
#2)同步到数据库中
python manage.py migrate

Shell 演示

python manage.py shell

#导入两个模型类,以便后续使用
from booktest.models import BookInfo, HeroInfo

数据库操作

from booktest.models import BookInfo, HeroInfo提前导入

  1. 通过创建模型类对象,执行对象的save()方法保存到数据库中。

外键定义补充说明:
hbook(对象)——>hbook_id(id)(表中存储的为id)
hbook_id=3 (id赋给id)
hbook_id=book.id (id赋给id)
hbook=book (对象赋给对象)

>>> from datetime import date
>>> book = BookInfo(
    btitle='西游记',
    bpub_date=date(1988,1,1),
    bread=10,
    bcomment=10
)
>>> book.save()
>>> hero = HeroInfo(
    hname='孙悟空',
    hgender=0,
    hbook=book
)
>>> hero.save()
>>> hero.save()
>>> hero2 = HeroInfo(
    hname='猪八戒',
    hgender=0,
    hbook_id=book.id
)
>>> hero2.save()
  1. 通过 模型类.objects.create( ) 保存
>>> HeroInfo.objects.create(
    hname='沙悟净',
    hgender=0,
    hbook=book
)
<HeroInfo: 沙悟净>

  1. 使用对象删除:对象.delete()
 #获取对象: 
hero = HeroInfo.objects.get(id=13)
 #调用对象的删除方法: 
hero.delete()
  1. 使用查询集删除: 模型类.objects.filter().delete( )
HeroInfo.objects.filter(id=14).delete()

  1. 修改模型类对象的属性,然后执行 save() 方法
hero = HeroInfo.objects.get(hname='猪八戒')
hero.hname = '猪悟能'
hero.save()
  1. 使用方式:查询集.update()
# 模型类.objects.filter().update(),返回值为受影响的行数。
HeroInfo.objects.filter(hname='沙悟净').update(hname='沙僧')

基本查询
get 查询单一结果,如果不存在会抛出 模型类.DoesNotExist 异常.
all 查询多个结果。
count 查询结果数量。

>>> BookInfo.objects.all()
>>> BookInfo.objects.get(id=3)
>>> BookInfo.objects.get(id=100)  #DoesNotExist 异常
>>> BookInfo.objects.count()

过滤查询

过滤条件的表达语法如下:属性名不能包括多个下划线
属性名称__比较运算符 = 值
filter 过滤出多个结果
exclude 排除掉符合条件剩下的结果
get 过滤单一结果

注:
filter查询数据不存在返回
get查询数据不存在会抛出 模型类.DoesNotExist 异常

比较查询
gt 大于 (greater then)
gte 大于等于 (greater then equal)
lt 小于 (less then)
lte 小于等于 (less then equal)

#相等exact:表示判等。
#例:查询编号为1的图书。
BookInfo.objects.filter(id__exact=1)
可简写为:
BookInfo.objects.filter(id=1)

#模糊查询contains:是否包含。说明:如果要包含%无需转义,直接写即可。
#例:查询书名包含'传'的图书。
BookInfo.objects.filter(btitle__contains='传')

#startswith、endswith:以指定值开头或结尾。
#例:查询书名以'部'结尾的图书
BookInfo.objects.filter(btitle__endswith='部')
#以上运算符都区分大小写,在这些运算符前加上i表示不区分大小写,如iexact、icontains、istartswith、iendswith.

#空查询isnull:是否为null
#例:查询书名不为空的图书。
BookInfo.objects.filter(btitle__isnull=False)

#范围查询in:是否包含在范围内
#例:查询编号为1或3或5的图书
BookInfo.objects.filter(id__in=[1, 3, 5])

#比较查询
#gt 大于 (greater then)
#gte 大于等于 (greater then equal)
#lt 小于 (less then)
#lte 小于等于 (less then equal)

#例:查询编号大于3的图书
BookInfo.objects.filter(id__gt=3)

exclude()过滤器,取反。
#例:查询编号不等于3的图书
BookInfo.objects.exclude(id=3)

日期查询
year、month、day、week_day、hour、minute、second:
对日期时间类型的属性进行运算
#例:查询1980年发表的图书。
BookInfo.objects.filter(bpub_date__year=1980)
#例:查询1980年1月1日后发表的图书。
BookInfo.objects.filter(bpub_date__gt=date(1990, 1, 1))

F 对象 和 Q 对象

F 对象
两个属性比较使用 F对象,语法如下:F(属性名)

#例:查询阅读量大于等于评论量的图书.
# 导入: 
from django.db.models import F

# 查询时使用 F 对象
BookInfo.objects.filter(bread__gte=F('bcomment'))

#可以在 F 对象 上使用算数运算.
#例:查询阅读量大于2倍评论量的图书。
BookInfo.objects.filter(bread__gt=F('bcomment') * 2)

Q 对象

  1. 多个过滤器逐个调用表示 逻辑与 关系
#例:查询阅读量大于20,并且编号小于3的图书。
BookInfo.objects.filter(bread__gt=20,id__lt=3)
#或
BookInfo.objects.filter(bread__gt=20).filter(id__lt=3)
  1. 使用 Q( ) 对象结合|运算符实现 逻辑或(or) 的查询
    Q对象在 django.db.models 中,
    语法如下:
    Q(属性名__运算符=值)
    Q对象 可以使用 &、|连接,&表示逻辑与,|表示逻辑或
    Q对象前可以使用~操作符,表示非not
# 导入
from django.db.models import Q

# 获取数据
#例:查询阅读量大于 20的图书,改写为 Q对象.
BookInfo.objects.filter(Q(bread__gt=20))

#Q对象 可以使用 &、|连接,&表示逻辑与,|表示逻辑或.
#例:查询阅读量大于20,或编号小于3的图书,只能使用Q对象实现
BookInfo.objects.filter(Q(bread__gt=20) | Q(pk__lt=3))

#Q对象 前可以使用~操作符,表示非not.
#例:查询编号不等于3的图书。
BookInfo.objects.filter(~Q(pk=3))

聚合函数和排序

聚合函数在 django.db.models 中,使用前需导入。
聚合函数包括:Avg 平均,Max 最大,Min 最小,Sum 求和(需要首字母大写,结合aggregate( ) 过滤器使用)。注意 aggregate 的返回值是一个字典类型。
count 数量,可直接用,首字母小写。

#例:查询图书的总阅读量.
from django.db.models import Sum
BookInfo.objects.aggregate(Sum('bread'))
#注意 aggregate 的返回值是一个字典类型:{'bread__sum':3}

#例:查询图书总数。
BookInfo.objects.count()
#注意 count 函数的返回值是一个数字

排序,使用 order_by 对结果进行排序。

BookInfo.objects.all().order_by('bread')  # 升序
BookInfo.objects.all().order_by('-bread')  # 降序

关联查询

  1. 由一到多的访问
    使用格式: 一对应的模型类对象.多对应的模型类名小写_set
book = BookInfo.objects.get(id=1)
book.heroinfo_set.all()
  1. 由多到一的访问
    使用格式:多对应的模型类对象.外键名
hero = HeroInfo.objects.get(id=1)
hero.hbook
  1. 访问一对一的模型类关联对象的 id
    使用格式:多对应的模型类对象.关联类属性_id
hero = HeroInfo.objects.get(id=1)
hero.hbook_id
  1. 关联过滤查询

(1)由多模型类条件查询一模型类数据
格式:关联模型类名小写__属性名__条件运算符=值
注意:如果没有"__运算符"部分,表示等于。

#查询图书,要求图书英雄为"孙悟空"
BookInfo.objects.filter(heroinfo__hname='孙悟空')
#查询图书,要求图书中英雄的描述包含"八"
BookInfo.objects.filter(heroinfo__hcomment__contains='八')

(2)由一模型类条件查询多模型类数据:
格式:一模型类关联属性名__一模型类属性名__条件运算符=值

#查询书名为“天龙八部”的所有英雄。
HeroInfo.objects.filter(hbook__btitle='天龙八部')
#查询图书阅读量大于30的所有英雄
HeroInfo.objects.filter(hbook__bread__gt=30)

查询集

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

BookInfo.objects.filter(bread__gt=30).order_by('bpub_date')

惰性执行
创建查询集不会访问数据库,
直到调用数据时,才会访问数据库。
作用:减少对数据库的访问

qs = BookInfo.objects.all()
#执行遍历迭代操作后,才真正的进行了数据库的查询
for book in qs:
    print(book.btitle)

缓存
再次使用查询集时会使用缓存的数据,
减少了数据库的查询次数。

#情况一:两个相同的查询集,无法重用缓存,每次查询都会与数据库进行一次交互,增加了数据库的负载。
from booktest.models import BookInfo
[book.id for book in BookInfo.objects.all()]
[book.id for book in BookInfo.objects.all()]

#情况二:经过存储后,可以重用查询集,第二次使用缓存中的数据。
qs=BookInfo.objects.all()
[book.id for book in qs]
[book.id for book in qs]

限制查询集
注意:不支持负数索引。

#获取第1、2项,运行查看。
qs = BookInfo.objects.all()[0:2]

其他补充说明

添加测试数据

部分数据

insert into tb_book(btitle,bpub_date,bread,bcomment,is_delete) values
('射雕英雄传','1980-5-1',12,34,0),
('天龙八部','1986-7-24',36,40,0);
insert into tb_hero(hname,hgender,hbook_id,hcomment,is_delete) values
('郭靖',1,1,'降龙十八掌',0),
('黄蓉',0,1,'打狗棍法',0),
('黄药师',1,1,'弹指神通',0);

字段类型(略)

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

选项(略)

选项 说明
null 如果为True,表示允许为空,默认值是False
blank 如果为True,则该字段允许为空白,默认值是False
db_column 字段的名称,如果未指定,则使用属性的名称
db_index 若值为True, 则在表中会为此字段创建索引,默认值是False
default 默认
primary_key 若为True,则该字段会成为模型的主键字段,默认值是False,一般作为AutoField的选项使用
unique 如果为True, 这个字段在表中必须有唯一值,默认值是False
related_name 在关联查询中,代替单一对象查找多对象 对象名小写_set(book.heroinfo_set.all() 的写法
auto_now_add 只在数据添加的时候,记录时间
auto_now 数据添加和更新的时候,记录时间

外键

类型 作用
CASCADE 级联 删除主表数据时连通一起删除外键表中数据
PROTECT 保护 通过抛出ProtectedError异常,来阻止删除主表中被外键应用的数据。
SET_NULL 设置为NULL,仅在该字段null=True允许为null时可用。
SET_DEFAULT 设置为默认值,仅在该字段设置了默认值时可用。
SET() 设置为特定值或者调用特定方法。

你可能感兴趣的:(后端)