Django ORM那些相關操作


一般操作

看專業的官網文檔,做專業的程序員!

必知必會13條

復制代碼
<1> all(): 查詢所有結果 <2> filter(**kwargs): 它包含了與所給篩選條件相匹配的對象 <3> get(**kwargs): 返回與所給篩選條件相匹配的對象,返回結果有且只有一個,如果符合篩選條件的對象超過一個或者沒有都會拋出錯誤。 <4> exclude(**kwargs): 它包含了與所給篩選條件不匹配的對象 <5> values(*field): 返回一個ValueQuerySet——一個特殊的QuerySet,運行后得到的並不是一系列model的實例化對象,而是一個可迭代的字典序列 <6> values_list(*field): 它與values()非常相似,它返回的是一個元組序列,values返回的是一個字典序列 <7> order_by(*field): 對查詢結果排序 <8> reverse(): 對查詢結果反向排序,請注意reverse()通常只能在具有已定義順序的QuerySet上調用(在model類的Meta中指定ordering或調用order_by()方法)。 <9> distinct(): 從返回結果中剔除重復紀錄(如果你查詢跨越多個表,可能在計算QuerySet時得到重復的結果。此時可以使用distinct(),注意只有在PostgreSQL中支持按字段去重。) <10> count(): 返回數據庫中匹配查詢(QuerySet)的對象數量。 <11> first(): 返回第一條記錄 <12> last(): 返回最后一條記錄 <13> exists(): 如果QuerySet包含數據,就返回True,否則返回False
復制代碼

備注:

1.使用order_by排序時,在字段名前加"-"表示倒序,等同於reverse

 

返回QuerySet對象的方法有

all()

filter()

exclude()

order_by()

reverse()

distinct()

特殊的QuerySet

values()       返回一個可迭代的字典序列

values_list() 返回一個可迭代的元祖序列

返回具體對象的

get()

first()

last()

返回布爾值的方法有:

exists()

返回數字的方法有

count()

單表查詢之神奇的雙下划線

復制代碼
models.Tb1.objects.filter(id__lt=10, id__gt=1) # 獲取id大於1 且 小於10的值 models.Tb1.objects.filter(id__in=[11, 22, 33]) # 獲取id等於11、22、33的數據 models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in models.Tb1.objects.filter(name__contains="ven") # 獲取name字段包含"ven"的 models.Tb1.objects.filter(name__icontains="ven") # icontains大小寫不敏感 models.Tb1.objects.filter(id__range=[1, 3]) # id范圍是1到3的,等價於SQL的bettwen and 類似的還有:startswith,istartswith, endswith, iendswith  date字段還可以: models.Class.objects.filter(first_day__year=2017)
復制代碼

ForeignKey操作

正向查找

對象查找(跨表)

語法:

對象.關聯字段.字段

 

示例:

book_obj = models.Book.objects.first()  # 第一本書對象 print(book_obj.publisher) # 得到這本書關聯的出版社對象 print(book_obj.publisher.name) # 得到出版社對象的名稱

字段查找(跨表)

語法:

關聯字段__字段

 備注:雙下划線就表示跨一張表。

示例:

print(models.Book.objects.values_list("publisher__name"))

反向操作

對象查找

語法:

obj.表名_set

示例:

publisher_obj = models.Publisher.objects.first()  # 找到第一個出版社對象 books = publisher_obj.book_set.all() # 找到第一個出版社出版的所有書 titles = books.values_list("title") # 找到第一個出版社出版的所有書的書名

字段查找

語法:

表名__字段

示例:

titles = models.Publisher.objects.values_list("book__title")

備注:

1.外鍵查找時,不管是正向查找還是反向查找,在使用對象查找時,必須得是一個具體的對象(使用get、first、last),有什么屬性就:對象.屬性;在使用字段查找時,必須是一個QuerySet對象(使用filter),才能使用 .values()和.values_list()方法。

2.使用related_name='xx'和related_query_name='xx'時,related_name用於反向查詢時代替 : 表名_set,而related_query_name用於反向雙下划線跨表查詢時,代替表名的。也用於,當反向查詢的表是動態的(多個時),用來代替動態的表名。

3.使用外鍵創建一對多表關系時,外鍵通常設置在多的一邊。

使用ManyToMany創建多對多表關系時,通常設置在正向查詢多的那一邊。

