前言
Django框架功能齐全自带数据库操作功能,本文主要介绍Django的ORM框架
到目前为止,当我们的程序涉及到数据库相关操作时,我们一般都会这么搞:
创建数据库,设计表结构和字段
使用 MySQLdb 来连接数据库,并编写数据访问层代码
业务逻辑层去调用数据访问层执行数据库操作
ORM是什么?:(在django中,根据代码中的类自动生成数据库的表也叫--code first)
ORM:Object Relational Mapping(关系对象映射)
类名对应------》数据库中的表名
类属性对应---------》数据库里的字段
类实例对应---------》数据库表里的一行数据
obj.id obj.name.....类实例对象的属性
Django orm的优势:
Django的orm操作本质上会根据对接的数据库引擎,翻译成对应的sql语句;所有使用Django开发的项目无需关心程序底层使用的是MySQL、Oracle、sqlite....,如果数据库迁移,只需要更换Django的数据库引擎即可;
一、Django连接MySQL
1、创建数据库 (注意设置 数据的字符编码)
由于Django自带的orm是data_first类型的ORM,使用前必须先创建数据库
create database day70 default character set utf8 collate utf8_general_ci;
2、修改project中的settings.py文件中设置 连接 MySQL数据库(Django默认使用的是sqllite数据库)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME':'day70',
'USER': 'eric',
'PASSWORD': '123123',
'HOST': '192.168.182.128',
'PORT': '3306',
}
}
扩展:查看orm操作执行的原生SQL语句
在project中的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',
},
}
}
3、修改project 中的__init__py 文件设置 Django默认连接MySQL的方式
import pymysql
pymysql.install_as_MySQLdb()
4、setings文件注册APP
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config',
]
5、models.py创建表
6、进行数据迁移
6.1、在winds cmd或者Linux shell的项目的manage.py目录下执行
python manage.py makemigrations #根据app下的migrations目录中的记录,检测当前model层代码是否发生变化?
python manage.py migrate #把orm代码转换成sql语句去数据库执行
python manage.py migrate --fake #只记录变化,不提交数据库操作
扩展:修改表结构之后常见报错
这个报错:因为表创建之时,新增字段既没有设置默认值,也没有设置新增字段可为空,去对应原有数据导致;
2中解决方法:
1.设置新增字段可以为空
startdate = models.CharField(max_length=255, verbose_name="任务开始时间",null=True, blank=True)
Handledate = models.CharField(max_length=255, verbose_name="开始处理时间",null=True, blank=True)
Handledone = models.CharField(max_length=255, verbose_name="处理完毕时间", null=True, blank=True)
enddate = models.CharField(max_length=255, verbose_name="任务结束时间",null=True, blank=True)
WorkTime_cost=models.CharField(max_length=255,verbose_name='工作耗时',null=True, blank=True)
2.设置新增字段默认值为 当前时间
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
>>> timezone.now()
7.设置pycharm可视化MySQL
二、modles.py创建表
ORM字段介绍
Djan提供了很多字段类型,比如URL/Email/IP/ 但是mysql数据没有这些类型,这类型存储到数据库上本质是字符串数据类型,其主要目的是为了封装底层SQL语句;
1、字符串类(以下都是在数据库中本质都是字符串数据类型,此类字段只是在Django自带的admin中生效)
name=models.CharField(max_length=32)
EmailField(CharField):
IPAddressField(Field)
URLField(CharField)
SlugField(CharField)
UUIDField(Field)
FilePathField(Field)
FileField(Field)
ImageField(FileField)
CommaSeparatedIntegerField(CharField)
扩展
models.CharField 对应的是MySQL的varchar数据类型
char 和 varchar的区别 :
char和varchar的共同点是存储数据的长度,不能 超过max_length限制,
不同点是varchar根据数据实际长度存储,char按指定max_length()存储数据;所有前者更节省硬盘空间;
2、时间字段
models.DateTimeField(null=True)
date=models.DateField()
3、数字字段
(max_digits=30,decimal_places=10)总长度30小数位 10位)
数字:
num = models.IntegerField()
num = models.FloatField() 浮点
price=models.DecimalField(max_digits=8,decimal_places=3) 精确浮点
4、枚举字段
choice=(
(1,'男人'),
(2,'女人'),
(3,'其他')
)
lover=models.IntegerField(choices=choice) #枚举类型
扩展
在数据库存储枚举类型,比外键有什么优势?
1、无需连表查询性能低,省硬盘空间(选项不固定时用外键)
2、在modle文件里不能动态增加(选项一成不变用Django的choice)
其他字段
db_index = True 表示设置索引
unique(唯一的意思) =True 设置唯一索引
联合唯一索引
class Meta:
unique_together = (
('email','ctime'),
)
联合索引(不做限制)
index_together = (
('email','ctime'),
)
ManyToManyField(RelatedField) #多对多操作
字段参数介绍
1.数据库级别生效
View Code
2、Django admin级别生效
针对 dango_admin生效的参数(正则匹配)(使用Django admin就需要关心以下参数!!))
View Code
三、ORM单表操作
0、orm操作前戏
orm使用方式:
orm操作可以使用类实例化,obj.save的方式,也可以使用create()的形式
QuerySet数据类型介绍
QuerySet与惰性机制
所谓惰性机制:Publisher.objects.all()或者.filter()等都只是返回了一个QuerySet(查询结果集对象),它并不会马上执行sql,而是当调用QuerySet的时候才执行。
QuerySet特点:
<1> 可迭代的
<2> 可切片
<3>惰性计算和缓存机制
View Code
增加和查询操作
增
View Code
根据条件判断,增加?更新?
View Code
删
View Code
改
View Code
查
View Code
连表查询
View Code
1、基本操作
View Code
2、进阶操作(了不起的双下划线)
利用双下划线将字段和对应的操作连接起来
View Code
3、其他操作(执行原生SQL)
#执行原生SQL
1.执行自定义SQL
#from django.db import connection, connections
#cursor = connection.cursor() # cursor = connections['default'].cursor()
#cursor.execute("""SELECT * from auth_user where id = %s""", [1])
#row = cursor.fetchone()
2.使用extra方法
# #extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
#Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
#Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
#Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
#Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])
3.使用raw方法
解释:执行原始sql并返回模型
说明:依赖model多用于查询
用法:
book = Book.objects.raw("select * from hello_book")
for item in book:
print(item.title)
https://www.cnblogs.com/413xiaol/p/6504856.html
#F
# #from django.db.models import F
#models.Tb1.objects.update(num=F('num')+1)
#Q
# #方式一:
#Q(nid__gt=10)
#Q(nid=8) | Q(nid__gt=10)
#Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
#方式二:
#con = Q()
#q1 = Q()
#q1.connector = 'OR'
#q1.children.append(('id', 1))
#q1.children.append(('id', 10))
#q1.children.append(('id', 9))
#q2 = Q()
#q2.connector = 'OR'
#q2.children.append(('c1', 1))
#q2.children.append(('c1', 10))
#q2.children.append(('c1', 9))
#con.add(q1, 'AND')
#con.add(q2, 'AND')
# #models.Tb1.objects.filter(con)
四、ORM连表操作
我们在学习django中的orm的时候,我们可以把一对多,多对多,分为正向和反向查找两种方式。
正向查找:ForeignKey在 UserInfo表中,如果从UserInfo表开始向其他的表进行查询,这个就是正向操作,反之如果从UserType表去查询其他的表这个就是反向操作。
一对多:models.ForeignKey(其他表)
多对多:models.ManyToManyField(其他表)
一对一:models.OneToOneField(其他表)
正向连表操作总结:
所谓正、反向连表操作的认定无非是Foreign_Key字段在哪张表决定的,
Foreign_Key字段在哪张表就可以哪张表使用Foreign_Key字段连表,反之没有Foreign_Key字段就使用与其关联的 小写表名;
1对多:对象.外键.关联表字段,values(外键字段__关联表字段)
多对多:外键字段.all()
反向连表操作总结:
通过value、value_list、fifter 方式反向跨表:小写表名__关联表字段
通过对象的形式反向跨表:小写表面_set().all()
应用场景:
一对多:当一张表中创建一行数据时,有一个单选的下拉框(可以被重复选择)
例如:创建用户信息时候,需要选择一个用户类型【普通用户】【金牌用户】【铂金用户】等。
多对多:在某表中创建一行数据是,有一个可以多选的下拉框
例如:创建用户信息,需要为用户指定多个爱好
一对一:在某表中创建一行数据时,有一个单选的下拉框(下拉框中的内容被用过一次就消失了
例如:原有含10列数据的一张表保存相关信息,经过一段时间之后,10列无法满足需求,需要为原来的表再添加5列数据
1、1对多
如果A表的1条记录对应B表中N条记录成立,两表之间就是1对多关系;在1对多关系中 A表就是主表,B表为子表,ForeignKey字段就建在子表;
如果B表的1条记录也对应A表中N条记录,两表之间就是双向1对多关系,也称为多对多关系;
在orm中设置如果 A表设置了外键字段user=models.ForeignKey('UserType')到B表(注意外键表名加引号)
就意味着 写在写A表的B表主键, (一列),代表B表的多个(一行)称为1对多,
查询
总结:利用orm获取 数据库表中多个数据
获取到的数据类型本质上都是 queryset类型,
类似于列表,
内部有3种表现形式(对象,字典,列表)
modle.表名.objects.all()
modle.表名.objects.values()
modle.表名.objects.values()
跨表
正操作
所以表间只要有外键关系就可以一直点下去。。。点到天荒地老
所以可以通过obj.外键.B表的列表跨表操作(注意!!orm连表操作必须选拿单个对象,不像SQL中直接表和表join就可以了)
print(obj.cls.title)
foreignkey字段在那个表里,那个表里一个"空格"代表那个表的多个(一行)
class UserGroup(models.Model):
"""部门 3"""
title = models.CharField(max_length=32)
class UserInfo(models.Model):
"""员工4"""
nid = models.BigAutoField(primary_key=True)
user = models.CharField(max_length=32)
password = models.CharField(max_length=64)
age = models.IntegerField(default=1)
#ug_id 1
ug = models.ForeignKey("UserGroup",null=True)
1. 在取得时候跨表
q = UserInfo.objects.all().first()
q.ug.title
2. 在查的时候就跨表了
UserInfo.objects.values('nid','ug_id')
UserInfo.objects.values('nid','ug_id','ug__title') #注意正向连表是 外键__外键列 反向是小写的表名
3. UserInfo.objects.values_list('nid','ug_id','ug__title')
反向连表:
反向操作无非2种方式:
1、通过对象的形式反向跨表:小写表面_set().all()
2、通过value和value_list方式反向跨表:小写表名__字段
1. 小写的表名_set 得到有外键关系的对象
obj = UserGroup.objects.all().first()
result = obj.userinfo_set.all() [userinfo对象,userinfo对象,]
2. 小写的表名 得到有外键关系的列 #因为使用values取值取得是字典的不是对象,所以需要 小写表名(外键表)__
v = UserGroup.objects.values('id','title')
v = UserGroup.objects.values('id','title','小写的表名称')
v = UserGroup.objects.values('id','title','小写的表名称__age')
3. 小写的表名 得到有外键关系的列
v = UserGroup.objects.values_list('id','title')
v = UserGroup.objects.values_list('id','title','小写的表名称')
v = UserGroup.objects.values_list('id','title','小写的表名称__age')
1对多自关联( 由原来的2张表,变成一张表! )
想象有第二张表,关联自己表中的 行
代码
class Comment(models.Model):
"""评论表"""
news_id = models.IntegerField() #新闻ID
content = models.CharField(max_length=32) #评论内容
user = models.CharField(max_length=32) #评论者
reply = models.ForeignKey('Comment',null=True,blank=True,related_name='xxxx') #回复ID
2、 多对多:
1、自己写第3张关系表
ORM多对多查询:
女士表:
男生表:
男女关系表
多对跨表操作
View Code
多对多关系表 数据查找思路
1、找到该对象
2.通过该对象 反向操作 找到第三张关系表
3.通过第三张关系表 正向操作 找到 和该对象有关系对象
总结(只要对象1和对象2 中间有关系表建立了关系; 对象1反向操作 到关系表 ,关系表正向操作到对象2,反之亦然
2、第3张关系表不用写(m=models.ManyToManyField(' 要关联的表') 自动生成 )
由于DjangoORM中一个类名对应一张表,要想操作表就modles.类直接操作那张表,但使用ManyToManyField字段生成 “第三张”关系表怎么操作它呢?
答案:通过单个objd对象 间接操作
View Code
正向操作: obj.m.all()
View Code
反向操作 :obj.小写的表名_set
多对多和外键跨表一样都是 小写的表名_set
3、既自定义第三张关系表 也使用ManyToManyField('Boy')字段(杂交类型)
ManyToManyField()字段创建第3张关系表,可以使用字段跨表查询,但无法直接操作第3张表,
自建第3表关系表可以直接操作,但无法通过字段 查询,我们可以把他们结合起来使用;
作用:
1、既可以使用字段跨表查询,也可以直接操作第3张关系表
2、obj.m.all() 只有查询和清空 方法
View Code
View Code
外键反向查找别名(方便反向查找)
在写ForeignKey字段的时候,如果想要在反向查找时不使用默认的 小写的表名_set,就在定义这个字段的时间加related参数!
related_name、related_query_name 字段=什么别名 反向查找时就使用什么别名!
反向查找:
设置了related_query_name 反向查找时就是obj.别名_set.all()保留了_set
related_query_name
View Code
related_name
反向查找:
设置了relatedname就是 反向查找时就说 obj.别名.all()
View Code
操作
View Code
多对多自关联(由原来的3张表,变成只有2张表)
把两张表通过 choices字段合并为一张表
‘第三张关系表’ 使用models.ManyToManyField('Userinfo')生成
特性:
obj = models.UserInfo.objects.filter(id=1).first() 获取对象
1、查询第三张关系表前面那一列:obj.m
select xx from xx where from_userinfo_id = 1
2、查询第三张关系表后面那一列:obj.userinfo_set
select xx from xx where to_userinfo_id = 1
View Code
查找方法
View Code
多对多自关联特性:
ManyToManyField生成的第三张表
五、浅谈ORM查询性能
View Code
六、分组和聚合查询
1、aggregate(*args,**kwargs) 聚合函数
通过对QuerySet进行计算,返回一个聚合值的字典。aggregate()中每一个参数都指定一个包含在字典中的返回值。即在查询集上生成聚合。
View Code
2、annotate(*args,**kwargs) 分组函数
View Code
七、F查询与Q查询
仅仅靠单一的关键字参数查询已经很难满足查询要求。此时Django为我们提供了F和Q查询:
1、F 可以获取对象中的字段的属性(列),并对其进行操作;
from django.db.models import F,Q
#F 可以获取对象中的字段的属性(列),并且对其进行操作;
models.Book.objects.all().update(price=F('price')+1) #对图书馆里的每一本书的价格 上调1块钱
2、Q多条件组合查询
Q()可以使orm的fifter()方法支持, 多个查询条件,使用逻辑关系(&、|、~)包含、组合到一起进行多条件查询;
语法:
fifter(Q(查询条件1)| Q(查询条件2))
fifter(Q(查询条件2)& Q(查询条件3))
fifter(Q(查询条件4)& ~Q(查询条件5))
fifter(Q(查询条件6)| Q(Q(查询条件4)& ~ Q(Q(查询条件5)& Q(查询条件3)))包含
View Code
注意:Q查询条件和非Q查询条件混合使用注意,不包Q()的查询条件一点要放在Q(查询条件)后面
八、Django自带ContentType表
首先声明本文介绍的ContentType不是http协议中请求头里Content Type,而是Django程序启动后自带的一张表;
setings.py配置文件
1、ContentType表内容介绍
ContentType表记录了Django程序的所有APP下model中的表名、和所在app的名称;
2、应用场景:
2.1 通过ContentType中的app名称和表名,查找到Django model中所有表;
View Code;
2.2 解决 1张表 同时 其他N张表建立外键,并且多个外键中只能选择1个,关系的复杂问题
场景1:你是一家在线教育的DBA,现有N种优惠券,每1种优惠券怎么分别对应 N门课程中的一1门课程,怎么设计表结构呢?
View Code
场景2 :学生 学习成绩如何要奖惩、 作业写得如何要奖惩、学习进度如何要奖惩、。。。。。。学生各种行为都要奖惩怎么设计表结构?
View Code
3、content type 操作
model
视图
3.1 GenericForeignKey 创建
给学位课1,创建优惠券100
方式1
方式2
model
视图
3.2 GenericRelation 查询
当前课程都有哪些优惠券?
model
视图
九、补充
oder_by(-id):按照某种规则排序
exclude(字段):字段获取数据时排除
View Code
二龙湖浩哥:http://www.cnblogs.com/yuanchenqi/articles/6811632.html#3763280
银角大王:http://www.cnblogs.com/wupeiqi/articles/5246483.html