- 数据库配置
- Django模型类定义
- 字段类型与选项
- 模型迁移
- Django交互环境
- 增
- 删
- 改
- 基本查询
- 过滤查询
- F对象
- Q对象
- 排序
- 聚合
- 关联查询
- 查询集QuerySet
- 自定义模型管理器
数据库配置
ORM Object relational mapping 对象关系映射,把类和数据库表对应,把对象和表记录对应,通过类和对象操作数据库表中的数据,而不需要编写SQL语句
1、使用MySQL数据库首先需要安装驱动程序
pip install PyMySQL
2、在Django的工程同名子目录的__init__.py中添加如下语句,作用是让Django的ORM能以mysqldb的方式来调用PyMySQL
from pymysql import install_as_MySQLdb install_as_MySQLdb()
3、编辑settings.py,修改DATABASE配置信息
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'HOST': '127.0.0.1',#主机 'PORT': 3306, #端口 'USER': 'root', #数据库用户名 'PASSWORD': 'root', #数据库用户密码 'NAME': 'demo' #数据名字 } }
4、在MySQL中创建数据库
create database demo default charset=utf8;
Django模型类的定义
- 模型类被定义到‘应用/models.py文件中’
- 模型类必须继承Model类,位于包django.db.models中
创建应用book,在models.py文件中定义模型
from django.db import models class Book(models.Model): title = models.CharField(max_length=20, verbose_name='名称') pub_date = models.DateField(verbose_name='发布日期') read_num = models.IntegerField(default=0, verbose_name='阅读量') comment_num = models.IntegerField(default=0, verbose_name='评论量') is_delete = models.BooleanField(default=False, verbose_name='逻辑删除') class Meta: db_table = 'book' #指明数据库表名 verbose_name = '图书' #在admin站点中显示的名称 verbose_name_plural = verbose_name #显示复数的名称 def __str__(self): """定义每个数据对象的显示信息""" return self.title class Hero(models.Model): GENDER_CHOICES = ( (0, 'male'), (1, 'female') ) name = models.CharField(max_length=20, verbose_name='名称') gender = models.SmallIntegerField(choices=GENDER_CHOICES, default=0, verbose_name='性别') comment = models.CharField(max_length=200, null=True, verbose_name='描述信息') book = models.ForeignKey(Book, on_delete=models.CASCADE, verbose_name='图书') #CASCADE级联,删除主表数据时连同一起删除外键表中的数据 is_delete = models.BooleanField(default=False, verbose_name='逻辑删除') class Meta: db_table = 'hero' verbose_name = '英雄' verbose_name_plural = verbose_name def __str__(self): return self.name
关于主键
- 主键:primary key,简写 pk
- 不需要主动定义,django会自动生成自增长的主键,属性名叫
id
, - 如果开发者自己定义了主键,则django不会再生成默认的主键
字段类型与选项
类型 | 说明 |
---|---|
AutoField | 自动增长的IntegerField,通常不用指定,不指定时Django会自动创建名为id的自动增长属性 |
BooleanField | 布尔字段,值为True或False |
NullBooleanField | 支持Null、True、False三种值 |
CharField | 字符串,必须指定:max_length ,表示最大字符个数 |
TextField | 大文本字段,一般超过4000个字符时使用 |
IntegerField | 整数 |
DecimalField | 十进制浮点数,用python中的Decimal实例来表示 必须指定: max_digits 总位数,decimal_places 小数位数。 |
FloatField | 浮点数 |
DateField | 日期 1) 参数 auto_now 表示每次修改保存对象时,自动设置该字段为当前时间,用于保存"最后一次修改"时间,默认为False; 2) 参数 auto_now_add 表示当对象第一次被创建时自动设置保存当前时间,用于保存"创建时间"时间,默认为值为False; 3) 参数 auto_now_add 和auto_now 是相互排斥的,不能同时用到一个属性中 |
TimeField | 时间,参数同DateField |
DateTimeField | 日期时间,参数同DateField |
FileField | 上传文件字段 |
ImageField | 继承于FileField,对上传的内容进行校验,确保是有效的图片 |
选项 | 默认值 | 描述 | 是否要迁移修改表结构 |
---|---|---|---|
null | False | True表示表字段允许为空 | 是 |
unique | False | True表示表字段不能重复 | 是 |
db_column | 属性名称 | 表字段名称 | 是 |
primary_key | False | True表示字段设置为了主键,一般作为AutoField的选项使用 | 是 |
default | - | 默认值 | 否 |
blank | False | 在django管理后台新增或编辑一条表数据时,该字段是否允许为空; null是数据库范畴的概念,blank是表单验证范畴的 |
否 |
choices | - | 在django管理后台新增或编辑一条表数据时,该字段显示为下拉框,默认为编辑框 | 否 |
- choices: 性别属性使用了choices选项后,在录入一条数据时,会以下拉框显示
-
blank:blank属性默认值为false, 表示录入一条数据时,当前字段必须填写,不能为空,否则js端js校验不通过,例如:下图的comment员工备注信息字段。
外键
ForeignKey
: 一对多,将关联属性
定义在多的一端中ManyToManyField
: 多对多,将关联属性
定义任意一方中OneToOneField
: 一对一,将关联属性
定义在任意一方中
模型迁移
定义完模型类以后,我们需要把模型生成到数据库里面去
python manage.py mikemigrations
python manage.py migrate
Django交互环境
在Django交互环境中,可以直接执行django项目代码,类似ipython交换环境
通过shell命令进入Django交换环境
insert into book(name,pub_date,read_num,comment_num,is_delete) values ('射雕英雄传','1980-5-1',12,34,0), ('天龙八部','1986-7-24',36,40,0), ('笑傲江湖','1995-12-24',28,18,0), ('雪山飞狐','1987-11-11',58,24,0); insert into hero(name,gender,book_id,comment,is_delete) values ('郭靖',1,1,'降龙十八掌',0), ('黄蓉',0,1,'打狗棍法',0), ('黄药师',1,1,'弹指神通',0), ('欧阳锋',1,1,'蛤蟆功',0), ('梅超风',0,1,'九阴白骨爪',0), ('乔峰',1,2,'降龙十八掌',0), ('段誉',1,2,'六脉神剑',0), ('虚竹',1,2,'天山六阳掌',0), ('王语嫣',0,2,'神仙姐姐',0), ('令狐冲',1,3,'独孤九剑',0), ('任盈盈',0,3,'弹琴',0), ('岳不群',1,3,'华山剑法',0), ('东方不败',0,3,'葵花宝典',0), ('胡斐',1,4,'胡家刀法',0), ('苗若兰',0,4,'黄衣',0), ('程灵素',0,4,'医术',0), ('袁紫衣',0,4,'六合拳',0);
增
现在我们可以在Django交互环境测试数据库操作,首先我们来对数据库进行增加操作,有两种方式
1)模型类对象.save()
from book.models import Book, Hero from datetime import date book = Book( title='西游记', pu_date=date(1988,1,1), read_num=10, comment_num=10 ) book.save
2)通过模型类.objects.creare()保存
from book.models import Book, Hero b = Book.objects.get(title='西游记') Hero.objects.create( name='沙悟净', gender=0, book=book ) 或者 Hero.objects.create( name='沙悟净', gender=0, book_id=book.id )
删
删除记录有两种方式
1)模型类对象.delete()
book = Book.objects.get(id=1)
book.delete()
2)模型类.filter(条件).delete()
Book.objects.filter(id=1).delete()
注意事项: on_delete选项
- 默认值为
models.CASCADE
,当删除图书时,会删除相关联的英雄 - 如果不想删除关联数据,可设置
on_delete
为 PROTECT
改
修改记录有以下两种方式
1)模型类对象.save()
h = Hero.objects.get(name='沙悟净') h.name = '沙僧' h.save()
2)模型类.filter()条件.update(属性1=值1, 属性2=值2, ...)
Hero.objetcs.filter(name='猪八戒').update(name='猪悟能')
基本查询
get 查询单一结果,如果不存在或抛出模型类.DoesNotExist异常,通过存在多条记录会抛出MultipleObjectsReturned异常
#查询id为3的图书 In [7]: Book.objects.get(id=3) Out[7]: <Book: 笑傲江湖> #查询id为100的图书,该图书其实不存在 In [8]: Book.objects.get(id=100) --------------------------------------------------------------------------- DoesNotExist Traceback (most recent call last) ... #查询is_delete为false的图书,有多条 In [9]: Book.objects.get(is_delete=0) --------------------------------------------------------------------------- MultipleObjectsReturned Traceback (most recent call last)
all 查询多个结果
In [6]: Book.objects.all()
Out[6]: <QuerySet [<Book: 射雕英雄传>, <Book: 天龙八部>, <Book: 笑傲江湖>, <Book: 雪山飞狐>, <Book: 西游记>]>
count 查询结果数量
In [10]: Hero.objects.count()
Out[10]: 20
过滤查询
- 调用filter方法:模型类.objects.filter(模型属性名__条件名=值)
- 返回包含查询结果数据的QuerySet对象
1)判等: exact
In [11]: Book.objects.filter(id__exact=1) Out[11]: <QuerySet [<Book: 射雕英雄传>]> 可简写为 In [12]: Book.objects.filter(id=1) Out[12]: <QuerySet [<Book: 射雕英雄传>]>
2)模糊查询: contains / endswith / startswith
In [13]: Hero.objects.filter(name__contains='不') Out[13]: <QuerySet [<Hero: 岳不群>, <Hero: 东方不败>]>
In [14]: Hero.objects.filter(name__startswith='黄') Out[14]: <QuerySet [<Hero: 黄蓉>, <Hero: 黄药师>]>
- 上面查询都是区分大小写的,如果不想区分大小写,只需要加个i,如 iexact、icontains、iendstartswith、istartswith
- 如何想查询包含%的字符,%不需要转义
3)空查询: isnull
In [17]: Book.objects.filter(title__isnull=False)
Out[17]: <QuerySet [<Book: 射雕英雄传>, <Book: 天龙八部>, <Book: 笑傲江湖>, <Book: 雪山飞狐>, <Book: 西游记>]>
4)范围查询: in
In [20]: Book.objects.filter(id__in=[1,3,5])
Out[20]: <QuerySet [<Book: 射雕英雄传>, <Book: 笑傲江湖>, <Book: 西游记>]>
5)比较查询: gt、lt、gte、lte
In [21]: Book.objects.filter(read_num__gt=20)
Out[21]: <QuerySet [<Book: 天龙八部>, <Book: 雪山飞狐>]>
6)日期查询: year、month、day、week_day、hour、minute、second
In [22]: Book.objects.filter(pub_date__year=1980)
Out[22]: <QuerySet [<Book: 射雕英雄传>]>
In [23]: Book.objects.filter(pub_date__gt=date(1980,1,1))
Out[23]: <QuerySet [<Book: 射雕英雄传>, <Book: 西游记>]>
F对象
之前查询都是对象的属性与常量值比较,两个属性怎么比较?这就需要用到F对象
需要导包: from django.db.models import F
语法: F(属性名)
In [25]: from django.db.models import F In [26]: Book.objects.filter(read_num__gte=F('comment_num')) Out[26]: <QuerySet [<Book: 雪山飞狐>, <Book: 西游记>]>
可以在F对象上使用算数运算
In [27]: from django.db.models import F
In [28]: Book.objects.filter(read_num__gt=F('comment_num') * 2)
Out[28]: <QuerySet [<Book: 雪山飞狐>]>
Q对象
作用: 对查询条件进行与 或 非(& | ~)的逻辑操作
需要导包: from django.db.models import Q
与: Q(查询条件1) & Q(查询条件2)
In [29]: from django.db.models import Q In [30]: Hero.objects.filter(Q(id__gt=2) & Q(name__contains='黄')) Out[30]: <QuerySet [<Hero: 黄药师>]> 可以简写为 In [31]: Hero.objects.filter(id__gt=2, name__contains='黄') Out[31]: <QuerySet [<Hero: 黄药师>]>
或: Q(查询条件1) | Q(查询条件2)
In [34]: Hero.objects.filter(Q(id__lt=2) | Q(name__contains='不')) Out[34]: <QuerySet [<Hero: 郭靖>, <Hero: 岳不群>, <Hero: 东方不败>]>
非: ~Q(查询条件)
Hero.objects.filter(~Q(id=3))
排序
作用: 对查询结果进行排序,默认为升序
用法:
- 升序: 模型类.objects.order_by('属性名')
- 降序: 模型类.objects.order_by('-属性名')
In [37]: Book.objects.all().order_by('-id') Out[37]: <QuerySet [<Book: 西游记>, <Book: 雪山飞狐>, <Book: 笑傲江湖>, <Book: 天龙八部>, <Book: 射雕英雄传>]>
聚合
作用: 聚合操作,对多行查询结果中的一列进行操作,返回一个值
用法: 模型类.objects.aggregate(聚合类('属性名'))
- 常用聚合类有:Sum, Count, Max, Min, Avg等
- 返回值是一个字典, 格式: {'属性名__聚合函数': 值}
- 使用时需要先导入聚合类: from django.db.models import Sum, Count, Max, Min, Avg
In [38]: from django.db.models import Avg In [39]: Book.objects.aggregate(Avg('read_num')) Out[39]: {'read_num__avg': 25.2}
关联查询
1)、由 一类对象 查询 多类对象: 一类对象.多类名小写_set.all()
In [42]: b = Book.objects.get(title='西游记') In [43]: b.hero_set.all() Out[43]: <QuerySet [<Hero: 孙悟空>, <Hero: 猪悟能>, <Hero: 沙僧>]>
2)、由 多类对象 查询 一类对象: 多类对象.关联属性
In [44]: h = Hero.objects.get(name='郭靖') In [45]: h.book Out[45]: <Book: 射雕英雄传>
通过模型类实现上述两个案例
- 一类名.objects.filter(多类名小写__多类属性名__条件名=值)
- 多类名.objects.filter(关联属性__一类属性名__条件名=值)
In [48]: Hero.objects.filter(book__title='西游记') Out[48]: <QuerySet [<Hero: 孙悟空>, <Hero: 猪悟能>, <Hero: 沙僧>]> In [49]: Book.objects.filter(hero__name='郭靖') Out[49]: <QuerySet [<Book: 射雕英雄传>]>
查询集 QuerySet
查询集,也称查询结果集、QuerySet,表示从数据库中获取的对象集合,当调用一下过滤器方法时,Django会返回查询集(而不是简单的列表)
- all(): 返回所有数据
- filter(): 返回满足条件的数据
- exclude(): 返回满足条件之外的数据
- order_by(): 对结果进行排序
对查询集可以再次调用过滤器进行过滤
Book.objects.filter(read_num__gt=30).order_by('pub_date')
查询集两大特性
1)惰性执行
创建查询集不会访问数据库,直到调用数据时,才会访问数据库,调用数据的情况包括迭代、序列化、与if合用
例如,当执行如下语句时,并未进行数据库查询,只是创建了一个查询集qs
qs = Book.objects.all()
继续执行遍历迭代操作后,才真正的进行数据库查询
for book in qs: print(book.title)
2)缓存
- 第一次查询数据后,Django会将查询集缓存起来,并返回请求的结果
- 再次查询相同数据时将重用缓存的结果
from Book.models import Book [book.id for book in Book.objects.all()] [book.id for book in Book.objects.all()]
from Book.models import Book list=Book.objects.all() [book.id for objects in list] [book.id for objectsin list]
限制查询集
可以对查询集进行取下标或切片操作,等同于sql中的limit和offset子句(注意,不支持负数索引)
对查询集进行切片后返回一个新的查询集,不会立即执行查询
如果获取一个对象,直接使用[0],等同于[0:1].get(),但是如果没有数据,[0]引发IndexError异常,[0:1].get()引发DoesNotExist异常
qs = Book.objects.all()[0:2]
自定义模型管理器
可以自定义模型管理器,比如以下两种场景:
- 需要重写模型管理器中现有的方法
- 需求:调用Book.objects.all()时,返回的是is_delete等于False的图书
- 封装增删改查的方法
- 需求:在管理器类中,封装一个创建图书的方法,方便直接调用
注意:自定义后模型管理器后, Django 将不再自动生成默认的 objects
# 在book.models.py文件添加以下代码, 自定义模型管理器 class BookManager(Manager): def all(self): """重写all方法:只返回没有删除的部门""" return super().all().filter(is_delete=False) def create_book(self, title, pub_date): """封装新增图书的方法,方便调用""" book = Book() book.title = title book.pub_date = pub_date book.save() return book # 自定义模型管理器 class Book(models.Model): """部门类""" ... # 自定义模型管理器 objects = BookManager()