4.使用基於對象查詢時,內部的SQL語句是一個子查詢形式的SQL語句,基於跨表的雙下查詢(基於queryset)時,內部的SQL語句是一個連表的查詢(join  ...  on ...),所以對於查詢效率來說,做好還是使用基於雙下划線的查詢,少用基於對象的查詢。

 

ManyToManyField

class RelatedManager

"關聯管理器"是在一對多或者多對多的關聯上下文中使用的管理器。

它存在於下面兩種情況:

  1. 外鍵關系的反向查詢
  2. 多對多關聯關系

簡單來說就是當 點后面的對象 可能存在多個的時候就可以使用以下的方法。

方法

create()

創建一個新的對象,保存對象,並將它添加到關聯對象集之中,返回新創建的對象。

>>> import datetime
>>> models.Author.objects.first().book_set.create(title="番茄物語", publish_date=datetime.date.today())

add()

把指定的model對象添加到關聯對象集中。

添加對象

>>> author_objs = models.Author.objects.filter(id__lt=3)
>>> models.Book.objects.first().authors.add(*author_objs)

添加id

>>> models.Book.objects.first().authors.add(*[1, 2])

備注:使用add添加時,如果對象為多個,則在對象前加*(列表)或**(字典),如果為一個具體的id值,則直接將值填寫。

set()

更新model對象的關聯對象。

>>> book_obj = models.Book.objects.first()
>>> book_obj.authors.set([2, 3])

備注:使用set()時,更新對象為多個時,不用在列表前加*,使用set更新時是先清空在添加

remove()

從關聯對象集中移除執行的model對象

>>> book_obj = models.Book.objects.first()
>>> book_obj.authors.remove(3)

clear()

從關聯對象集中移除一切對象。

>>> book_obj = models.Book.objects.first()
>>> book_obj.authors.clear()

注意:

對於ForeignKey對象,clear()和remove()方法僅在null=True時存在。

舉個例子:

ForeignKey字段沒設置null=True時,

class Book(models.Model):
    title = models.CharField(max_length=32)
    publisher = models.ForeignKey(to=Publisher)

沒有clear()和remove()方法:

>>> models.Publisher.objects.first().book_set.clear()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
AttributeError: 'RelatedManager' object has no attribute 'clear'

當ForeignKey字段設置null=True時,

class Book(models.Model):
    name = models.CharField(max_length=32)
    publisher = models.ForeignKey(to=Class, null=True)

此時就有clear()和remove()方法:

>>> models.Publisher.objects.first().book_set.clear()

 

注意:

  1. 對於所有類型的關聯字段,add()、create()、remove()和clear(),set()都會馬上更新數據庫。換句話說,在關聯的任何一端,都不需要再調用save()方法。

三種方式創建多對多外鍵方式

通過外鍵創建

復制代碼
class Class(models.Model): id = models.AutoField(primary_key=True) # 主鍵 cname = models.CharField(max_length=32) # 班級名稱 first_day = models.DateField() # 開班時間 class Teacher(models.Model): tname = models.CharField(max_length=32) # 自定義第三張表,通過外鍵關聯上面兩張表 class Teacher2Class(models.Model): teacher = models.ForeignKey(to="Teacher") the_class = models.ForeignKey(to="Class") class Meta: unique_together = ("teacher", "the_class")
復制代碼

通過ManyToManyField創建

復制代碼
class Class(models.Model): id = models.AutoField(primary_key=True) # 主鍵 cname = models.CharField(max_length=32) # 班級名稱 first_day = models.DateField() # 開班時間 class Teacher(models.Model): tname = models.CharField(max_length=32) # 通過ManyToManyField自動創建第三張表 cid = models.ManyToManyField(to="Class", related_name="teachers")
復制代碼

通過外鍵和ManyToManyField創建

復制代碼
class Class(models.Model): id = models.AutoField(primary_key=True) # 主鍵 cname = models.CharField(max_length=32) # 班級名稱 first_day = models.DateField() # 開班時間 class Teacher(models.Model): tname = models.CharField(max_length=32) # 通過ManyToManyField和手動創建第三張表 cid = models.ManyToManyField(to="Class", through="Teacher2Class", through_fields=("teacher", "the_class")) class Teacher2Class(models.Model): teacher = models.ForeignKey(to="Teacher") the_class = models.ForeignKey(to="Class") class Meta: unique_together = ("teacher", "the_class")
復制代碼

 備注(特別重要):

