Django ORM models操作



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('ut') 里面是外鍵,可以加多個
  • 在查詢的時候進行連表
    user_list = models.UserInfo.objects.all().select_related('ut')
    for item in user_list:
        print(item.age,item.name,item.ut.title)
  • 實際上連表對性能有影響,連表是約束和節省內存
  • 這里做了兩次查詢,先查UserInfo,之后查UserType,這種查詢非常快
    user_list = models.UserInfo.objects.all().prefetch_related('ut')
    for item in user_list:
        print(item.age,item.name,item.ut.title)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM