Django-models進階

Django-models進階

extra

extra(select=None, where=None, params=None, 
      tables=None, order_by=None, select_params=None)

有些狀況下,Django的查詢語法難以簡單的表達複雜的 WHERE 子句,對於這種狀況, Django 提供了 extra() QuerySet修改機制 — 它能在 QuerySet生成的SQL從句中注入新子句java

extra能夠指定一個或多個 參數,例如 selectwhere or tables這些參數都不是必須的,可是你至少要使用一個!要注意這些額外的方式對不一樣的數據庫引擎可能存在移植性問題.(由於你在顯式的書寫SQL語句),除非萬不得已,儘可能避免這樣作python

警告web

不管什麼時候你都須要很是當心的使用extra(). 每次使用它時,您都應該轉義用戶可使用params控制的任何參數,以防止SQL注入攻擊。sql


數之select

The  select  參數可讓你在  SELECT  從句中添加其餘字段信息, 它應該是一個字典,存放着屬性名到 SQL 從句的映射。
queryResult=models.Article
           .objects.extra(select={'is_recent': "create_time > '2017-09-05'"})
結果集中每一個 Entry 對象都有一個額外的屬性is_recent, 它是一個布爾值,表示 Article對象的create_time 是否晚於2017-09-05.


date_list=models.Article.objects.filter(user=user_obj).extra(select={"fdate":"strftime('%%Y/%%m/%%d',create_time)"}).values_list("fdate").annotate(Count("id"))
 '''
 輸出結果:
 <QuerySet [('2017/11/22', 2), ('2017/11/24', 1)]>
 '''

參數之where / tables

您可使用where定義顯式SQL WHERE子句 - 也許執行非顯式鏈接。您可使用tables手動將表添加到SQL FROM子句。數據庫

wheretables都接受字符串列表。全部where參數均爲「與」任何其餘搜索條件。bootstrap

舉例來說:api


queryResult=models.Article
           .objects.extra(where=['nid in (1,3) OR title like "py%" ','nid>2'])

QuerySet

可切片

使用Python 的切片語法來限制查詢集記錄的數目 。它等同於SQL 的LIMIT 和OFFSET 子句。
>>> Entry.objects.all()[:5]      # (LIMIT 5)
不支持負的索引(例如Entry.objects.all()[-1])。一般,查詢集 的切片返回一個新的查詢集 —— 它不會執行查詢。

可迭代

articleList=models.Article.objects.all()

for article in articleList:
    print(article.title)

惰性查詢

查詢集  是惰性執行的 —— 建立 查詢集 不會帶來任何數據庫的訪問。 你能夠將過濾器保持一成天,直到查詢集 須要求值時,Django 纔會真正運行這個查詢。
示例:

def queryTest(request):
    ret=models.Article.objects.all().iterator()
    for i in ret:
        print(i)
    return HttpResponse("ok")
輸出結果:
#打印了就進行查詢
'''
Quit the server with CTRL-BREAK.
(0.001) SELECT "app01_article"."id", "app01_article"."title", "app01_article"."desc", "app01_article"."read_count", "app01_article"."comment_count", "app01_article"."up_count", "app01_a
rticle"."down_count", "app01_article"."create_time", "app01_article"."category_id", "app01_article"."user_id", "app01_article"."site_article_category_id" FROM "app01_article"; args=()
bootstrap導航欄.nav與.navbar區別
[Python]循環嵌套nested loop-練習題
Java提升十五:容器元素比較Comparable&Comparator深刻分析
dsds
sql
dsdsds
dsds
東京不太熱
dsds
東京有點熱
射太陽的人
'''
示例2:
def queryTest(request):
    ret=models.Article.objects.all().iterator()
    # for i in ret:
    #     print(i)
    return HttpResponse("ok")

輸出結果:
#不打印則不進行查詢

緩存機制

def queryTest(request):
    ret=models.Article.objects.all()                                #將qureyset放入一個變量
    for i in ret:                                                  #而後兩次循環這個變量
        print(i)
    for i in ret:
        print(i)
    return HttpResponse("ok")