1.自己創建第三張表,利用外鍵分別關聯這兩張表時,使用這種方法時,我們一定要對對聯的兩個表的id字段進行設置聯合索引,來保證不可重復。即class Meta:unique_together=('關聯字段名1','關聯字段名2')。使用此種方法時。缺點是:關聯查詢比較麻煩,因為沒有辦法使用ORM提供的便利方法。

2.在使用外鍵和ManyToMany指定關聯的表時,和使用ORM自動幫我們創建的第三張表的區別:

  如果第三張表除了關聯字段和id字段外沒有額外的字段時,使用ORM幫我們創建的第三張表的這種方式,如果第三張表中有額外的字段時,就使用指定表的這種方式,比如:QQ中加好友時,除了雙方的關聯id外還有其他字段諸如:加好友的時間等。

3.在使用外鍵和ManyToMany來創建多對多的表關系時,通過through和through_fields 來指定使用自己創建的第三張表來構建多對多的關系。而且,使用這種方式時:沒有 add() 和 remove()等方法,其他的查詢方法跟正常的ORM使用方法一樣。也沒有Django ORM封裝的那些快捷方式,我們要自己親自改第三張表。里面的through_fields屬性為指定字段,為元組形式,指定字段時是有順序的,順序為在哪張表里設定ManyToMany屬性,這張表的關聯字段就在最前面。

 

聚合查詢和分組查詢

聚合

aggregate()QuerySet 的一個終止子句,意思是說,它返回一個包含一些鍵值對的字典。

鍵的名稱是聚合值的標識符,值是計算出來的聚合值。鍵的名稱是按照字段和聚合函數的名稱自動生成出來的。

用到的內置函數:

from django.db.models import Avg, Sum, Max, Min, Count

示例:

>>> models.Book.objects.all().aggregate(Avg("price"))
{'price__avg': 13.233333}

如果你想要為聚合值指定一個名稱,可以向聚合子句提供它。

>>> models.Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 13.233333}

如果你希望生成不止一個聚合,你可以向aggregate()子句中添加另一個參數。所以,如果你也想知道所有圖書價格的最大值和最小值,可以這樣查詢:

>>> models.Book.objects.all().aggregate(Avg("price"), Max("price"), Min("price"))
{'price__avg': 13.233333, 'price__max': Decimal('19.90'), 'price__min': Decimal('9.90')}

分組

我們在這里先復習一下SQL語句的分組。

假設現在有一張公司職員表:

 

 我們使用原生SQL語句,按照部分分組求平均工資:

select dept,AVG(salary) from employee group by dept;

ORM查詢:

