Django的日常-模型层(1)
模型层
模型层其实就是我们应用名下的models.py文件,我们在里面写入想要创建的表的表结构,以类的形式表示出来,然后通过django的ORM来实现表的创建,以及表的增删改查等.
django测试环境
不是正式开发项目的话,其实我们不必要一定按正式的流程来,又要建html,又要写urls,写views,因为我们在学习模型层的时候其实只需要通过ORM来对数据库进行操作,所以我们大可以搭建一个django的测试环境,以此来实验我们的一些命令,创建测试环境的步骤如下:
# 我们需要在应用名下的test.py里面写入如下语句来搭建测试环境
#test.py
import os
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "创建的项目名字.settings")
import django
django.setup()
#在下方写需要测试的对数据库操作的代码即可
另外有一个小点,就是我们在通过ORM对数据库操作的时候,如果我们想看到ORM转换过程中的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',
},
}
}
ORM查询
ORM的概念我们在前文已经介绍过了,下面我们来看ORM对于数据库的一些具体操作
在写ORM语句之前我们首先要有一个数据库,以及配置好当前django所用的数据库,步骤如下
# 首先,删除或者注释掉settings.py里面的DATABASES选项,然后手动加一个本地数据库的信息
# settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mysql1',
'USER': 'root',
'PASSWORD': "root",
'HOST': '127.0.0.1',
'PORT': 3306,
'CHARSET': 'utf8'
}
}
# 设置过这个配置项之后我们还需要在项目名文件夹下的__init__.py里面写入如下两行代码,以便于让django默认的数据库连接方式从MySQLdb转换成pymysql
import pymysql
pymysql.install_as_MySQLdb()
然后我们需要在models.py里面写自己想要创建的表结构
'''我们以书店为例,需要创建的表为,书籍表,作者表,作者详情表,出版社表,其中
书籍-出版社,一对多
作者-作者详情,一对一
书籍-作者,多对多
'''
# models.py
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8,decimal_places=2)
publish_date = models.DateField(auto_now_add=True)
"""
auto_now:每次修改数据的时候 都会自动更新时间
auto_now_add:在创建数据的时候 会自动将当前时间记录下来
后期如果你不人为修改的话 数据不变
"""
# 书籍与出版社 是一对多关系
publish = models.ForeignKey(to='Publish')
# 书籍与作者 是多对多
authors = models.ManyToManyField(to='Author')
"""
authors虚拟字段
1.告诉orm自动帮你创建第三张关系表
2.orm查询的时候 能够帮助你更加方便的查询
"""
class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=64)
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
# author_detail = models.ForeignKey(unique=True,to='AuthorDetail')
author_detail = models.OneToOneField(to='AuthorDetail')
class AuthorDetail(models.Model):
phone = models.BigIntegerField()
addr = models.CharField(max_length=64)
单表查询
单表查询,顾名思义就是在一张表内进行数据查询,下面的语句就是单表查询所常用的一些方法
# 对数据库的操作无外乎就是增删改查,下面逐一介绍,下面的语句都是写在test.py里面,就是我们前面搭建好的测试环境里面
# test.py
from 应用名 import models
# 增
# 方式一,直接调用create添加数据
models.Book.objects.create(title='jpm',price=123.23,publish_date='2019-10-24')
# 方式二,生成对象,以对象.save的方式保存数据
from datetime import date
ctime = date.today() # 时间格式是可以直接使用date的日期的
book_obj = models.Book(title='sgyy',price=666.66,publish_date=ctime)
book_obj.save()
# 改
# 方式一,以update的方式更新数据
models.Book.objects.filter(pk=1).update(price=999.66)
# 方式二,生成对象,直接修改,然后用.save保存
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.title = '不符合*核心价值观'
book_obj.save()
# 删除
# 筛选出符合条件的记录直接delete()即可
models.Book.objects.filter(pk=1).delete()
# 查
# 1.all() 查询所有
res = models.Book.objects.all()
# 惰性查询,惰性查询的概念在于,如果我们只是定义了这个语句的话,他并不会执行,只有在调用它,比如打印的时候,这个语句才会真正运行,查询的这么多方法里面大都是惰性查询
print(res)
for i in res:
print(i.title)
# 2.filter() 按照条件查询,会匹配到所有符合条件的记录,且filter可以无限叠加
res = models.Book.objects.filter(pk=2)
print(res)
# 3.get() 可以拿到数据对象本身,但是如果符合条件的记录不存在,会报错,所以不推荐使用
# 4.first() 拿第一个
res = models.Book.objects.all()
print(res)
print(res.first())
# 5. last() 拿最后一个
res = models.Book.objects.all()
print(res)
print(res.last())
# 6.exclude() 除此之外,即符合条件的记录不取,取剩余的全部记录
res = models.Book.objects.exclude(pk=3).filter(pk=4).filter(pk=1).filter(pk=4)
print(res)
# 7.values 返回数据的格式是列表套字典
res = models.Book.objects.values('title')
for r in res:
print(r.get('title'))
# 8.values_list 列表套元组
res = models.Book.objects.values_list('title')
print(res)
# 9.count() 统计数据的个数
res = models.Book.objects.count()
res1 = models.Book.objects.all().count()
print(res,res1)
# 10.distinct() 去重
"""去重:数据必须是一模一样的情况下才能去重,包括主键,尤其要注意"""
res = models.Book.objects.all().distinct()
res1 = models.Book.objects.values('title','price').distinct()
print(res1)
# 11.order_by() 以某个标准排序
res = models.Book.objects.order_by('price') # 默认是升序
res1 = models.Book.objects.order_by('-price') # 加负号就是降序
print(res)
print(res1)
# 12.reverse() 注意,前面必须是先经过排序后的数据才可以反转,否则反转会无效,数据顺序不会发生变化
res = models.Book.objects.order_by('price').reverse()
print(res)
# 13.exists() 排除符合条件的记录,不太常用
res = models.Book.objects.filter(pk=1).exists()
print(res)
# 查询还有一类比较好用的方法,即双下划线的方式
# 双下划线可以实现很多非常神奇的筛选方法,比如大于小于,或者是限定范围,还支持模糊匹配
# 1. __gt和__gte,gt是大于,gte是大于等于
res1 = models.Book.objects.filter(price__gt=300)
res2 = models.Book.objects.filter(price__gte=300)
# 上面的语句分别可以查询得到价格大于300和大于等于300的图书的对象
# 2. __lt和__lte,lt是小于,lte是小于等于
res1 = models.Book.objects.filter(price__lt=300)
res2 = models.Book.objects.filter(price__lte=300)
#上面语句可以查询得到价格小于300和小于等于300的所有图书的对象
# 3. __in和__range,in是否在其内,后面通常为容器类型,range,是否在一个范围内,后面通常跟元组表示范围
res = models.Book.objects.filter(price__in=[100,200,456,]) #筛选范围仅仅是列表内的这几个值
res = models.Book.objects.filter(price__range=(100,499)) #筛选价格在100到499范围内的书,这里是顾头不顾尾的,即价格为100的书在范围内,但是价格为499的书不在范围内.
# 我们知道sql语句里面的模糊匹配其实就只有like,再加上%或者_,%是任意位数的任意字符,而_是一位的任意字符
# 双下划线可以实现ORM里面的模糊匹配,常用的几个方法包括contains,icontains,startswith,endswith,year,month等等.
# 1. contains 按字符模糊匹配,区分大小写
# 2. icontains 按字符模糊匹配,不区分大小写
# 3. startswith 以某个字符开头
# 4. endswith 以某个字符结尾
# 5. year 用于判定日期格式数据的年份
# 6. month 用于判断日期格式数据的月份
多表查询
上面我们介绍了单表查询的一些方法和语句,下面就来看看多表查询常用的一些方法
我们依然从增删改查四个方面来分析多表间的操作:
# 多对多表关系之间修改字段的四个方法,add,set,remove以及clear
# 1. add()添加,括号内可以填多个值,会生成多条记录,其实修改的是多对多的那个关系表,add的括号里面可以传数字,也可以直接传一个对象,ORM会自动把该对象的主键的值取出来,传给add使用,比如
# 传数字
book_obj = models.Book.objects.filter(pk = 1).first()#这里取出一个书籍对象
book_obj.authors.add(1)# 书籍对象.作者,其实得到的就是两者的关系表
# 传对象
author_obj = models.Author.objects.filter(pk=1).first()
book_obj.authors.add(author_obj)
# 2. set() 修改,用法和add相似,也支持多个值,支持传数字和对象,不同的是括号里要是一个可迭代对象,也就是说括号内不能为一个单独的值,而应该是列表,元素,字典或者集合这种可迭代对象
# 传数字
book_obj = models.Book.objects.filter(pk = 3).first()
book_obj.authors.set([1,])
# 传对象
author_obj = models.Author.objects.filter(pk=1).first()
book_obj.authors.set((author_obj,))#这里是元组,注意,元组如果只有一个值,要加逗号,不然会报错
# 3. remove(),移除某条记录,可以传数字和对象,支持传多个值
# 传数字
book_obj = models.Book.objects.filter(pk = 3).first()
book_obj.authors.remove(2)
# 传对象
author_obj = models.Author.objects.filter(pk=1).first()
book_obj.authors.remove(author_obj)
# 4. clear(),清空,括号内不需要任何值,可以直接清空当前关系表
book_obj = models.Book.objects.filter(pk=2).first()
book_Obj.authors.clear() # 这里会拿到作者和书籍的关系表,然后把book表里主键值为2对应的所有数据清空
因为实际生产环境中其实还是多表用的比较多,毕竟很多数据都不是放在一起的,这时候我们就需要通过联表或者是通过子查询的方式来解决数据查询的问题,所以ORM提供给我们两个更好的多表查询的方法,其原理依然还是联表和子查询,不过其叫法不同
- 基于对象的跨表查询其实就是子查询
- 基于双下划线的跨表查询其实就是联表查询.
这里我们还要清楚一个正向查询和反向查询的概念,即我们从外键所在的表向外面的表查询的时候,叫做正向查询,反过来就叫做反向查询.
'''基于对象的跨表查询'''
#顾名思义,我们要通过筛选并生成出一个对象,然后从这个对象连到其他表,再查询取值,比如:
# 正向查询
# 单个值
book_obj = models.Book.objects.filter(title='python').first()
print(book_obj.publish.name)#这里用书籍对象直接.出版社,此时其实就已经到了publish表里面,然后直接.name就可以拿到相对应的值,这里因为书籍对应的出版社都是单个值,所以可以直接取值
# 多个值
book_obj = models.Book.objects.filter(pk = 1).first()
print(book_obj.authors) # 我们可以看到这里返回结果是 应用名.Author.None,这并不意味着我们的语句写错了,而是返回的结果不是单个值,我们取不到
print(book_obj,authors.all())# 所以如果查询到结果是多个值,要在后面加.all(),才能正常看到返回结果
# 反向查询
# 单个值
author_detail_obj = models.AuthorDetail.objects.filter(pk=1).first()
print(author_detail_obj.author)
print(author_detail_obj.author.name)
# 多个值
publish_obj = models.Publish.objects.filter(pk = 1).first()
print(publish_obj.book_set) # 这里结果是 应用名.Book.None,是不是很熟悉?对,要加.all()
print(publish_obj.book_set.all())
#总结下来就是,反向查询时,当查询结果是多个值的时候,需要加_set.all()在后面,而查询结果只有一个值的话不需要加这个
'''基于双下划线的跨表查询'''
# 上文中我们说过,基于双下划线的跨表查询其实就是联表查询,那么在原生sql里面联表方式有几种呢?四种,即左联left join,右联right join,内联inner join,全联 union.在当前的ORM里面我们不必关心这些联表方式,内部会帮我们做处理,我们需要做的只是理清楚查询逻辑,或者说其实我们只需要认清楚是正向查询还是反向查询就可以了.
# 和双下划线相匹配使用的方法就是.values()
res = models.Book.objects.filter(pk=1).values('publish__name')
print(res)
# 上面以书籍的主键来寻找到出版社的名字是正向查询,那么与之对应的反向查询如下
res = models.Publish.objects.filter(book__pk=1),values('name')
print(res)