title: Django ORM models操作 tags: Django
Django ORM models操作
Django ORM基本操作
一、數據庫的創建及增刪改查
1 使用類創建表
- 在models.py中
from django.db import models - 繼承models.Model
1 寫類,對應表名
from django.db import models
class UserInfo(models.Model):
name = models.CharField(max_length=16)
age = models.IntegerField()
- 設置外鍵 ut = models.ForeignKey('UserType')
- 設置外鍵可以為空, null=True ut = models.ForeignKey('UserType',null=True)
- 生成的外鍵在數據庫中是ut_id,自動添加了下划線+id
class UserType(models.Model):
"""
用戶組
"""
title = models.CharField(max_length=32)
def __str__(self):
return "%s-%s" % (self.id, self.title)
class UserInfo(models.Model):
"""
用戶表
"""
name = models.CharField(max_length=16)
age = models.IntegerField()
ut = models.ForeignKey('UserType',null=True) # 設置外鍵
2 注冊app
在setting中注冊app的名字,下面的都會在數據庫中生成表,都有相應的用處
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01',
]
3 生成表
python3 manage.py makemigratons
python3 manage.py migrate
2 修改數據庫
如果數據庫中有數據,想添加一列的時候,數據庫會提示。。。,解決方式:
- null=True 設置默認為空
- default=1 設置默認值為1
from django.db import models
class UserInfo(models.Model):
name = models.CharField(max_length=16)
age = models.IntegerField()
email = models.CharField(null=True)
email = models.CharField(default=1)
增刪改查
# 增加數據
models.UserInfo.objects.create(name='aaa',age='18',ut_id='1') # 注意外鍵是一行對象,用表中額ut_id
# 查詢數據
models.UserInfo.objects.all()
# 過濾
models.UserInfo.objects.filter(name='xxx')
# 更新
models.UserInfo.objects.update(name='ddd')
二、連表操作
1 外鍵連表
連一張表的情況
UserType表:

UserInfo表: 
下面是通過UserInfo表中的外鍵ut,此時的ut就代表UserType這個表的一行對象,直接用item.ut.title就可以進行連表查詢。
def test(request):
user_list = models.UserInfo.objects.all()
for item in user_list:
print(item.age,item.name,item.ut_id,item.ut.title) # 通過外鍵進行連表
查看原生SQL語句
使用query進行查看,可以查看models的SQL操作。
user_list = models.UserInfo.objects.all()
print(user_list.query)
SELECT "app01_userinfo"."id", "app01_userinfo"."name", "app01_userinfo"."age", "app01_userinfo"."ut_id" FROM "app01_userinfo"
連多張表的情況
class UserType(models.Model):
"""
用戶類型
"""
title = models.CharField(max_length=32)
fo = models.ForeignKey('Foo')
在UserType表中添加外鍵fo,關聯Foo表