from django.db.models import Avg Employee.objects.values("dept").annotate(avg=Avg("salary").values(dept, "avg")

連表查詢的分組:

 

SQL查詢:

select dept.name,AVG(salary) from employee inner join dept on (employee.dept_id=dept.id) group by dept_id;

ORM查詢:

from django.db.models import Avg models.Dept.objects.annotate(avg=Avg("employee__salary")).values("name", "avg")

#或者寫成
models.Employee.objects.values("dept_id").annotate(avg=Avg("salary")).values("dept__name","avg")

備注:在ORM中,values(‘字段’)或者values_list("字段") 代表SQL語句中的要查詢的字段(select dept_name,salary from 表,即select后面的),如果后面跟annotate(分組)時,表示按照什么字段分組。

更多示例:

為調用的QuerySet中每一個對象都生成一個獨立的統計值。

示例1:統計每一本書的作者個數

復制代碼
>>> book_list = models.Book.objects.all().annotate(author_num=Count("author"))
>>> for obj in book_list:
...     print(obj.author_num)
...
2
1
1
復制代碼

示例2:統計出每個出版社買的最便宜的書的價格

復制代碼
>>> publisher_list = models.Publisher.objects.annotate(min_price=Min("book__price"))
>>> for obj in publisher_list:
...     print(obj.min_price)
...     
9.90
19.90
復制代碼

方法二:

>>> models.Book.objects.values("publisher__name").annotate(min_price=Min("price"))
<QuerySet [{'publisher__name': '沙河出版社', 'min_price': Decimal('9.90')}, {'publisher__name': '人民出版社', 'min_price': Decimal('19.90')}]>

示例3:統計不止一個作者的圖書

>>> models.Book.objects.annotate(author_num=Count("author")).filter(author_num__gt=1)
<QuerySet [<Book: 番茄物語>]>

示例4:根據一本圖書作者數量的多少對查詢集 QuerySet進行排序

>>> models.Book.objects.annotate(author_num=Count("author")).order_by("author_num")
<QuerySet [<Book: 香蕉物語>, <Book: 橘子物語>, <Book: 番茄物語>]>

示例5:查詢各個作者出的書的總價格

>>> models.Author.objects.annotate(sum_price=Sum("book__price")).values("name", "sum_price")
<QuerySet [{'name': '小精靈', 'sum_price': Decimal('9.90')}, {'name': '小仙女', 'sum_price': Decimal('29.80')}, {'name': '小魔女', 'sum_price': Decimal('9.90')}]>

F查詢和Q查詢

F查詢

在上面所有的例子中,我們構造的過濾器都只是將字段值與某個常量做比較。如果我們要對兩個字段的值做比較,那該怎么做呢?

Django 提供 F() 來做這樣的比較。F() 的實例可以在查詢中引用字段,來比較同一個 model 實例中兩個不同字段的值。

示例1:

查詢評論數大於收藏數的書籍

from django.db.models import F
models.Book.objects.filter(commnet_num__gt=F('keep_num'))

Django 支持 F() 對象之間以及 F() 對象和常數之間的加減乘除和取模的操作。

models.Book.objects.filter(commnet_num__lt=F('keep_num')*2)

修改操作也可以使用F函數,比如將每一本書的價格提高30元

models.Book.objects.all().update(price=F("price")+30)

引申:

如果要修改char字段咋辦?

如:把所有書名后面加上(第一版)

>>> from django.db.models.functions import Concat
>>> from django.db.models import Value
>>> models.Book.objects.all().update(title=Concat(F("title"), Value("("), Value("第一版"), Value(")")))

備注:update()方法用於修改多個值。具體的某一個對象沒有update()方法,QuerySet對象才有update()方法。

obj = models.Book.objects.exclude(id=2).values('title')
obj.update(title='北京')   #將所有id不為2的title值修改

 

Q查詢

filter() 等方法中的關鍵字參數查詢都是一起進行“AND” 的。 如果你需要執行更復雜的查詢(例如OR語句),你可以使用Q對象

示例1:

查詢作者名是小仙女或小魔女的

models.Book.objects.filter(Q(authors__name="小仙女")|Q(authors__name="小魔女"))

你可以組合& 和|  操作符以及使用括號進行分組來編寫任意復雜的Q 對象。同時,Q 對象可以使用~ 操作符取反,這允許組合正常的查詢和取反(NOT) 查詢。

示例:查詢作者名字是小仙女並且不是2018年出版的書的書名。

>>> models.Book.objects.filter(Q(author__name="小仙女") & ~Q(publish_date__year=2018)).values_list("title")
<QuerySet [('番茄物語',)]>

查詢函數可以混合使用Q 對象和關鍵字參數。所有提供給查詢函數的參數(關鍵字參數或Q 對象)都將"AND”在一起。但是,如果出現Q 對象,它必須位於所有關鍵字參數的前面。

例如:查詢出版年份是2017或2018,書名中帶物語的所有書。

>>> models.Book.objects.filter(Q(publish_date__year=2018) | Q(publish_date__year=2017), title__icontains="物語")
<QuerySet [<Book: 番茄物語>, <Book: 香蕉物語>, <Book: 橘子物語>]>

Q的另一種用法:

此外,Q還有一種用法:這種用法的應用場景比較常見,那就是條件搜索框的實現

我們可以實例化一個q對象q=Q(),這個q對象有一個方法是:q.children.append(("字符串格式的字段名","搜索內容")),特別注意的是,這里我們接收的是一個元組,這個元組的第一個參數是對應搜索內容在模型表中的字段名(字符串格式的)。第二個值是對應的用戶輸入的搜索內容,多個條件時,直接append多個值即可,默認是且(and)的關系,我們可以設置一個參數來改變這個默認的關系:q.connector = 'or',這是多個條件之間的關系就是或的關系了。查詢時我們只需要將這個q對象傳進去即可。

from django.db.models import Q

q = Q()

q.connector = 'or' #修改多條件的處理關系
#假設用戶輸入一個書名(title):圍城,輸入一個價格(price):120 # q.children.append(("title__icontains","圍城")) #篩選書名中包含圍城的書籍對象 q.children.append(("price__icontains","120")) #假設模型表為Book result = Book.objects.filter(q) #得到一個篩選后的條件,返回一個包含符合條件的queryset對象<QuerySet [書籍對象,...]>

備注:使用append添加條件時,每次只能添加一個條件

 備注:如果q對象為空,Book.objects.filter(q) 等同於all()。

 

事務

復制代碼
import os if __name__ == '__main__': os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings") import django django.setup() import datetime from app01 import models try: from django.db import transaction with transaction.atomic(): new_publisher = models.Publisher.objects.create(name="火星出版社") models.Book.objects.create(title="橘子物語", publish_date=datetime.date.today(), publisher_id=10) # 指定一個不存在的出版社id except Exception as e: print(str(e))
復制代碼

 

其他鮮為人知的操作(有個印象即可)

Django ORM執行原生SQL

# extra
# 在QuerySet的基礎上繼續執行子語句
# extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)

# select和select_params是一組,where和params是一組,tables用來設置from哪個表
# 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'])

舉個例子:
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']
                )
                """
                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
# 更高靈活度的方式執行原生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()
ORM執行原生SQL語句方法
 
          
復制代碼

  備注:還可以通過raw方式     models.XXX.objects.raw("select * from xxxx where id>12")

QuerySet方法大全

 

##################################################################
# PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET #
##################################################################

def all(self)
    # 獲取所有的數據對象

def filter(self, *args, **kwargs)
    # 條件查詢
    # 條件可以是:參數,字典,Q

def exclude(self, *args, **kwargs)
    # 條件查詢
    # 條件可以是:參數,字典,Q

def select_related(self, *fields)
    性能相關:表之間進行join連表操作,一次性獲取關聯的數據。

    總結:
    1. select_related主要針一對一和多對一關系進行優化。
    2. select_related使用SQL的JOIN語句進行優化,通過減少SQL查詢的次數來進行優化、提高性能。

def prefetch_related(self, *lookups)
    性能相關:多表連表操作時速度會慢,使用其執行多次SQL查詢在Python代碼中實現連表操作。

    總結:
    1. 對於多對多字段(ManyToManyField)和一對多字段,可以使用prefetch_related()來進行優化。
    2. prefetch_related()的優化方式是分別查詢每個表,然后用Python處理他們之間的關系。

def annotate(self, *args, **kwargs)
    # 用於實現聚合group by查詢

    from django.db.models import Count, Avg, Max, Min, Sum

    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id'))
    # SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id

    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1)
    # SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

    v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1)
    # SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1

def distinct(self, *field_names)
    # 用於distinct去重
    models.UserInfo.objects.values('nid').distinct()
    # select distinct nid from userinfo

    注:只有在PostgreSQL中才能使用distinct進行去重

def order_by(self, *field_names)
    # 用於排序
    models.UserInfo.objects.all().order_by('-id','age')

def 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'])

 def reverse(self):
    # 倒序
    models.UserInfo.objects.all().order_by('-nid').reverse()
    # 注:如果存在order_by,reverse則是倒序,如果多個排序則一一倒序


 def defer(self, *fields):
    models.UserInfo.objects.defer('username','id')
    或
    models.UserInfo.objects.filter(...).defer('username','id')
    #映射中排除某列數據

 def only(self, *fields):
    #僅取某個表中的數據
     models.UserInfo.objects.only('username','id')
     或
     models.UserInfo.objects.filter(...).only('username','id')

 def using(self, alias):
     指定使用的數據庫,參數為別名(setting中的設置)


##################################################
# PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
##################################################

def raw(self, raw_query, params=None, translations=None, using=None):
    # 執行原生SQL
    models.UserInfo.objects.raw('select * from userinfo')

    # 如果SQL是其他表時,必須將名字設置為當前UserInfo對象的主鍵列名
    models.UserInfo.objects.raw('select id as nid from 其他表')

    # 為原生SQL設置參數
    models.UserInfo.objects.raw('select id as nid from 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")

    ################### 原生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() # fetchall()/fetchmany(..)


def values(self, *fields):
    # 獲取每行數據為字典格式

def values_list(self, *fields, **kwargs):
    # 獲取每行數據為元祖

def dates(self, field_name, kind, order='ASC'):
    # 根據時間進行某一部分進行去重查找並截取指定內容
    # kind只能是:"year"(年), "month"(年-月), "day"(年-月-日)
    # order只能是:"ASC"  "DESC"
    # 並獲取轉換后的時間
        - year : 年-01-01
        - month: 年-月-01
        - day  : 年-月-日

    models.DatePlus.objects.dates('ctime','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’)
    """