輸出結果:
#只查詢了一次,可是打印了兩次,說明第一次查詢的值已經緩存到了變量中,下次直接從緩存中拿值就能夠了
'''
(0.001) SELECT "app01_article"."id", "app01_article"."title", "app01_article"."desc", "app01_article"."read_count", "app01_article"."comment_count", "app01_article"."up_count", "app01_
article"."down_count", "app01_article"."create_time", "app01_article"."category_id", "app01_article"."user_id", "app01_article"."site_article_category_id" FROM "app01_article"; args=()


bootstrap導航欄.nav與.navbar區別
[Python]循環嵌套nested loop-練習題
Java提升十五:容器元素比較Comparable&Comparator深刻分析
dsds
sql
dsdsds
dsds
東京不太熱
dsds
東京有點熱
射太陽的人
bootstrap導航欄.nav與.navbar區別
[Python]循環嵌套nested loop-練習題
Java提升十五:容器元素比較Comparable&Comparator深刻分析
dsds
sql
dsdsds
dsds
東京不太熱
dsds
東京有點熱
射太陽的人

'''

優化查詢

exists()與iterator()方法

exists:

簡單的使用if語句進行判斷也會徹底執行整個queryset而且把數據放入cache,雖然你並不須要這些 數據!爲了不這個,能夠用exists()方法來檢查是否有數據:緩存

if queryResult.exists():
    #SELECT (1) AS "a" FROM "blog_article" LIMIT 1; args=()
        print("exists...")

iterator:

當queryset很是巨大時,cache會成爲問題。app

處理成千上萬的記錄時,將它們一次裝入內存是很浪費的。更糟糕的是,巨大的queryset可能會鎖住系統 進程,讓你的程序瀕臨崩潰。要避免在遍歷數據的同時產生queryset cache,可使用iterator()方法 來獲取數據,處理完數據就將其丟棄。ide

def queryTest(request):
    ret=models.Article.objects.all().iterator()                          #優化查詢後面添加iterator(),至關因而一個迭代器
    #iterator()能夠一次只從數據庫獲取少許數據,這樣能夠節省內存
    for i in ret:
        print(i)
    for i in ret:
        print(i)       #第二次無結果
    return HttpResponse("ok")
輸出結果:
#優化查詢就是迭代器第一次查詢遍歷到最後了,後面在打印一次就取得是空值
'''
(0.001) SELECT "app01_article"."id", "app01_article"."title", "app01_article"."desc", "app01_article"."read_count", "app01_article"."comment_count", "app01_article"."up_count", "app01_
article"."down_count", "app01_article"."create_time", "app01_article"."category_id", "app01_article"."user_id", "app01_article"."site_article_category_id" FROM "app01_article"; args=()

bootstrap導航欄.nav與.navbar區別
[Python]循環嵌套nested loop-練習題
Java提升十五:容器元素比較Comparable&Comparator深刻分析
dsds
sql
dsdsds
dsds
東京不太熱
dsds
東京有點熱
射太陽的人
'''

中介模型

處理相似搭配 pizza 和 topping 這樣簡單的多對多關係時,使用標準的ManyToManyField  就能夠了。可是,有時你可能須要關聯數據到兩個模型之間的關係上。

例如,有這樣一個應用,它記錄音樂家所屬的音樂小組。咱們能夠用一個ManyToManyField 表示小組和成員之間的多對多關係。可是,有時你可能想知道更多成員關係的細節,好比成員是什麼時候加入小組的。

對於這些狀況,Django 容許你指定一箇中介模型來定義多對多關係。 你能夠將其餘字段放在中介模型裏面。源模型的ManyToManyField 字段將使用through 參數指向中介模型。對於官網上的的音樂小組的例子,我寫的blog系統的關係的代碼以下:

class Article(models.Model):
    '''
    文章表
    '''

    title = models.CharField(max_length=50, verbose_name='文章標題')
    desc = models.CharField(max_length=255, verbose_name='文章描述')
    read_count = models.IntegerField(default=0)
    comment_count = models.IntegerField(default=0)
    up_count = models.IntegerField(default=0)
    down_count = models.IntegerField(default=0)
    create_time = models.DateTimeField(verbose_name='建立時間', auto_now_add=True)
    category = models.ForeignKey(verbose_name='文章類型', to='Category', null=True,blank=True)
    user = models.ForeignKey(verbose_name='所屬用戶', to='Userinfo')
    tags = models.ManyToManyField(
        to="Tag",
        through='Article2Tag',              #自定義建立多對多的表
        through_fields=('article', 'tag'),
    )
    site_article_category = models.ForeignKey("SiteArticleCategory", null=True)

    def __str__(self):
        return self.title
    class Mate:
        verbose_name_plural = "文章表"
        
        
        
