ORM (数据库)
Django的ORM操作本质上会根据对接的数据库引擎,翻译成对应的sql语句;所有使用Django开发的项目无需关心程序底层使用的是MySQL、Oracle、sqlite等等;如果数据库迁移,只需要更换Django的数据库引擎即可。
ORM 默认使用的数据库:SQLlite
ORM核心功能:
1.操作表(创建表、修改表(新增表字段、修改表字段)、删除表)
2.操作表数据行(增删改查)
推荐博客:https://www.cnblogs.com/wupeiqi/articles/6216618.html
数据库配置操作步骤
a. 以MySQL
数据库为案例介绍,本地或服务器上已成功安装MySQL
并创建需要的数据库(djangot)。
b. 修改数据库配置文件:
- 打开
mysite/settings.py
,找到DATABASES
进行以下内容修改:
DATABASES = {
'default': {
# 修改连接MySQL
'ENGINE': 'django.db.backends.mysql', # 数据库类型
'NAME': 'djangot', # 要连接的数据库名称
'USER': 'root', # 登录账号
'PASSWORD': '', # 登录密码
'HOST': 'localhost', # 连接地址(IP)
'PORT': 3306, # 端口,默认3306
}
}
ENGINE
可选值: 'django.db.backends.sqlite3'
'django.db.backends.postgresql'
'django.db.backends.mysql'
'django.db.backends.oracle'
c. 在与工程同名的目录下[mysite\mysite\],在__init__.py文件添加以下内容:
将Django默认连接的MySQLdb,改为pymysql。
import pymysql # 导入MySQL模块,第三方库需提前安装
pymysql.install_as_MySQLdb()
创建数据库表
a. 在应用app01
目录下的 model.py 文件中写入需要创建的相关表结构(不包含数据),比如以下示例:
注意:类必须继承 models.Model
;在Django的ORM中“类”代表数据库中表,对象(object)代表数据表中的一行数据,对象中包含每个字段。
# app01\model.py
from django.db import models
# 类名==表名
class UserInfo(models.Model): # 必须继承 models.Model
# nid字段,可不写,django默认会自动添加字段(id)并加自增主键
nid = models.BigAutoField(primary_key=True) # primary_key==主键 自增
name = models.CharField(max_length=32) # max_length==最大长度
class UserGroup(models.Model):
title = models.CharField(max_length=32)
每个class
被表示为 django.db.models.Model
类的子类。class
名就等于数据库表名;每个class
有一些类变量,它们都表示模型里的一个数据库字段。
b. 将上面写的表,在数据库中进行创建,需执行以下两个命令:
注意:需在工程目录下执行命令
老版本:
D:\mysite $ python manage.py syncdb
Django 1.7.1 及以上的版本需要用以下命令:
D:\mysite $ python manage.py makemigrations # 根据app下的[migrations]目录中的记录,检测当前[model]层代码是否发生变化
D:\mysite $ python manage.py migrate # 把ORM代码转换成SQL语句去数据库执行
以上命令执行成功后,查看对应的数据库会创建UserInfo
和UserGroup
两个表。
D:\mysite $ python manage.py migrate
Operations to perform:
Apply all migrations: admin, app01, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying app01.0001_initial... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying sessions.0001_initial... OK
显示以上信息表示执行成功,请查看对应数据库。
- models常用字段类型:
方法 | 说明 |
---|---|
BigAutoField | bigint自增列,必须填入参数 primary_key=True |
AutoField | int自增列,必须填入参数 primary_key=True |
CharField | 字符类型,必加参数:max_length |
IntegerField | 整数列(有符号的) -2147483648 ~ 2147483647 |
PositiveIntegerField | 正整数 0 ~ 2147483647 |
BigIntegerField | 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807 |
FloatField | 浮点型 |
ForeignKey | 用于关联表 |
TextField | 文本类型 |
DateTimeField | 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] |
DateField | 日期格式 YYYY-MM-DD |
TimeField | 时间格式 HH:MM[:ss[.uuuuuu]] |
ForeignKey ( to, on_delete, **options )
有两个必选的参数 :
第一个参数:要关联的表(主表),在默认的情况下,外键储存的是主表的主键(Primary key)。
第二个参数:models.CASCADE
,当主表的字段被删除时,和他有关的子表字段也会被删除,还有models.PROTECT
(返回错误提示,阻止删除),models.SET_NULL
(用null替代),models.SET_DEFAULT
(用默认值替代),或者用models.SET()
自定义。
可选参数,下面介绍较为常用的几个:
1、to_field
: 设置关联到主表的字段,例如:models.ForeignKey('tablename', to_field=tablename.字段名)
注意:关联字段的内容必须是不重复的。在默认情况下,Django 关联到的字段是主表的主键。
2、related_name
:自定义一个名称,用于反向查询
注意:当一张子表里,多个foreignkey指向同一个主表,related_name必须设置。
- 建表字段常用参数说明:
参数 | 说明 |
---|---|
primary_key | True表示设置主键,默认False |
null | True表示可以为空,默认False |
default | 设置默认值,NOT_PROVIDED(默认不提供) |
db_column | 数据库中字段的列名 |
db_index | 数据库中字段是否可以建立索引 |
unique | 数据库中字段是否可以建立唯一索引 |
unique_for_date | 数据库中字段【日期】部分是否可以建立唯一索引 |
unique_for_month | 数据库中字段【月】部分是否可以建立唯一索引 |
unique_for_year | 数据库中字段【年】部分是否可以建立唯一索引 |
c. 给表返回的对象加显示内容:
class UserInfo(models.Model):
nid = models.BigAutoField(primary_key=True)
name = models.CharField(max_length=32)
def __str__(self):
# 设置访问对象时,显示的字段内容
return self.name
# 不加 __str__ 情况:
obj02 = models.UserInfo.objects.all()
print(obj02) # <QuerySet [<UserInfo: UserInfo object (1)>, <UserInfo: UserInfo object (2)>]>
# 加 __str__ 情况:
obj02 = models.UserInfo.objects.all()
print(obj02) # <QuerySet [<UserInfo: root>, <UserInfo: test1>]>
数据表相关操作
新增字段
如果对已创建的表想要新增字段时,只需要在原有的基础上直接添加即可,以UserGroup
表为案例,新增一个name
字段:
注意:因为原表已有字段是有数据的,所以新增的字段必须加默认值或为空设置。
# app01\model.py
class UserGroup(models.Model):
title = models.CharField(max_length=32)
name = models.CharField(max_length=64, null=True) # 新增部分
代码添加完成后执行以下两个命令:
$ python manage.py makemigrations
Migrations for 'app01':
app01\migrations\0002_UserGroup_name.py
- Add field name to UserGroup
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, app01, auth, contenttypes, sessions
Running migrations:
Applying app01.0002_test01_name... OK
显示以上信息表示执行成功,请查看数据库对应字段已添加上。
修改原字段
如果对已创建的表中某个字段名想要修改时,以UserGroup
表为案例,比如修改原字段name
改为username
,只需要在原有的基础上直接修改:
class UserGroup(models.Model):
title = models.CharField(max_length=32)
# name = models.CharField(max_length=64, null=True) # 原来的
username = models.CharField(max_length=64, null=True) # 修改的,只修改变量名别的不动
代码添加完成后执行以下两个命令:
$ python manage.py makemigrations
# 以下的意思是说明:是否将name重命名为username,输入 y 表示同意,N 表示退出
Did you rename UserGroup.name to UserGroup.username (a CharField)? [y/N] y
Migrations for 'app01':
app01\migrations\0003_auto_20211109_1703.py
- Rename field name on UserGroup to username
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, app01, auth, contenttypes, sessions
Running migrations:
Applying app01.0003_auto_20211109_1703... OK
显示以上信息表示执行成功,请查看数据库对应字段已修改。
表和表进行关联
如果想让表与表之间进行关联,只需在其中一个表中新增一个ForeignKey
类型的字段,以下案例详细说明。
- UserInfo表关联UserGroup表(A -> B)
注意:在原有表进行增加字段时,需要给ForeignKey
设置为空(null=True)情况。数据库加关联字段时会自动给变量(ut)多加个”_id“,数据库表中字段为:ut_id
ut 代表关联表中的一行数据的对象,默认与主键关联;
关联方式类似于SQL的 JOIN ... on A.ut=B.id,比如UserInfo表中"ut"代表职位id,将于UserGroup表中对应的职位id对应关联。
# app01\model.py
from django.db import models
class UserInfo(models.Model):
name = models.CharField(max_length=32)
# 关联表:UserInfo -> UserGroup
ut = models.ForeignKey('UserGroup', on_delete=models.CASCADE, null=True)
class UserGroup(models.Model):
title = models.CharField(max_length=32)
查询跨表数据方式,注意 ut 在数据表中的区别
# app01\views.py
from app01 import models
def text(request):
userinfo = models.UserInfo.objects.all()
for obj in userinfo:
print(obj.username, obj.password, obj.ut_id, obj.ut.title)
# obj.ut_id 将获取UserGroup表中的 id 数据,obj.ut.title将获取UserGroup表中的 title 数据
- 正向关联
以跨表查询数据(A -> B -> C)案例
以下案例中,实现三个表进行关联
# app01\model.py
from django.db import models
class UserInfo(models.Model):
name = models.CharField(max_length=32)
ut = models.ForeignKey('UserGroup', on_delete=models.CASCADE, null=True)
class UserGroup(models.Model):
title = models.CharField(max_length=32)
ug = models.ForeignKey('Test01', on_delete=models.CASCADE, null=True)
class Test01(models.Model):
title = models.CharField(max_length=32)
name = models.CharField(max_length=64, null=True)
查询跨表数据方式,注意 ut 在数据表中的区别
# app01\views.py
from app01 import models
def text(request):
userinfo = models.UserInfo.objects.all()
for obj in userinfo:
print(obj.ut.ug.title)
# obj.ut.usergroup.ug 将获取Test01表中的 id 数据;
# 执行顺序是先通过ut跨UserGroup表,再通过UserGroup表中的ug跨到Test01表,获取Test01表中的字段数据
- 反向关联
被ForeignKey关联的表,该函数都会隐含 table(小写表名)_set 字段,通过隐含 table_set.all()
方法可以获取该表中的所有数据。table_set.all()
方法结果返回的是 QuerySet对象 list。
以跨表查询数据(A <- B)案例
通过 UserType 表中的隐含方法,来获取 UserInfo 表中的字段数据:
# app01\model.py
from django.db import models
class UserInfo(models.Model):
name = models.CharField(max_length=32)
ut = models.ForeignKey('UserGroup', on_delete=models.CASCADE, null=True)
class UserGroup(models.Model):
title = models.CharField(max_length=32)
# app01\views.py
from app01 import models
def text(request):
obj = models.UserGroup.objects.all().first() # 只取一行数据
for item in obj.userinfo_set.all():
print(item.name, item.id)
- 多对多关联
点击查看代码
# app01\model.py 创建表
from django.db import models
class Info(models.Model):
name = models.CharField(max_length=32)
class Group(models.Model):
title = models.CharField(max_length=32)
class Test02(models.Model):
info = models.ForeignKey('Info')
group = models.ForeignKey('Group')
# 联合唯一索引,约束
class Meta:
unique_together = [('info', 'group'),]
点击查看代码
# 给对应表插入数据
objs1 = [ models.Info(name='A1'), models.Info(name='A2'), models.Info(name='A3')]
models.Info.objects.bulk_create(objs1, 2)
objs2 = [models.Group(title='B1'),models.Group(title='B2'), models.Group(title='B3')]
models.Group.objects.bulk_create(objs2, 2)
models.Test02.objects.create(info_id=1, group_id=1)
models.Test02.objects.create(info_id=1, group_id=2)
models.Test02.objects.create(info_id=2, group_id=1)
models.Test02.objects.create(info_id=2, group_id=3)
点击查看代码
# 查询数据,通过Test02表进行跨表查询Info表中A1对应的Group表中对应的数据
tt = models.Test02.objects.filter(info_name='A1')
for it in tt:
print(it.group.title)
- ManyToManyField
另一种方式关联表: ManyToManyField 字段关联
点击查看代码
# app01\model.py 创建表
from django.db import models
class Info(models.Model):
name = models.CharField(max_length=32)
a = models.ManyToManyField('Group') # Django会自动创建新表,进行两个表关联 表名:info_a
class Group(models.Model):
title = models.CharField(max_length=32)
#a = models.ManyToManyField('Info') # 相关联的表可以放到任意表中,对该表不会做额外操作
''' 对自动生成的表,添加数据操作 '''
obj=models.Info.objects.filter(name='A1').first()
obj.a.add(1)
# 执行结果:info_a表中增加1条数据 name_id=1, title_id=1
obj.a.add(2,3)
# 执行结果:info_a表中增加2条数据 name_id=1, title_id=2, name_id=1, title_id=3
obj.a.add(*[4,])
# 执行结果:info_a表中增加1条数据 name_id=1, title_id=4
'''删除操作'''
obj.a.remove(1)
# 执行结果:info_a表中 title_id=1 的相关行数据
obj.a.remove(2,3)
# 执行结果:info_a表中 title_id=2, title_id=3 的相关行数据
obj.a.remove(*[4,])
# 执行结果:info_a表中 title_id=4 的相关行数据
'''清空数据'''
obj.a.clear() # 将name='A1'关联Group表中的数据,全部清空
'''覆盖原数据'''
obj.a.set([1,2,3]) # info_a表中会将原数据全部删除,添加列表中的新数据,对应 title_id 字段
'''获取数据'''
q = obj.a.all() # 这里取的是Group表的对象数据
'''通过Group表获取Info表的数据'''
obj = models.Group.objects.filte(title='B1').first() # 获取Group表对象
v = obj.info_set.all() # 获取Info表对象
- ManyToManyField 进行自己关联
点击查看代码
class UserInfoTest(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=64)
age = models.IntegerField(default=0)
gender_choices = ((1,'男'),(2,'女'))
genderss = models.IntegerField(choices=gender_choices)
# 自己与自己关联
m = models.ManyToManyField('UserInfo')
执行命令后,数据库自动创建数据表:
点击查看代码
def test(request):
obj = models.UserInfoTest.objects.filter(id=2).first()
res = obj.m.all() # 通过m字段去获取对应的数据,匹配的是‘from_userinfotest_id’字段列
for it in res:
print(it.username)
# 反向查询
v = obj.userinfotest_set.all() # userinfotest_set=匹配的是‘to_userinfotest_id’字段列
for it in v:
print(it.username)
return HttpResponse('......')
- ForeignKey 自己关联
class UserInfoTT(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=64)
age = models.IntegerField(default=0)
gender_choices = ((1,'男'),(2,'女'))
genderss = models.IntegerField(choices=gender_choices)
# 自己与自己关联
f = models.ForeignKey('UserInfo', on_delete=models.CASCADE, null=True, blank=True)
执行后数据库字段显示:
表数据操作
案例中的相关表结构:
# app01\model.py
from django.db import models
class UserInfo(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=64)
ut = models.ForeignKey('UserGroup', on_delete=models.CASCADE, null=True)
class UserGroup(models.Model):
title = models.CharField(max_length=32)
增加数据
- create() 新增数据(单表操作)
# app01\views.py
from app01 import models
def text(request):
''' 增加数据 '''
models.UserType.objects.create(title='开发部') # 往UserType表增加一行数据
models.UserInfo.objects.create(username='root', password=123, ut_id=1) # UserInfo表增加一行数据
return HttpResponse('......')
以上函数执行完后,数据库中就新增一条数据:
- bulk_create 批量创建数据
# bulk_create(self, objs, batch_size=None)
obj = [ models.Test01(title='xx1'), # 创建的对象,还没有执行到数据库
models.Test01(title='xx2'), ]
models.Test01.objects.bulk_create(obj, 10) # obj=数据对象 10=每次操作条数,上限999
- get_or_create
如果存在,则获取,否则,创建
defaults 指定创建时,其他字段的值
# get_or_create(self, defaults=None, **kwargs)
obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'password': '1234','ut_id': 3, 'age': 18})
或
obj, created = models.UserInfo.objects.get_or_create(
username='root1', password= '1234', # 支持多个
defaults={'ut_id': 3, 'age': 18})
# 先在UserInfo表中查username字段数据为root1的,如果存在,则返回数据;如果不存在,则新创建数据。
# obj==对象 created==True/False
- update_or_create
如果存在,则更新,否则,创建
defaults 指定创建时或更新时的其他字段
obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'password': '1234','ut_id': 3, 'age': 18})
查询数据
- all() 查询表中所有数据(对象)(单表操作)
# app01\views.py
from app01 import models
def text(request):
''' 查询数据 '''
usertype_obj_list = models.UserType.objects.all() # 返回QuerySet类型,类似list->对象
print(usertype_obj_list) # <QuerySet [<UserType: UserType object (1)>]>
# 获取其中具体字段的数据
for row in usertype_obj_list:
print(row.id, row.title) # 1 开发部
return HttpResponse('......')
- filter() 条件筛选(where),传入多条件表示:and(单表操作)
# app01\views.py
from app01 import models
def text(request):
# 只查询字段id=1的数据
obj_list01 = models.UserType.objects.filter(id=1)
# 多条件默认为and
obj_list02 = models.UserType.objects.filter(id=1, title='开发部')
# 只查询字段id>1的数据
obj_list03 = models.UserType.objects.filter(id__gt=1)
# 跨表方式
obj_list04 = models.UserType.objects.filter(ut__title='开发部')
print(obj_list04.ut.title) # 获取具体数据
return HttpResponse('......')
# 传多个条件,用字典方式:
com = {'id':1, 'title':"开发部"}
models.UserType.objects.filter(**com)
- 组合条件查询:Q方式(下面详细介绍)
- 其他筛选条件方式:
参数 | 说明 |
---|---|
__gt | 大于 eq: id__gt=1 |
__lt | 小于 eq: id__lt=5 |
__gte | 大于等于 eq: id__gte=1 |
__lte | 小于等于 eq: id__lte=5 |
__in | 在...范围内 eq: id__in = [1,2,3] |
__range | 在...范围内 eq: id__range = [1,2,3] |
__exact | 精确等于 like 'aaa' |
__iexact | 精确等于 忽略大小写 ilike 'aaa' |
__contains | 包含 like '%aaa%' |
__icontains | 包含 忽略大小写 ilike '%aaa%',但是对于sqlite来说,contains的作用效果等同于icontains |
__startswith | 以…开头 |
__endswith | 以…结尾 |
__istartswith | 以…开头 忽略大小写 |
__iendswith | 以…结尾,忽略大小写 |
__year | 日期字段的年份 |
__month | 日期字段的月份 |
__day | 日期字段的日 |
__week_day | 日期字段的周 |
__isnull | 是否为空 eq: __isnull=True/False |
__regex | 正则匹配,Entry.objects.get(title__regex=r'^(An?The) +' ) |
__iregex | 正则不区分大小写,Entry.objects.get(title__iregex=r'^(an?|the)+' ) |
- values() 取指定某字段列的数据,返回的是列表嵌套字典
# app01\views.py
from app01 import models
def text(request):
res = models.UserInfo.objects.values("username")
print(res) #> <QuerySet [{'username': 'root'}, {'username': 'test1'}]>
print(list(res)) #> [{'username': 'root'}, {'username': 'test1'}]
- values_list() 取指定某字段列的数据,返回的是列表嵌套元组
# app01\views.py
from app01 import models
def text(request):
res = models.UserInfo.objects.values_list("username")
print(res) #> <QuerySet [('root',), ('test1',)]>
print(list(res)) #> [('root',), ('test1',)]
- values()和values_list() 跨表查询数据方式
注意: 通过ForeignKey的变量加“双下划线”加跨表的字段(ut__title)方式,进行跨表查询
# app01\views.py
from app01 import models
def text(request):
res = models.UserInfo.objects.values("username", "ut__title")
obj = models.UserInfo.objects.values_list("username", "ut__title")
print(obj) #> <QuerySet [('root', '开发部'), ('test1', '开发部')]>
print(list(obj)) #> [('root', '开发部'), ('test1', '开发部')]
- first() 取第一行数据
# app01\views.py
from app01 import models
def text(request):
obj = models.UserInfo.objects.first() # 返回对象
print(obj, obj.username)
- count() 计算总行数
# app01\views.py
from app01 import models
def text(request):
number = models.UserInfo.objects.count() # 返回 int 类型
print(number)
- 索引方式[ : ]
# app01\views.py
from app01 import models
def text(request):
obj = models.UserInfo.objects.all()[1:5] # 返回 对象
print(obj)
- in_bulk 根据主键进行查询
# 类似于 in条件查询
models.UserInfo.objects.filter(id__in=[1,2,3])
# 上下同理
models.UserInfo.objects.in_bulk([1,2,3])
- last 取最后一个数据
# app01\views.py
from app01 import models
def text(request):
obj = models.UserInfo.objects.last() # 返回对象
print(obj, obj.username)
修改数据
- update() 更新指定数据(单表操作)
# app01\views.py
from app01 import models
def text(request):
''' 更新数据 '''
# 查询条件id=1的数据,更新指定字段
models.UserType.objects.filter(id=1).update(title='分析部')
return HttpResponse('......')
- 更新一列数据方式: F方式(下面详细介绍)
删除数据
- delete() 删除指定数据(单表操作)
# app01\views.py
from app01 import models
def text(request):
''' 删除数据 '''
# 删除查询条件字段id=2的数据
models.UserType.objects.filter(id=2).delete()
return HttpResponse('......')
跨表读取字段方式
以 UserInfo表和 UserGroup表为案例,讲解读取跨表字段情况:
# app01\model.py
from django.db import models
class UserInfo(models.Model):
name = models.CharField(max_length=32)
ut = models.ForeignKey('UserGroup', on_delete=models.CASCADE, null=True)
class UserGroup(models.Model):
title = models.CharField(max_length=32)
- 正向获取表字段数据(UserInfo left join UserType)
# 1. 通过 ForeignKey 类型字段获取数据
obj01 = UserInfo.objects.first() # 返回对象
print(obj01.ut.title) # 返回具体字段的数据
# 2. 通过双下划线(__) 获取数据
obj02 = UserInfo.objects.values('ut_id', 'ut__title') # 返回对象
print(list(obj02)) # 返回[{},{}]
obj03 = UserInfo.objects.values_list('ut_id', 'ut__title') # 返回对象
print(list(obj03)) # 返回[(),()]
- 反向获取表字段数据(UserType left join UserInfo)
# 1. 通过 小写表名_set 取跨表的数据
obj01 = UserGroup.objects.first() # 返回对象
res = obj01.userinfo_set.all() # 返回 userinfo表的对象[userinfo对象,userinfo对象]
print(res[0].username) # 返回具体字段数据
# 2. 通过 小写表名 和 表名+双下划线(__) 方式获取跨表字段数据
obj02 = UserGroup.objects.values('userinfo', 'userinfo__name') # 返回对象
print(list(obj02)) # 返回[{},{}]
obj03 = UserGroup.objects.values_list('userinfo', 'userinfo__name') # 返回对象
print(list(obj03)) # 返回[(),()]
其他条件查询方式
条件 不等于(exclude() )
obj03 = models.UserInfo.objects.values('ut_id').exclude(id=1)
print(obj03.query)
# SELECT `app01_userinfo`.`ut_id` FROM `app01_userinfo` WHERE NOT (`app01_userinfo`.`id` = 1)
去重 distinct()
注意:只有PostgreSQL 数据库中,distinct才能传参数,进行去重操作。
连 mysql 或 sqlite 数据库中,distinct不能传参数。
obj = models.UserInfo.objects.values('ut_id').distinct()
print(obj.query)
# SELECT DISTINCT `app01_userinfo`.`ut_id` FROM `app01_userinfo`
排序 order_by(字段)
obj1 = obj01.userinfo_set.all().order_by('id') # 表示按 id 从小到大排序
# 加减号(-)表示方向排序
obj2 = obj01.userinfo_set.all().order_by('-id') # 表示按 id 从大到小排序
# 多字段排序
obj3 = obj01.userinfo_set.all().order_by('id', '-name') # 先按id排序,在按name排序
reverse 倒序
只能和 order by 一起使用才会生效。
如果存在order_by,reverse则为倒序,如果多个排序规则一一倒序
models.UserInfo.objects.all().order_by('-nid', 'age')
# 以上排序方式: 先nid从大到小,后age从小到大
models.UserInfo.objects.all().order_by('-nid', 'age').reverse()
# 以上排序方式: 先nid从小到大,后age从大到小
分组 annotate(name=规则)
from django.db.models import Count, Min, Max, Sum
obj02 = models.UserInfo.objects.values('ut_id').annotate(f=Count('id'))
print(obg.query)
# SELECT `app01_userinfo`.`ut_id`, COUNT(`app01_userinfo`.`id`) AS `f` FROM `app01_userinfo` GROUP BY `app01_userinfo`.`id` ORDER BY NULL
having 用法
obj02 = models.UserInfo.objects.values('ut_id').annotate(ff=Count('id')).filter(ff__lt=10)
print(obj02.query)
# SELECT `app01_userinfo`.`ut_id`, COUNT(`app01_userinfo`.`id`) AS `ff` FROM `app01_userinfo` GROUP BY `app01_userinfo`.`ut_id` HAVING COUNT(`app01_userinfo`.`id`) < 10 ORDER BY NULL
query 输出 sql 语句
obj = UserGroup.objects.all()
print(obg.query) # 返回生成的 SQL 语句
only 取指定字段
注意:主键写不写都会取数据
models.UserInfo.objects.only('username','id')
或
models.UserInfo.objects.filter(...).only('username','id')
或
models.UserInfo.objects.all().only('username').extra( select={'nid':1,} )
F 字段列自增数值
- F("字段名") 更新时取原来的数据
比如给 UserInfo 表中的 age 字段列所有数据自增 +1
from django.db.models import F
obj = models.UserInfo.objects.all().update(age=F("age")+1)
Q 组合条件查询方式
- Q() 多条件查询
用于filter()多个条件时的组合,还可以加其他条件
from django.db.models import Q
# 方式一:
models.UserInfo.objects.filter(Q(id=1)) # Q(id=1) 等价于 id=1, id为数据表中的字段
models.UserInfo.objects.filter(Q(id=1) | Q(id__gt=2)) # | 表示 or
models.UserInfo.objects.filter(Q(id=1) & Q(id=2)) # & 表示 and
# 方式二:
q1 = Q() # 创建对象
q1.connector = 'OR' # 字段与字段的条件规则 等价于 id=1 or id=9
q1.children.append(('id', 1)) # 字段添加到 q1 对象中, id为数据表中的字段
q1.children.append(('id__gt', 9))
q2 = Q()
q2.connector = 'OR' # 等价于 c1=1 or c1=10
q2.children.append(('c1', 1)) # c1为数据表中的字段
q2.children.append(('c1__gt', 10))
con = Q() # 将q1和q2合并为一个大的条件
con.add(q1, 'AND')
con.add(q2, 'AND') # con 等价于 (id=1 or id=9) and (c1=1 or c1=10)
models.UserInfo.objects.filter(con) # 传入条件
# 案例,比如格式: (id=1 or id=2 or (name='xx' and name='yy'))
a1 = Q()
a1.connector = 'OR'
a1.children.append(('id', 1))
a1.children.append(('id__gt', 2))
a2 = Q()
a2.connector = 'AND'
a2.children.append(('name', 'xx'))
a2.children.append(('name', 'yy'))
a1.add(a2, 'OR') # 将a2的条件添加到a1中
# 案例,比如格式: 自动将字典转换为条件格式
comon_dict = {
'a': [1,2,3],
'b': [4,5],
'c': [6]
}
con = Q()
for k,v in comon_dict.items():
q = Q()
q.connector = 'OR'
for i in v:
q.children.append((k, i)) # 这里的'k'对应数据表的字段
con.add(q, 'AND')
models.UserInfo.objects.filter(con)
extra 额外的查询条件
models.表.objects.extra(
select={}, select_params=[], # 映射
where=[], params=[], # 条件
order_by=[], # 排序
tables=[]) # 表
参数用法:
select={}, select_params=[]
# 作用于: select 此处 from 表
where=[], params=[]
# 作用于: select * from 表 where 此处
示例: where=["id=1 or id=2", "age>10"] # 参数之间是用 and 连接
tables=[] # 参数对应数据库中的表名
# 作用于: select * from 表, 此处
order_by=[],
# 作用于: select * from 表 order by 此处
# 示例:
obj = models.UserInfo.objects.extra(
select={'new1': 'select count(1) from app01_usertype where id > %s',
'new2': 'select count(1) from app01_test01 where id > %s', },
select_params=[1, 2],
where=['age>=%s'], params=[2],
order_by=['-age'],
tables=['app01_usertype'])
print(obj.query)
"""SELECT (select count(1) from app01_usertype where id > 1) AS `new1`,
(select count(1) from app01_test01 where id > 2) AS `new2`,
`app01_userinfo`.`id`, `app01_userinfo`.`username`, `app01_userinfo`.`password`, `app01_userinfo`.`age`, `app01_userinfo`.`ut_id` FROM `app01_userinfo` , `app01_usertype` WHERE (age>=2) ORDER BY `app01_userinfo`.`age` DESC
"""
用原 SQL 直接进行查询
将原SQL语句转换为models类型进行相关查询操作,导入方式: from django.db import connection, connections
,内部会自动进行数据库连接。
在配置文件“setting.py”中的DATABASES
配置多个数据库:
# setting.py 配置两个数据库
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db1.sqlite3'),
},
'db2': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db2.sqlite3'),
},
}
- connection 连接默认数据库(default)
from django.db import connection
cursor = connection.cursor() # connection = default中的数据库
- connections[] 连接指定数据库
from django.db import connections
cursor = connections['db2'].cursor()
- 查询数据方式
# 写SQL
cursor.execute(""" select * from 表 where id=%s""", [1])
# 获取查询数据
a = cursor.fetchone() # 获取一条数据 tuple
b = cursor.fetchall() # 获取所有数据 tuple嵌套tuple
raw 执行原生SQL
# raw(self, raw_query, params=None, translations=None, using=None)
result = models.UserInfo.objects.raw('select * from app01_usertype')
# 返回[obj(UserInfo),obj(UserInfo)]
# 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名
models.UserInfo.objects.raw('select id as nid from 其他表')
# 为原生SQL设置参数
models.UserInfo.objects.raw('select id as nid from app01_userinfo where nid>%s', params=[12,])
# 将获取的到列名转换为指定列名
name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)
# 指定数据库
models.UserInfo.objects.raw('select * from userinfo', using="default")
using 指定数据库取数据
根据 setting.py 中配置的数据库, 默认取default 。
models.UserInfo.objects.all().useing('db2')
# 这就表示向 db2 数据库中获取表数据
select_related 一次性获取关联的数据
# 性能相关:表之间进行join连表操作,一次性获取关联的数据。
model.tb.objects.all().select_related()
model.tb.objects.all().select_related('外键字段')
model.tb.objects.all().select_related('外键字段__外键字段')
prefetch_related
# 性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。
# 获取所有用户表
# 获取用户类型表where id in (用户表中的查到的所有用户ID)
models.UserInfo.objects.prefetch_related('外键字段')
from django.db.models import Count, Case, When, IntegerField
Article.objects.annotate(
numviews=Count(Case(
When(readership__what_time__lt=treshold, then=1),
output_field=CharField(),
)))
students = Student.objects.all().annotate(num_excused_absences=models.Sum(
models.Case(
models.When(absence__type='Excused', then=1),
default=0,
output_field=models.IntegerField()
)))
Meta 元数据
在每个模型类的里面我们还可以定义一个子类Meta,这个子类可以定义一些有关数据库或者数据表的相关信息,这些相关信息我们称之为元数据。
强调:每个模型都可以有自己的元数据类,每个元数据类也只对自己所在模型起作用。
字段介绍
# 让其他的类来继承这个基类
class BaseModel(models.Model):
'''定义模型抽象基类'''
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
uptate_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
is_delete = models.BooleanField(default=False, verbose_name='删除标记')
class Meta:
abstract = True # 说明是一个抽象模型类
# 其他字段说明:
db_table = "table_name" # 自定义数据库中生成的表名称
app_label='app_name' # app_label声明属于哪个应用
verbose_name = "name"
# 给这个模型取一个更简单、好读的名字,用于在各种打印、页面展示等场景,可以用中文。
verbose_name_plural = verbose_name
# 这个就是模型对象的复数名,比如“apples”。因为我们中文通常不区分单复数,所以保持和verbose_name一致也可以。
ordering = [“需要升序排列的字段名”]
# 注意:等号右边必须以元组或者列表的形式。这样的话也可以定义多个字段的排序了。
index_together = [ ("字段1", "字段2"),] # 联合索引
unique_together = (("字段1", "字段2"),) # 联合唯一索引 等同于数据库的联合约束!
Django使用多个数据库
一、定义数据库
使用Django的多个数据库的第一步是告诉Django将使用的数据库服务器。 这是使用DATABASES设置完成的。 此设置将数据库别名映射到该特定连接的设置字典,该数据库别名是一种在整个Django中引用特定数据库的方法。 内部词典中的设置在DATABASES文档中有完整描述。
数据库可以包含您选择的任何别名。 当没有选择其他数据库时,Django使用具有默认别名default的数据库。
比如需求:
- 在进行django项目开发的时候,遇到了需要连接两个MySQL数据库的问题。同时使用django自带的admin进行后台数据管理。针对django项目中有多个app,app之间使用不同数据库的需求。
实现步骤:
-
设置路由,将app映射到相对应的数据库
在settings.py文件同目录下,创建一个database_router.py文件(文件名自定义),添加内容如下:
点击查看代码
# -*- coding: utf-8 -*-
from django.conf import settings
DATABASE_MAPPING = settings.DATABASE_APPS_MAPPING
class DatabaseAppsRouter(object):
"""
A router to control all database operations on models for different
databases.
In case an app is not set in settings.DATABASE_APPS_MAPPING, the router
will fallback to the `default` database.
Settings example:
DATABASE_APPS_MAPPING = {'app1': 'db1', 'app2': 'db2'}
"""
def db_for_read(self, model, **hints):
""""
应用于读取类型对象的数据库模型,如果数据库提供附加信息会在hints字典中提供,最后如果没有则返回None
"""
if model._meta.app_label in DATABASE_MAPPING:
return DATABASE_MAPPING[model._meta.app_label]
return None
def db_for_write(self, model, **hints):
"""
应用于写入类型对象的数据库模型,hints字典提供附加信息,如果没有则返回None
"""
if model._meta.app_label in DATABASE_MAPPING:
return DATABASE_MAPPING[model._meta.app_label]
return None
def allow_relation(self, obj1, obj2, **hints):
"""
外键操作,判断两个对象之间是否是应该允许关系,是返回True,否则返回False,如果路由允许返回None
"""
db_obj1 = DATABASE_MAPPING.get(obj1._meta.app_label)
db_obj2 = DATABASE_MAPPING.get(obj2._meta.app_label)
if db_obj1 and db_obj2:
if db_obj1 == db_obj2:
return True
else:
return False
return None
# for Django 1.4 - Django 1.6
def allow_syncdb(self, db, model):
"""
Make sure that apps only appear in the related database.
"""
if db in DATABASE_MAPPING.values():
return DATABASE_MAPPING.get(model._meta.app_label) == db
elif model._meta.app_label in DATABASE_MAPPING:
return False
return None
# Django 1.7 - Django 1.11
def allow_migrate(self, db, app_label, model_name=None, **hints):
"""
db确定是否允许在具有别名的数据库上运行迁移操作,操作运行返回True,否则返回False,或者返回None,如果路由器没有意见。
app_label:位置参数是正在迁移的应用程序的标签。
model_name:多个迁移操作设置模型的值,如:model._meta.app_label
"""
if db in DATABASE_MAPPING.values():
return DATABASE_MAPPING.get(app_label) == db
elif app_label in DATABASE_MAPPING:
return False
return None
- 在settings.py 中的
DATABASES
配置:
settings
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
},
'db02': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'app02',
'USER': 'root',
'PASSWORD': '',
'HOST': '127.0.0.1',
'PORT': 3306,
},
'db03': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'app03',
'USER': 'root',
'PASSWORD': '',
'HOST': '127.0.0.1',
'PORT': 3306,
}
}
注意:其中default可以为空,但是不能删除,db02, db03 是需要配置的数据库连接信息。
settings.py 配置其他内容:
settings
# 数据库路由
DATABASE_ROUTERS = ['project_name.database_router.DatabaseAppsRouter'] # 路径
# 根据app名称路由指定的数据库
DATABASE_APPS_MAPPING = {
# example:
# 'app_name':'database_name',
'app02': 'db02',
'app03': 'db03',
}
- 为每个app的model分别指定所需要连接的数据库:
model
# app02/model.py
class UserInfoApp02(models.Model):
username=models.CharField(max_length=32)
password=models.CharField(max_length=64)
class Meta:
app_label = 'app02' #定义该model的app_label
- 执行数据库迁移命令,可以通过--database指定迁移使用的数据库
python manage.py makemigrations
python manage.py migrate --database=db02
- 执行后数据库建表情况:
二、跨数据库操作
使用using()指定查询的数据库的别名:
# 在查询的语句后面用 using(dbname) 来指定要操作的数据库即可
task = User.objects.using('db02').filter(userId = 1)
# 保存 或 删除
user_obj.save(using='db02')
user_obj.delete(using='db03')
三、数据导入导出
使用的时候和一个数据库的区别是:
如果不是defalut(默认数据库)要在命令后边加 --database=数据库对应的settings.py中的名称 如: --database=db1 或 --database=db2
- 数据库同步(创建表)
python manage.py migrate --database=db1
- 数据导出
python manage.py dumpdata app1 --database=db1 > app1_fixture.json
python manage.py dumpdata app2 --database=db2 > app2_fixture.json
python manage.py dumpdata auth > auth_fixture.json
- 数据库导入
python manage.py loaddata app1_fixture.json --database=db1
python manage.py loaddata app2_fixture.json --database=db2
使用Django中的admin操作指定的数据库
在admin.py中通过下述代码告诉django在处理这些model的orm关系时,使用settings里配置的哪个数据库进行连接。然后再与正常一样进行使用。
admin
class MultiDBModelAdmin(admin.ModelAdmin):
# A handy constant for the name of the alternate database.
using = 'db02' #指定使用的数据库
def save_model(self, request, obj, form, change):
# Tell Django to save objects to the 'other' database.
obj.save(using=self.using)
def delete_model(self, request, obj):
# Tell Django to delete objects from the 'other' database
obj.delete(using=self.using)
def get_queryset(self, request):
# Tell Django to look for objects on the 'other' database.
return super(MultiDBModelAdmin, self).get_queryset(request).using(self.using)
def formfield_for_foreignkey(self, db_field, request, **kwargs):
# Tell Django to populate ForeignKey widgets using a query
# on the 'other' database.
return super(MultiDBModelAdmin, self).formfield_for_foreignkey(db_field, request, using=self.using, **kwargs)
def formfield_for_manytomany(self, db_field, request, **kwargs):
# Tell Django to populate ManyToMany widgets using a query
# on the 'other' database.
return super(MultiDBModelAdmin, self).formfield_for_manytomany(db_field, request, using=self.using, **kwargs)
class UserProfileAdmin(MultiDBModelAdmin):
model = CraCrawl
admin.site.register(UserProfile, UserProfileAdmin) #注册模型到admin后台管理页面