def none(self):
    # 空QuerySet對象


####################################
# METHODS THAT DO DATABASE QUERIES #
####################################

def aggregate(self, *args, **kwargs):
   # 聚合函數,獲取字典類型聚合結果
   from django.db.models import Count, Avg, Max, Min, Sum
   result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid'))
   ===> {'k': 3, 'n': 4}

def count(self):
   # 獲取個數

def get(self, *args, **kwargs):
   # 獲取單個對象

def create(self, **kwargs):
   # 創建對象

def bulk_create(self, objs, batch_size=None):
    # 批量插入
    # batch_size表示一次插入的個數
    objs = [
        models.DDD(name='r11'),
        models.DDD(name='r22')
    ]
    models.DDD.objects.bulk_create(objs, 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})

def first(self):
   # 獲取第一個

def last(self):
   # 獲取最后一個

def in_bulk(self, id_list=None):
   # 根據主鍵ID進行查找
   id_list = [11,21,31]
   models.DDD.objects.in_bulk(id_list)

def delete(self):
   # 刪除

def update(self, **kwargs):
    # 更新

def exists(self):
   # 是否有結果
QuerySet方法大全

 

備注:1.select_related和prefetch_related 根據不同的情況可以提高查詢的性能。select_related適用於一對一(OneToOne)和多對一(外鍵的正向查詢),先使用select_releted進行連表操作,再做其他操作(比如:models.Person.objects.select_releted().'其他操作')。而prefetch_related 適用於多對多(ManyToMany)和一對多(外鍵的反向查詢),它是將每一張表單獨查詢出來,在通過Python代碼進行相關篩選。