class Tag(models.Model):
    title = models.CharField(verbose_name='標籤名稱', max_length=32)
    blog = models.ForeignKey(verbose_name='所屬博客', to='Blog')

    class Mate:
        verbose_name_plural = "標籤表"
    def __str__(self):
        return self.title        
    
    
    
    

class Article2Tag(models.Model):
    article = models.ForeignKey(verbose_name='文章', to="Article")
    tag = models.ForeignKey(verbose_name='標籤', to="Tag")
    class Meta:
        unique_together = [                       #惟一
            ('article', 'tag'),
        ]

    def __str__(self):
        return self.tag
文章表

標籤表
文章跟標籤的相關聯的表
**還能夠添加別的字段


id
id
article_id tag_id 標籤分類 。。





















如何這樣建立了就與普通的多對多字段不一樣,你不能使用add、 create和賦值語句(好比,beatles.members [...])來建立關係:

爲何不能這樣作? 這是由於你不能只建立 Person和 Group之間的關聯關係,你還要指定 Membership模型中所須要的全部信息;而簡單的addcreate 和賦值語句是作不到這一點的。因此它們不能在使用中介模型的多對多關係中使用。此時,惟一的辦法就是建立中介模型的實例。

 remove()方法被禁用也是出於一樣的緣由。可是clear() 方法倒是可用的。它能夠清空某個實例全部的多對多關係:

只能經過添加表記錄的方式來進行添加了

示例:

models.Article2Tag.objects.create(article_id=1,tag_id=1,)   #後面添加自定義字段的值就行了

查詢優化

表數據

class UserInfo(AbstractUser):
    """
    用戶信息
    """
    nid = models.BigAutoField(primary_key=True)
    nickname = models.CharField(verbose_name='暱稱', max_length=32)
    telephone = models.CharField(max_length=11, blank=True, null=True, unique=True, verbose_name='手機號碼')
    avatar = models.FileField(verbose_name='頭像',upload_to = 'avatar/',default="/avatar/default.png")
    create_time = models.DateTimeField(verbose_name='建立時間', auto_now_add=True)
 
    fans = models.ManyToManyField(verbose_name='粉絲們',
                                  to='UserInfo',
                                  through='UserFans',
                                  related_name='f',
                                  through_fields=('user', 'follower'))
 
    def __str__(self):
        return self.username
 
class UserFans(models.Model):
    """
    互粉關係表
    """
    nid = models.AutoField(primary_key=True)
    user = models.ForeignKey(verbose_name='博主', to='UserInfo', to_field='nid', related_name='users')
    follower = models.ForeignKey(verbose_name='粉絲', to='UserInfo', to_field='nid', related_name='followers')
 
class Blog(models.Model):
 
    """
    博客信息
    """
    nid = models.BigAutoField(primary_key=True)
    title = models.CharField(verbose_name='我的博客標題', max_length=64)
    site = models.CharField(verbose_name='我的博客後綴', max_length=32, unique=True)
    theme = models.CharField(verbose_name='博客主題', max_length=32)
    user = models.OneToOneField(to='UserInfo', to_field='nid')
    def __str__(self):
        return self.title
 
class Category(models.Model):
    """
    博主我的文章分類表
    """
    nid = models.AutoField(primary_key=True)
    title = models.CharField(verbose_name='分類標題', max_length=32)
 
    blog = models.ForeignKey(verbose_name='所屬博客', to='Blog', to_field='nid')
 
class Article(models.Model):
 
    nid = models.BigAutoField(primary_key=True)
    title = models.CharField(max_length=50, verbose_name='文章標題')
    desc = models.CharField(max_length=255, verbose_name='文章描述')
    read_count = models.IntegerField(default=0)
    comment_count= models.IntegerField(default=0)
    up_count = models.IntegerField(default=0)
    down_count = models.IntegerField(default=0)
    category = models.ForeignKey(verbose_name='文章類型', to='Category', to_field='nid', null=True)
    create_time = models.DateField(verbose_name='建立時間')
    blog = models.ForeignKey(verbose_name='所屬博客', to='Blog', to_field='nid')
    tags = models.ManyToManyField(
        to="Tag",
        through='Article2Tag',
        through_fields=('article', 'tag'),
)
 
 
class ArticleDetail(models.Model):
    """
    文章詳細表
    """
    nid = models.AutoField(primary_key=True)
    content = models.TextField(verbose_name='文章內容', )
 
    article = models.OneToOneField(verbose_name='所屬文章', to='Article', to_field='nid')
 
 
