model数据库模型介绍
Django默认支持的数据库
Django默认支持sqlite、mysql、oracle、postgresql数据库
- sqlite
- Django默认使用sqlite数据库,默认自带sqlite的数据库驱动,引擎名称:django.db.backends.sqlite3
- mysql
- 引擎名称:django.db.backends.mysql
在settings.py中配置数据库
配置使用其他数据库
如果有的时候不希望使用sqlite而想使用mysql或者其他数据库,这时候需要先在setting.py文件中进行相关配置,找到settings.py中的DATABASE字典,将内容更改为如下形式:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # 数据库引擎,上面有提到,根据数据库选择对应的引擎
'NAME': 'books', # 数据库名称
'USER': 'root', # 数据库用户名
'PASSWORD': '', # 数据库密码
'HOST': '', # 数据库主机,留空默认为localhost
'PORT': '3306', # 数据库端口
}
}
然后,启动项目,可能会报错:no module named MySQLdb
这是因为django默认导入的驱动是MySQLdb,可是MySQLdb对于py3有很大问题,所以需要的驱动是PyMySQL,它对python3的支持更好
所以,只需要找到项目名文件下的__init__,在里面写入:
import pymysql pymysql.install_as_MySQLdb()
即可
输出sql语句
model的相关方法其实都是在跟数据库打交道,因此可以将每一个方法都翻译成相对应的sql语句,那么怎样把这些sql语句打印到控制台让开发者看到呢?
只需要在settings.py文件中进行相关的配置即可,在settings.py的末尾添加上如下列表
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
添加完成后,就可以将sql语句打印到控制台了
在models.py中创建一个类对应一张表
说了这么多,终于把配置都搞定了,那么开始上主菜,要想操作表,首先要有表可以操作,但是并不需要手动建表,只需要在models.py文件中建立一个类就表示数据库中的一个表,利用这个类可以去操作数据库中相关的表,类中的每一个字段就表示表中的每一列,首先先建立一个Book类试试
class Book(models.Model):
name = models.CharField(max_length=20)
price = models.IntegerField()
pub_date = models.DateField()
向上面这样光是写了一个类可还不够,还需要在项目下执行两条命令才能在数据库中建立相应的表,这两条命令便是:
python manage.py makemigrations python manage.py migrate
执行过后就会在数据库中生成一张名为 应用名_类名小写 的表,例如假设应用的名字是app,那么此时建立的表名就是 app_book
单表查询之增删改查
配置弄完了,表也生成了,那么就下来就是操作表的时间了,当然,在Django中操作表不再需要写sql语句了(如果sql写的特别6的可以自己写,甚至连整个Django的ORM都不用学),而对应的是一个个方法,下面就来一一介绍
增
向数据库表中添加一条数据的方式有两种
方式一
b = Book(name="python基础", price=90, pub_date="2017-12-12") # 实例化一个Book对象 b.save() # 将数据添加到数据库
方式二
Book.objects.create(name="python基础", price=90, pub_date="2017-12-12")
注意:有的时候获得了用户的输入内容,可以将内容封装成一个字典,一起存进数据库
dic = {"name": "python基础", "price": 90, "pub_date": "2017-12-12"}
Book.objects.create(**dic) # 必须保证字典的键的名字跟类的字段名相同才能一一对应
删
自古删的操作就很简单,在Django的ORM中也是,找到删除之就可以了
# 先找到要删除的对象,然后执行delete()方法删除 Book.objects.filter(name="python基础").delete()
改
改的方式也分为两种,可以直接使用update()方法,也获取要修改的对象,在将对象修改,然后执行save()方法保存即可
方式一
# 先查到收条数据,然后修改 Book.objects.filter(name="python基础").update(price=999)
方式二
# 找到要修改的对象 b = Book.objects.get(name="python基础") b.price = 100 # 修改数据 b.save() # 将数据保存到数据库
注意:使用update修改数据时,只会更新修改的内容,但是使用save()方法修改数据,会把所有的数据都更新一遍,因此效率很低,所以用update()更好
查
自古查询就很复杂,方式杂多,可惜在Django的ORM中也不例外
查询的API
| 方法 | 说明 | 演示 | 演示说明 |
| all | 查询所有的记录 | Book.objects.all() | 可以查出书籍表中的每一列数据 |
| first | 拿到第一条记录 | Book.objects.first() | 拿到书籍表中的第一条记录 |
| last | 拿到最后一条记录 | Book.objects.last() | 拿到书籍表中的最后一条记录 |
| get |
拿到符合条件的数据,只有一条数据,而且只有结果是一条数据时才没问题, 如果查询结果是多条数据或者是0的话就会报错 |
Book.objects.get(**kwargs) |
这里拿到的是一个book对象,查询的内容也可以是某个单一的字段, 例如:Book.objects.get(id=3) |
| values | 获取查询对象中的某个或某几个字段(以字典的形式展示) | Book.objects.filter(name="python基础").values("price", "pub_date") | 拿到的是所有名字为python基础的书籍的价格和出版日期 |
| values_list | 获取查询对象中的某个或某几个字段(以列表的形式展示) | Book.objects.filter(name="python基础").values_list("price", "pub_date") | 与上面相同,只是将数据都存到了列表中,每一列的数据单独以一个元组储存 |
| count | 计数 | Book.objects.all().count() | 计算查到的QuerySet集合的长度 |
| filter | 按照条件进行查找 | Book.objects.filter(name="python基础") | 与get()方法不同,这个可以查询多条记录 |
| exclude | 查询出条件以外的数据 | Book.objects.exclude(name="python基础") | 查询出名字不为 python基础的书籍 |
| defer | 不取某几列数据 |
models.UserInfo.objects.filter(...).defer('username','id')
|
不取username跟id列 |
| only | 只取某几列数据 |
models.UserInfo.objects.only('username','id')
|
只取username跟id列,相当于SELECT username, id FROM UserInfo |
| dates | 根据时间进行某一部分的去重查找并截取指定内容 |
models.DatePlus.objects.dates('ctime','day','DESC')
|
参数说明: field_name:要筛选的字段名 kind:只能是"year" "month" "day" order只能是"ASC" "DESC" 并获取转换后的时间 year:年-01-01 month:年-月-01 day:年-月-日 |
| datetimes | 根据时间进行某一部分的去重查找并截取指定内容,将时间转换成指定时区的时间 |
models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC)
models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai'))
pip3 install pytz import pytz pytz.all_timezones pytz.timezone(‘Asia/Shanghai’) |
参数说明: field_name:要筛选的字段名 kind:只能是"year" "month" "day" "hour" "minute" "second" order只能是"ASC" "DESC" tzinfo:时区对象 |
| none | 空QuerySet对象 | ||
| get_or_create | 如果存在则获取,否则创建 |
obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2})
|
如果存在username等于root1则取出,否则创建root1,并且其他字段是defaults里面的内容 |
| in_bulk | 根据主键ID进行查找 |
id_list = [11,21,31] models.DDD.objects.in_bulk(id_list) |
万能的双下划线 __
在filter()、get()、exclude()、values()和values_list()等方法中,除了可以将字段或者字典作为查询条件以外,还可以通过双下划线去筛选出符合某一范围的条件
| 方法 | 说明 | 演示 | 演示说明 |
| gt | 大于 | Book.objects.filter(price__gt = 50) | 筛选出价格大于50的书籍 |
| lt | 小于 | Book.objects.filter(price__lt = 50) | 筛选出价格小于50的书籍 |
| contains | 名字中包含指定内容的字段 | Book.objects.filter(name__contains="p") | 筛选出名字中带有p的书籍 |
| icontains | 名字中包含指定内容的字段(忽略大小写) | Book.objects.filter(name__icontains="a") | 筛选出名字中带有a或A的书籍 |
| in | 筛选出符合指定集合的数据 | Book.objects.filter(price__in=[11, 22, 33]) | 筛选出价格是等于11或22或33的数据 |
| range | 筛选出某个范围内的数据 | Book.objects.filter(price__range(0, 20)) | 筛选出价格在0到20之间的书籍 也就是数据库中的between and |
| startswith | 以某某开头 | ||
| istartswith | 以某某开头(忽略大小写) | ||
| endswith | 以某某结尾 | ||
| iendswith | 以某某结尾(忽略大小写) | ||
| regex | 正则匹配 |
Entry.objects.get(title__regex=r'^(An?|The) +') |
筛选出title中匹配了r'^(An?|The) +'的 |
| iregex | 忽略大小写 |
Entry.objects.get(title__iregex=r'^(an?|the) +') |
|
| date | 匹配日期 |
Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1)) |
|
| year | 匹配年份 |
Entry.objects.filter(pub_date__year=2005) Entry.objects.filter(pub_date__year__gte=2005) |
|
| month | 匹配月份 |
Entry.objects.filter(pub_date__month=12) |
|
| day | 匹配天 |
Entry.objects.filter(pub_date__day=3) |
|
| week_day | 匹配周 |
Entry.objects.filter(pub_date__week_day=2) |
|
| hour | 匹配小时 |
Event.objects.filter(timestamp__hour=23) |
|
| minute | 匹配分钟 |
Event.objects.filter(timestamp__minute=29) |
|
| second | 匹配秒钟 |
Event.objects.filter(timestamp__second=31) |
多表查询之一对多
一对多就是两张表通过外键关联起来的,那么首先需要有两张表
建表
class Book(models.Model):
name = models.CharField(max_length=20)
price = models.IntegerField()
pub_date = models.DateField()
# 这里的Publish用引号引起来了,也可以不用引号引起来,但是必须把Publish类放到Book类的上面,这样才能找到
# 而用引号将Publish引起来后则不需要关心顺序,因为是在加载完后再去找的Publish,所以无所谓先后顺序
publish = models.ForeignKey("Pubish") # 创建Publish的外键,自动关联主键
def __str__(self):
return self.name
class Publisher(models.Model):
name = models.CharField(max_length=32)
city = models.CharField(max_length=32)
之后执行 python manage.py makemigrations 跟 python manage.py migrate 即可
一对多添加关联
将一张表中的某一列与另一张表中的某一列关联有两种方式,第一种是直接为外键指定要关联的列的id,第二种是把要关联的对象赋值给类中的外键字段
方式一
# 对数据库中的publish_id进行赋值 Book.objects.create(name="linux", price=49, pub_date="2017-12-12", publish_id=2)
方式二
# 对类中的publish进行赋值 publish_obj = Publish.objects.filter(name="人民出版社")[0] Book.objects.create(name="linux", price=49, pub_date="2017-12-12", publish=publish_obj)
一对多查询(通过对象查询)
通过多的一方查出一的一方(正向查询)
b = Book.objects.get(name="linux") b.publish # 拿到的是与该对象对应的一的一方的对象,一定是一个对象
通过一的一方找多的一方(反向查询)
反向查询的方式有两种,一种是通过外键字段查找,两一种是通过 类名小写_set的方式查找
方式一
pub_obj = Publish.objects.filter(name='人民出版社')[0] book_obj = Book.objects.filter(publish=pub_obj) # 拿到的是所有与pub_obj关联的book对象
方式二
pub_obj = Publish.objects.filter(name='人民出版社')[0]
pub_obj.book_set.all() # 获取与该对象关联的Book对象
pub_obj.book_set.all().values('name', 'price')
注意:这里默认使用的是表名小写加set,即book_set,但是也可以自己定义,就是在models.py中在外键关联的字段中加上related_name字段即可
class Book(models.Model):
name = models.CharField(max_length=20)
price = models.IntegerField()
pub_date = models.DateField()
publish = models.ForeignKey("Pubish", related_name="bbbb") # 下次反向查询的时候就可以使用bbbb进行查询了
进行反向查询
pub_obj = Publish.objects.get(name='人民出版社') pub_obj.bbbb.all() # 获取所有该出版社出版的书籍
一对多查询(通过双下划线 __ 查询)
在经历了上面的对象查询后发现,用对象查询真的太麻烦了,那么能不能有一条语句就解决问题的方法呢,自然是有的,那就是通过万能的双下划线查询,而且对于 filter()跟values()都可以使用
通过多的一方查询一的一方(正向查找)
方式一
Publish.objects.filter(book__name="python基础").values("name") # 这里的book是book对象
方式二(通过values查找)
Book.objects.filter(name="python基础").values("publish__name") # 这里的publish是外键
通过一的一方查多的一方(反向查找)
Book.objects.filter(publish__name="人民出版社").values("name") # 这里的publish是外键
多表查询之多对多
多对多查询是通过建立第三张表然后通过两个外键关联其他两张表,我们可以建立三个类然后自己设定外键关系,也可以创建两个类,通过ManyToMany让Django为我们建立第三张表
建表
class Book(models.Model):
name = models.CharField(max_length=20)
price = models.IntegerField()
pub_date = models.DateField()
author = models.ManyToManyField("Author") # 与Author建立多对多关系
def __str__(self):
return self.name
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField(default=20)
之后执行 python manage.py makemigrations 跟 python manage.py migrate 即可
添加关联信息
book_obj = Book.objects.get(id=4) author_obj = Author.objects.all() author_obj2 = Author.objects.get(id=1) # 给一本书籍添加多个作者 book_obj.author.add(*author_obj) # 将所有的作者对该书籍关联 # 给一本书籍添加一个作者 book_obj.author.add(author_obj2)
解除关联关系
book_obj = Book.objects.get(id=4) author_obj = Author.objects.all() author_obj2 = Author.objects.get(id=1) # 解除多个作者与书籍的绑定关系 book_obj.author.remove(*author_obj) # 解除一个作者与书籍的绑定关系 book_obj.author.remove(author_obj2) # 通过作者的id解除关系 book_obj.author.remove(1) # 解除id为1的作者与书籍的关系
多对多查询(通过对象查询)
通过有ManyToMany字段的一方查询没有的(正向查找)
book_obj = Book.objects.get(id=3) book_obj.author.all()
通过没有ManyToMany字段查询有的(反向查找)
author_obj = Author.objects.get(id=2) author_obj.book_set.all() # 拿到所有与author_obj关联的书籍对象
注意:在ManyToMany字段中也有related_name字段,设置该字段后再进行反向查找也可以通过设置的名字进行查找
多对多查询(通过filter,values配合双下划线查询)
通过有ManyToMany字段的一方查询没有的(正向查找)
Book.objects.filter(author__name="alex").values("name")
Author.objects.filter(name="alex").values("book__name")
通过没有ManyToMany字段查询有的(反向查找)
Author.objects.filter(book__name="python基础")
聚合函数跟分组查询
聚合函数:aggregate()
引入文件
from django.db.models import Avg, Min, Sum, Max, Count
使用
# 求平均值
ret = Book.objects.all().aggregate(Avg("price"))
# 求总和
ret = Book.objects.filter(author__name='alex').aggregate(Sum("price"))
分组:annotate()
# 求每个作者出的书的总价格
ret = Book.objects.values("author__name").annotate(Sum("price"))
# 拿到每个出版社出的价格最低的书
ret = Publish.objects.values("name").annotate(Min("book__price"))
F查询跟Q查询
F查询:拿到查询的结果
# 将所有的记录的某列都加10
Book.objects.all().update(price=F("price")+10) # 将每本书的价格都加10
Q查询:组合多个查询条件
# 或 Book.objects.filter(Q(name="python基础")|Q(price__gt=40)) # 拿到python基础或者价格大于40的书籍 # 非 Book.objects.filter(~Q(name="python基础")) # 拿到除了python基础之外的书籍 # Q查询联合普通关键字查询,Q查询一定要在前面 Book.objects.filter(Q(name="Go"), price=40)
除了像上面那样直接在filter中写Q查询语句以外,还可以事先准备好查询对象,给查询对象添加查询条件后,将这个对象传到filter中即可
from django.db.models import Q
q = Q() # 创建一个Q对象
q.connector = 'OR' # 将多个条件之间设置为或,相当于在filter中直接写 |,默认取值是 'AND'
q.children.append(("contact__contains", "33")) # 设置查询条件,可以通过dir(q.children)查看所有的方法,使用q.children.pop()可以删除最后一个条件
q.children.append(("consultant__name__contains", "33")) # 设置另一个查询条件,与上面的天剑通过 ‘OR’ 相连
models.CustomerInfo.objects.filter(q) # 将条件置入filter中进行查询
QuerySet
QuerySet的特点
- 可迭代
- 可切片
QuerySet的高效使用
1. Django的queryset是惰性的
在执行 Book.objects.filter(price=40) 该条语句的时候,并不会查询数据库,只是把该条代码翻译成了sql,只有真正用到的时候才会去查询
2. queryset具有cache
何为cache? cache即缓存,在Django执行第一次查询的时候就已经把查询的数据保存到了queryset内置的cache中了,如果程序再次遍历这个queryset会直接到cache中拿,不会再走数据库
3. 不必要的时候不让数据存入cache
上面说过,在执行查询命令的时候并不会真正去查询,只是把查询的代码翻译成了sql语句,用到时再查询,那么即使在用if对这个queryset集合进行判断也会执行sql并且把结果放到cache中,如果仅仅只是想判断某个queryet中是否有数据而不想把数据放到cache中,可以使用exists()方法
obj = Book.objects.filter(price__lt=40)
if obj.exists(): # 这样不会把数据存到cache中,但当然会想向数据库发送sql
print("exists")
5. 当queryset非常巨大时,cache会成为问题
处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的queryset可能会锁住系统进程,让你的程序濒临崩溃。要避免在遍历数据的同时产生queryset cache,可以使用iterator()方法来获取数据,处理完数据就将其丢弃。
obj = Book.objects.filter(name__contain = 'p')
obj = obj.iterator() # obj现在是一个迭代对象
# iterator()可以一次只从数据库中去取量的数据,这样恶意节省内存
for i in obj:
print(i.name)
# 但是再次遍历就没有数据了,因为迭代器已经在上一个for循环中遍历完了,再次遍历已经没有了
for i in obj:
print(i.name)
总结:在使用itrator()防止生成cache时,会有两个问题
- 第一个就是由于迭代器的性质,只能遍历一次,所以不能重复使用
- 第二个就是会造成额外的数据库查询
因此什么时候用iterator()应该根据情况而定,真正到了查询上万级别记录的时候,还是用iterator(),别把内存玩儿坏了
更多Models介绍:见本文

浙公网安备 33010602011771号