使用item.ut.fo.caption可以通過外鍵一致點。
def test(request):
user_list = models.UserInfo.objects.all()
for item in user_list:
print(item.age,item.name,item.ut_id,item.ut.fo.caption)
2 正向操作
- 一個用戶只有一個用戶類型
- 正向操作關鍵,含有FK,直接點的
obj = models.UserInfo.objects.all().first() # 這是獲取的第一個對象
print(obj.name,obj.age,obj.ut.title)
3 反向操作
- 一個用戶類型有多個用戶
- 使用的前提是獲得是一個對象,然后用這個對象進行小寫的表名字_set
- 要關聯的表的小寫的表明加下划線set
- 如UserType反向去取UserInfo中的內容,userinfo_set.all()
obj = models.UserType.objects.all().first()
print(obj.id,obj.title,obj.userinfo_set.all())
結果:通過反向查表獲得是另一個表的QuerySet對象
1 技術部 <QuerySet [<UserInfo: UserInfo object>]>
下面可以通過UserType反向查詢到UserInfo表中的內容,當然還可以用外鍵進行連表,就沒有意義了
obj = models.UserType.objects.all().first()
for item in obj.userinfo_set.all():
print(item.id,item.name,item.age)
或者
user_list = models.UserType.objects.all()
for obj in user_list:
for obj2 in obj.userinfo_set.all(): # 再進行循環
print(obj2.name,obj2.age)
4 values和value_list
values
- values獲得是Quseryset對象,內部的數據代表獲得的列
- 數據類型是字典類型
result = models.UserInfo.objects.all().values('id','name') # 拿到的是兩列數據
for item in result:
print(item)
結果是字典類型
{'id': 1, 'name': 'aaa'}
{'id': 2, 'name': 'bbb'}
用values進行跨表:
- 這種不是在for循環的時候跨表了,是在查詢的時候進行的跨表,然后對獲取的數據進行的遍歷,效率高
- 使用外鍵+雙下划線+字段名
result = models.UserInfo.objects.all().values('id', 'name','ut__title') # 拿到的是兩列數據
for item in result:
print(item['id'],item['name'],item['ut__title'])
value_list
- value_list的數據是元組類型
result = models.UserInfo.objects.all().values_list('id', 'name')
for item in result:
print(item)
結果:
(1, 'aaa')
(2, 'bbb')
- 使用values_list進行跨表,也是在查詢的時候獲取
- 數據是元組,通過雙下划線
- 是通過索引進行取值的
result = models.UserInfo.objects.all().values_list('id', 'name','ut__title') # 拿到的是兩列數據
for item in result:
print(item[0],item[1],item[2])
5 values和value_list 反向操作
使用小寫的表名+雙下划線
v = models.UserGroup.objects.values('id','title','userinfo__age') # userinfo__age
v = models.UserGroup.objects.values_list('id','title','userinfo__age')
跨表操作大總結
class UserGroup(models.Model):
"""
部門
"""
title = models.CharField(max_length=32)
class UserInfo(models.Model):
"""
員工
"""
nid = models.BigAutoField(primary_key=True)
user = models.CharField(max_length=32)
password = models.CharField(max_length=64)
age = models.IntegerField(default=1)
ug = models.ForeignKey("UserGroup",null=True)
- 跨表
正:
1. q = UserInfo.objects.all().first()
q.ug.title 使用外鍵點
2.
models.UserInfo.objects.values('nid','ug_id')
models.UserInfo.objects.values('nid','ug_id','ug__title') # 使用外鍵__+字段
3. models.UserInfo.objects.values_list('nid','ug_id','ug__title')
反:
1. 小寫的表名_set
obj = UserGroup.objects.all().first()
result = obj.userinfo_set.all() [userinfo對象,userinfo對象,]
2. 小寫的表名
v = models.UserGroup.objects.values('id','title')
v = models.UserGroup.objects.values('id','title','小寫的表名稱') # userinfo
v = models.UserGroup.objects.values('id','title','小寫的表名稱__age') # userinfo__age
v = models.UserGroup.objects.values('id','title','小寫的表名稱__ug_id') # 關聯的是字段外鍵ug_id
3. 小寫的表名
v = models.UserGroup.objects.values_list('id','title')
v = models.UserGroup.objects.values_list('id','title','小寫的表名稱')
v = models.UserGroup.objects.values_list('id','title','小寫的表名稱__age')
v = models.UserGroup.objects.values_list('id','title','小寫的表名稱__ug_id') # 關聯的是字段外鍵ug_id
related_name=None, # 反向操作時,使用的字段名,用於代替 【表名_set】 如: obj.表名_set.all()
OnetoOne 一對一 操作
假如用戶模型中有個 Person 模型,它通過屬性 user=models.OneToOneField(User) 與框架的 User 建立起一對一映射。在使用時 user 是一個 User 對象,user.person 可以獲得對應的 Person 對象。
進階操作
排序
- order_by進行排序
- 如有order_by('-id','name') 先排id,id有相同的再拍name
- 加減號是反向排序
user_list = models.UserInfo.objects.all().order_by('-id')
print(user_list)
結果:
<QuerySet [<UserInfo: 2-bbb>, <UserInfo: 1-aaa>]>
分組
- 分組用values和annotate
- values是獲取一列數據
- annotate是加聚合函數的
- 內部用Count
- c是別名
- 導入from django.db.models.aggregates import Count,Sum,Max,Min
- 設置了自動導入模塊 快捷鍵是alt+M
obj = models.UserInfo.objects.all().values_list('ut__id').annotate(c=Count('id'))
print(obj.query)
原生的SQL語句:
SELECT "app01_userinfo"."ut_id", COUNT("app01_userinfo"."id") AS "c" FROM "app01_userinfo" GROUP BY "app01_userinfo"."ut_id"
分組后篩選
- SQL中是Having
- 這里用filter,而且是在后面
- filter在前面是Where
obj = models.UserInfo.objects.all().values_list('ut__id').annotate(c=Count('id')).filter(c__gt=1)
print(obj.query)
SQL:
SELECT "app01_userinfo"."ut_id", COUNT("app01_userinfo"."id") AS "c" FROM "app01_userinfo" GROUP BY "app01_userinfo"."ut_id" HAVING COUNT("app01_userinfo"."id") > 1
filter放在前面的時候
obj = models.UserInfo.objects.filter(id__gt=2).values_list('ut__id').annotate(c=Count('id')).filter(c__gt=1)
print(obj.query)
sql
SELECT "app01_userinfo"."ut_id", COUNT("app01_userinfo"."id") AS "c" FROM "app01_userinfo" WHERE "app01_userinfo"."id" > 2 GROUP BY "app01_userinfo"."ut_id" HAVING COUNT("app01_userinfo"."id") > 1
過濾
- __gt大於
- __lt小於
- in是范圍,后面是列表
- range就是between and
- __startswith 以xxxx開頭
- __conains 包含xxxx
- exclude(id=1) 處id=1的之外
models.UserInfo.objects.filter(id__gt=1)
models.UserInfo.objects.filter(id__lt=1)
models.UserInfo.objects.filter(id__lte=1)
models.UserInfo.objects.filter(id__gte=1)
models.UserInfo.objects.filter(id__in=[1,2,3])
models.UserInfo.objects.filter(id__range=[1,2])
models.UserInfo.objects.filter(name__startswith='xxxx')
models.UserInfo.objects.filter(name__contains='xxxx')
models.UserInfo.objects.exclude(id=1)
高級操作
F
- F是在原來的基礎上進行的操作,下面是對所有用戶的age+1
- 在sql中是 age = age + 1
from django.db.models.expressions import F
models.UserInfo.objects.all().update(age=F('age')+1)
Q
Q的應用1
- 在這里Q的作用是filter中的條件
- 解決了或的問題
obj = models.UserInfo.objects.filter(Q(id=1))
obj = models.UserInfo.objects.filter(Q(id=1)|Q(id=2))
obj = models.UserInfo.objects.filter(Q(id=1)&Q(id=2))
Q的應用2
- 應用於AutoCMDB中的資產管理的組合搜索
- 多個條件可以寫成字典類型,然后filter(**字典) 進行接收
q1 = Q() # 建立一個Q對象
q1.connector = 'OR' # q1內部是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))
q3 = Q()
q3.connector = 'AND'
q3.children.append(('id', 1))
q3.children.append(('id', 2))
q1.add(q3,'OR') # 在q1中添加新的關系q3
con = Q() # 這是總的Q
con.add(q1, 'AND')
con.add(q2, 'AND')
"""
(id=1 or id = 10 or id=9 or (id=1 and id=2)) and (c1=1 or c1=10 or c1=9)
"""
實際應用中的代碼
- 對於同一種類別,q內部的都是or的關系
condition_dict = {
'k1':[1,2,3,4],
'k2':[1,],
}
con = Q() # 總的條件
for k,v in condition_dict.items():
q = Q()
q.connector = 'OR' # q內部的都是or的關系
for i in v:
q.children.append(('id', i))
con.add(q,'AND') # 總的條件用and
models.UserInfo.objects.filter(con)
extra
extra內部的有:
- select select_params
- where params
- tables
- order_by
models.UserInfo.objects.extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
extra是針對復雜的SQL語句
select select_params
v = models.UserInfo.objects.all().extra(select={
'n': "select count(1) from app01_userinfo WHERE id>%s"
},
select_params=[1,] # 有多個% 內部就有多個參數
)
print(v)
for item in v:
print(item.id,item.name,item.n)# 這里使用n
where params
models.UserInfo.objects.extra(
where=["id=1","name='aaa'"]
)
models.UserInfo.objects.extra(
where=["id=1 or id=%s ","name=%s"],
params=[1,"aaa"]
)
tables
models.UserInfo.objects.extra(
tables=['app01_usertype'],
)
# """select * from app01_userinfo,app01_usertype"""
總結:
# a. 映射
# select
# select_params=None
# select 此處 from 表
# b. 條件
# where=None
# params=None,
# select * from 表 where 此處
# c. 表
# tables
# select * from 表,此處
# c. 排序
# order_by=None
# select * from 表 order by 此處
models.UserInfo.objects.extra(
select={'newid':'select count(1) from app01_usertype where id>%s'},
select_params=[1,],
where = ['age>%s'],
params=[18,],
order_by=['-age'],
tables=['app01_usertype']
)
最終的SQL:
select
app01_userinfo.id,
(select count(1) from app01_usertype where id>1) as newid
from app01_userinfo,app01_usertype
where
app01_userinfo.age > 18
order by
app01_userinfo.age desc
原生SQL
Django內部提供了寫原生SQL的方法
- 在setting中配置
- connection.cursor()默認是default數據庫
- cursor = connections['db2'].cursor() 可以定義自己的數據庫
from django.db import connection, connections
cursor = connection.cursor() #默認 connection=default數據庫
cursor = connections['db2'].cursor()
cursor.execute("""SELECT * from auth_user where id = %s""", [1])# 寫原生SQL
row = cursor.fetchone()
row = cursor.fetchall()
其他
時間相關,時間格式化
def dates(self, field_name, kind, order='ASC'):
# 根據時間進行某一部分進行去重查找並截取指定內容
# kind只能是:"year"(年), "month"(年-月), "day"(年-月-日)
# order只能是:"ASC" "DESC"
# 並獲取轉換后的時間
- year : 年-01-01 # 截取到年,后面默認是01-01
- month: 年-月-01 # 截取到月,后面默認是01
- day : 年-月-日
models.DatePlus.objects.dates('ctime','day','DESC') # ctime是時間列 day,代表截取到day DESC對截取后的時間進行排序
def datetimes(self, field_name, kind, order='ASC', tzinfo=None):
# 根據時間進行某一部分進行去重查找並截取指定內容,將時間轉換為指定時區時間
# kind只能是 "year", "month", "day", "hour", "minute", "second"
# order只能是:"ASC" "DESC"
# tzinfo時區對象
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’)
"""
- 更新數據后,create有返回值,是添加的數據的id
- 使用**字典進行接收
obj = models.UserType.objects.create(title='xxx')
obj = models.UserType.objects.create(**{'title': 'xxx'})
print(obj.id) # 返回值
v = models.UserInfo.objects.all().first() # 推薦使用first
models.UserInfo.objects.get(id=1) # get沒有獲取到會報錯
- 批量操作
- odels.Userinfo(name='r11'), # 注意這是對象 沒有用objects
bulk_create(self, objs, batch_size=None):
# 批量插入
# batch_size表示一次插入的個數
objs = [
models.Userinfo(name='r11'), # 注意這是對象 沒有用objects
models.Userinfo(name='r22')
]
models.Userinfo.objects.bulk_create(objs, 10) # 一次性提交的數據10個,100條數據會沒10條提交
def get_or_create(self, defaults=None, **kwargs):
# 如果存在,則獲取,否則,創建
# defaults 指定創建時,其他字段的值
obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2})
def update_or_create(self, defaults=None, **kwargs):
# 如果存在,則更新,否則,創建
# defaults 指定創建時或更新時的其他字段
obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 1})
性能相關
下面的這種情況每次for循環都會到數據庫查詢,性能低。使用values,values_list是一次查詢,但是查詢的不是對象,是字典列表類型
user_list = models.UserInfo.objects.all()
for item in user_list:
print(item.age,item.name,item.ut.title)
select_related 查詢主動做連表
- 使用select_related('ut') 里面是外鍵,可以加多個
- 在查詢的時候進行連表
user_list = models.UserInfo.objects.all().select_related('ut')
for item in user_list:
print(item.age,item.name,item.ut.title)
prefetch_related 不做連表 做多次查詢
- 實際上連表對性能有影響,連表是約束和節省內存
- 這里做了兩次查詢,先查UserInfo,之后查UserType,這種查詢非常快
user_list = models.UserInfo.objects.all().prefetch_related('ut')
for item in user_list:
print(item.age,item.name,item.ut.title)