2.使用ORM批量出入數據時,使用bulk_create()。接收兩個參數,一個是批量插入的對象objects,一個為可選參數,每次插入幾條(多少條數據一提交),默認為一次全部插入。models.DDD.objects.bulk_create(objs, 10)

 3.defer()和only()查詢方法的使用,defer()方法提供了延遲加載,也就是利用這個方法我們可以把不展示的字段添加進去。only()方法正好相反,提供我們要展示的字段信息。利用這兩個方法可以很好的優化響應,提高查詢的性能。

orm中配置數據庫讀寫分離的兩種方式

  前提條件是要做好數據庫層面的讀寫分離和數據同步

  第一步、配置數據庫,以sqlite為例, 

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    },
    'db2': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db2.sqlite3'),
    },
}

  第二步、數據庫遷移。 python manage.py migrate 默認使用default的數據配置,完整的寫法為: python manage.py migrate --database default ,同樣的遷移完默認的數據庫后,也要遷移從數據庫  python manage.py migrate --database db2

  第三步、讀寫分離

  方式一:可以使用手動的方式指定數據庫的讀寫,通過一個方法  .using("數據庫")    models.xxx.objects.using("db2").first()

      my_object.save(using='db2') 可以指定存儲的模型表
from django.shortcuts import HttpResponse
from . import models

#指定寫的數據庫 
def write(request):
    models.Products.objects.using('default').create(prod_name='熊貓公仔', prod_price=12.99)
    return HttpResponse('寫入成功')

# 指定讀的數據庫
def read(request):
    obj = models.Products.objects.filter(id=1).using('db2').first()
    return HttpResponse(obj.prod_name)

  方式二:自動讀寫分離

  通過配置數據庫路由,來自動實現,這樣就不需要每次讀寫都手動指定數據庫了。數據庫路由中提供了四個方法(db_for_read、db_for_write、allow_relation、allow_migrate)。這里主要用其中的兩個:def db_for_read()決定讀操作的數據庫,def db_for_write()決定寫操作的數據庫。

定義Router類

新建myrouter.py腳本,定義Router類:

class Router: def db_for_read(self, model, **hints): return 'db2' def db_for_write(self, model, **hints): return 'default'
配置Router

settings.py中指定DATABASE_ROUTERS

DATABASE_ROUTERS = ['myrouter.Router',] 

 

可以指定多個數據庫路由,比如對於讀操作,Django將會循環所有路由中的db_for_read()方法,直到其中一個有返回值,然后使用這個數據庫進行當前操作。

一主多從方案