class Comment(models.Model):
    """
    評論表
    """
    nid = models.BigAutoField(primary_key=True)
    article = models.ForeignKey(verbose_name='評論文章', to='Article', to_field='nid')
    content = models.CharField(verbose_name='評論內容', max_length=255)
    create_time = models.DateTimeField(verbose_name='建立時間', auto_now_add=True)
 
    parent_comment = models.ForeignKey('self', blank=True, null=True, verbose_name='父級評論')
    user = models.ForeignKey(verbose_name='評論者', to='UserInfo', to_field='nid')
 
    up_count = models.IntegerField(default=0)
 
    def __str__(self):
        return self.content
 
class ArticleUpDown(models.Model):
    """
    點贊表
    """
    nid = models.AutoField(primary_key=True)
    user = models.ForeignKey('UserInfo', null=True)
    article = models.ForeignKey("Article", null=True)
    models.BooleanField(verbose_name='是否贊')
 
class CommentUp(models.Model):
    """
    點贊表
    """
    nid = models.AutoField(primary_key=True)
    user = models.ForeignKey('UserInfo', null=True)
    comment = models.ForeignKey("Comment", null=True)
 
 
class Tag(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(verbose_name='標籤名稱', max_length=32)
    blog = models.ForeignKey(verbose_name='所屬博客', to='Blog', to_field='nid')
 
 
 
class Article2Tag(models.Model):
    nid = models.AutoField(primary_key=True)
    article = models.ForeignKey(verbose_name='文章', to="Article", to_field='nid')
    tag = models.ForeignKey(verbose_name='標籤', to="Tag", to_field='nid')

select_related

簡單使用

對於一對一字段(OneToOneField)和外鍵字段(ForeignKey),可使用select_related 來對QuerySet進行優化。

select_related 返回一個QuerySet,當執行它的查詢時它沿着外鍵關係查詢關聯的對象的數據。它會生成一個複雜的查詢並引發性能的損耗,可是在之後使用外鍵關係時將不須要數據庫查詢。

簡單說,在對QuerySet使用select_related()函數後,Django會獲取相應外鍵對應的對象,從而在以後須要的時候沒必要再查詢數據庫了。

下面的例子解釋了普通查詢和select_related() 查詢的區別。

普通查詢

(0.000) SELECT "app01_category"."id", "app01_category"."title", "app01_category"."blog_id" FROM "app01_category" WHERE "app01_category"."id" = 2; args=(2,)
yang的python
優化查詢
 若是咱們使用select_related()函數:
articleList = models.Article.objects.select_related("category").all()

    for article_obj in articleList:
        #  Doesn't hit the database, because article_obj.category
        #  has been prepopulated in the previous query.
        print(article_obj.category)



輸出結果:
#能夠看到sql語句中,以最左查詢   LEFT OUTER JOIN 以左表爲主
'''
(0.002) SELECT "app01_article"."id", "app01_article"."title", "app01_article"."desc", "app01_article"."read_count", "app01_article"."comment_count", "app01_article"."up_count", "app01_
article"."down_count", "app01_article"."create_time", "app01_article"."category_id", "app01_article"."user_id", "app01_article"."site_article_category_id", "app01_category"."id", "app0
1_category"."title", "app01_category"."blog_id" FROM "app01_article" LEFT OUTER JOIN "app01_category" ON ("app01_article"."category_id" = "app01_category"."id"); args=()
yang的web
yang的python
yang的java
None
None
None
None
None
None
None
None

'''



總結

  1. select_related主要針一對一和多對一關係進行優化。
  2. select_related使用SQL的JOIN語句進行優化,經過減小SQL查詢的次數來進行優化、提升性能。
  3. 能夠經過可變長參數指定須要select_related的字段名。也能夠經過使用雙下劃線「__」鏈接字段名來實現指定的遞歸查詢。
  4. 沒有指定的字段不會緩存,沒有指定的深度不會緩存,若是要訪問的話Django會再次進行SQL查詢。
  5. 也能夠經過depth參數指定遞歸的深度,Django會自動緩存指定深度內全部的字段。若是要訪問指定深度外的字段,Django會再次進行SQL查詢。
  6. 也接受無參數的調用,Django會盡量深的遞歸查詢全部的字段。但注意有Django遞歸的限制和性能的浪費。
  7. Django >= 1.7,鏈式調用的select_related至關於使用可變長參數。Django < 1.7,鏈式調用會致使前邊的select_related失效,只保留最後一個。



相關文章
相關標籤/搜索