網站的讀的性能通常更重要,因此,可以多配置幾個數據庫,並在讀取時,隨機選取,比如:

class Router: def db_for_read(self, model, **hints): """ 讀取時隨機選擇一個數據庫 """ import random return random.choice(['db2', 'db3', 'db4']) def db_for_write(self, model, **hints): """ 寫入時選擇主庫 """ return 'default'

 

分庫分表

在大型web項目中,常常會創建多個app來處理不同的業務,如果希望實現app之間的數據庫分離,比如app01走數據庫db1,app02走數據庫

class Router: def db_for_read(self, model, **hints): if model._meta.app_label == 'app01': return 'db1' if model._meta.app_label == 'app02': return 'db2' def db_for_write(self, model, **hints): if model._meta.app_label == 'app01': return 'db1' if model._meta.app_label == 'app02': return 'db2'

 

更多請參考官網

 

Django終端打印SQL語句

在Django項目的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', }, } }
復制代碼

即為你的Django項目配置上一個名為django.db.backends的logger實例即可查看翻譯后的SQL語句。 

備注:查看單個ORM執行的SQL語句時,我們可以對這個ORM語句執行query屬性。注意:query屬性是queryset下的一個屬性,所以我們查看單個語句時,必須保證結果為一個QuerySet對象,否則沒有query屬性。

from blog import models

ret = models.Article.objects.values("title")
print(ret.query)

上面這種方法只適用於queryset對象,對於非queryset就不能使用了,還有一種更標准的輸出:

from pprint import pprint as p
p(connection.queries)

pprint常與connection.queries搭配使用,需要注意的是:

  • 這個是一個單元測試用法
  • 注意點只用在debug = Flase 的情況下才可以用connection.queries打印sql日志
  • 只用在引入from django.db import connection才可以打印日志
  • 在tests 單元測試中默認是 debug = True,所以要引入from django.test.utils import override_settings 寫裝飾器進行配置
[{'sql': 'SELECT @@SQL_AUTO_IS_NULL', 'time': '0.000'},
 {'sql': 'SELECT `blog_article`.`nid`, `blog_article`.`title`, '
         '`blog_article`.`desc`, `blog_article`.`create_time`, '
         '`blog_article`.`comment_count`, `blog_article`.`up_count`, '
         '`blog_article`.`down_count`, `blog_article`.`category_id`, '
         '`blog_article`.`user_id` FROM `blog_article` ORDER BY '
         '`blog_article`.`nid` ASC LIMIT 1',
  'time': '0.000'}]

 

Django項目常用完整版LOGGING配置:

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'standard': {
            'format': '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]'
                      '[%(levelname)s][%(message)s]'
        },
        'simple': {
            'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
        },
        'collect': {
            'format': '%(message)s'
        }
    },
    'filters': {
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'filters': ['require_debug_true'],  # 只有在Django debug為True時才在屏幕打印日志
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'default': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自動切
            'filename': os.path.join(BASE_LOG_DIR, "xxx_info.log"),  # 日志文件
            'maxBytes': 1024 * 1024 * 50,  # 日志大小 50M
            'backupCount': 3,
            'formatter': 'standard',
            'encoding': 'utf-8',
        },
        'error': {
            'level': 'ERROR',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自動切
            'filename': os.path.join(BASE_LOG_DIR, "xxx_err.log"),  # 日志文件
            'maxBytes': 1024 * 1024 * 50,  # 日志大小 50M
            'backupCount': 5,
            'formatter': 'standard',
            'encoding': 'utf-8',
        },
        'collect': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自動切
            'filename': os.path.join(BASE_LOG_DIR, "xxx_collect.log"),
            'maxBytes': 1024 * 1024 * 50,  # 日志大小 50M
            'backupCount': 5,
            'formatter': 'collect',
            'encoding': "utf-8"
        }
    },
    'loggers': {
       # 默認的logger應用如下配置
        '': {
            'handlers': ['default', 'console', 'error'],  # 上線之后可以把'console'移除
            'level': 'DEBUG',
            'propagate': True,
        },
        # 名為 'collect'的logger還單獨處理
        'collect': {
            'handlers': ['console', 'collect'],
            'level': 'INFO',
        }
    },
}
完整配置

 

在Python腳本中調用Django環境

復制代碼
import os if __name__ == '__main__': os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings") import django django.setup() from app01 import models books = models.Book.objects.all() print(books)
復制代碼


免責聲明!

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



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