<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        當前位置: 首頁 - 科技 - 知識百科 - 正文

        利用Python的Django框架中的ORM建立查詢API

        來源:懂視網 責編:小采 時間:2020-11-27 14:39:29
        文檔

        利用Python的Django框架中的ORM建立查詢API

        利用Python的Django框架中的ORM建立查詢API: 摘要 在這篇文章里,我將以反模式的角度來直接討論Django的低級ORM查詢方法的使用。作為一種替代方式,我們需要在包含業務邏輯的模型層建立與特定領域相關的查詢API,這些在Django中做起來不是非常容易,但通過深入地了解ORM的內容原理,我將告訴你一些簡捷
        推薦度:
        導讀利用Python的Django框架中的ORM建立查詢API: 摘要 在這篇文章里,我將以反模式的角度來直接討論Django的低級ORM查詢方法的使用。作為一種替代方式,我們需要在包含業務邏輯的模型層建立與特定領域相關的查詢API,這些在Django中做起來不是非常容易,但通過深入地了解ORM的內容原理,我將告訴你一些簡捷

        摘要

        在這篇文章里,我將以反模式的角度來直接討論Django的低級ORM查詢方法的使用。作為一種替代方式,我們需要在包含業務邏輯的模型層建立與特定領域相關的查詢API,這些在Django中做起來不是非常容易,但通過深入地了解ORM的內容原理,我將告訴你一些簡捷的方式來達到這個目的。

        概覽

        當編寫Django應用程序時,我們已經習慣通過添加方法到模型里以此達到封裝業務邏輯并隱藏實現細節。這種方法看起來是非常的自然,而且實際上它也用在Django的內建應用中。

        >>> from django.contrib.auth.models import User
        >>> user = User.objects.get(pk=5)
        >>> user.set_password('super-sekrit')
        >>> user.save()
        

        這里的set_password就是一個定義在django.contrib.auth.models.User模型中的方法,它隱藏了對密碼進行哈希操作的具體實現。相應的代碼看起來應該是這樣:

        from django.contrib.auth.hashers import make_password
         
        class User(models.Model):
         
         # fields go here..
         
         def set_password(self, raw_password):
         self.password = make_password(raw_password)
        


        我們正在使用Django,建立一個特定領域的頂部通用接口,低等級的ORM工具。在此基礎上,增加抽象等級,減少交互代碼。這樣做的好處是使代碼更具可讀性、重用性和健壯性。

        我們已經在單獨的例子中這樣做了,下面將會把它用在獲取數據庫信息的例子中。

        為了描述這個方法,我們使用了一個簡單的app(todo list)來說明。

        注意:這是一個例子。因為很難用少量的代碼展示一個真實的例子。不要過多的關心todo list繼承他自己,而要把重點放在如何讓這個方法運行。
        下面就是models.py文件:

        from django.db import models
         
        PRIORITY_CHOICES = [(1, 'High'), (2, 'Low')]
         
        class Todo(models.Model):
         content = models.CharField(max_length=100)
         is_done = models.BooleanField(default=False)
         owner = models.ForeignKey('auth.User')
         priority = models.IntegerField(choices=PRIORITY_CHOICES, default=1
        


        想像一下,我們將要傳遞這些數據,建立一個view,來為當前用戶展示不完整的,高優先級的 Todos。這里是代碼:

        def dashboard(request):
         
         todos = Todo.objects.filter(
         owner=request.user
         ).filter(
         is_done=False
         ).filter(
         priority=1
         )
         
         return render(request, 'todos/list.html', {
         'todos': todos,
         })
        

        注意:這里可以寫成request.user.todo_set.filter(is_done=False, priority=1)。但是這里只是一個實驗。

        為什么這樣寫不好呢?

        首先,代碼冗長。七行代碼才能完成,正式的項目中,將會更加復雜。

        其次,泄露實現細節。比如代碼中的is_done是BooleanField,如果改變了他的類型,代碼就不能用了。

        然后就是,意圖不清晰,很難理解。

        最后,使用中會有重復。例:你需要寫一行命令,通過cron,每周發送給所有用戶一個todo list,這時候你就需要復制-粘貼著七行代碼。這不符合DRY(do not repeat yourself)


        讓我們大膽的猜測一下:直接使用低等級的ORM代碼是反模式的。
        如何改進呢?

        使用 Managers 和 QuerySets
        首先,讓我們先了解一下概念。

        Django 有兩個關系密切的與表級別操作相關的構圖:managers 和 querysets

        manager(django.db.models.manager.Manager的一個實例)被描述成 “通過查詢數據庫提供給Django的插件”。Manager是表級別功能的通往ORM大門。每一個model都有一個默認的manager,叫做objects。
        Quesyset (django.db.models.query.QuerySet) 是“數據庫中objects的集合”。本質上是一個SELECT查詢,也可以使用過濾,排序等(filtered,ordered),來或者修改查詢到的數據。用來 創建或操縱 django.db.models.sql.query.Query實例,然后通過數據庫后端在真正的SQL中查詢。

        啊?你還不明白?

        隨著你慢慢深入的了解ORM,你就會明白Manager和QuerySet之間的區別了。


        人們會被所熟知的Manager接口搞糊涂,因為他并不是看上去那樣。

        Manager接口就是個謊言。

        QuerySet方法是可鏈接的。每一次調用QuerySet的方法(如:filter)都會返回一個復制的queryset等待下一次的調用。這也是Django ORM 流暢之美的一部分。

        但是當Model.objects 是一個 Manager時,就出現問題了。我們需要調用objects作為開始,然后鏈接到結果的QuerySet上去。

        那么Django又是如何解決呢?

        接口的謊言由此暴露,所有的QuerySet 方法基于Manager。在這個方法中,通過self.get_query_set()的代理,重新創建一個

        QuerySet。
         
        class Manager(object):
         
         # SNIP some housekeeping stuff..
         
         def get_query_set(self):
         return QuerySet(self.model, using=self._db)
         
         def all(self):
         return self.get_query_set()
         
         def count(self):
         return self.get_query_set().count()
         
         def filter(self, *args, **kwargs):
         return self.get_query_set().filter(*args, **kwargs)
         
         # and so on for 100+ lines...
        

        更多代碼,請參照Manager的資源文件。

        讓我們立刻回到todo list ,解決query接口的問題。Django推薦的方法是自定義Manager子類,并加在models中。

        你也可以在model中增加多個managers,或者重新定義objects,也可以維持單個的manager,增加自定義方法。

        下面讓我們實驗一下這幾種方法:

        方法1:多managers


        class IncompleteTodoManager(models.Manager):
         def get_query_set(self):
         return super(TodoManager, self).get_query_set().filter(is_done=False)
         
        class HighPriorityTodoManager(models.Manager):
         def get_query_set(self):
         return super(TodoManager, self).get_query_set().filter(priority=1)
         
        class Todo(models.Model):
         content = models.CharField(max_length=100)
         # other fields go here..
         
         objects = models.Manager() # the default manager
         
         # attach our custom managers:
         incomplete = models.IncompleteTodoManager()
         high_priority = models.HighPriorityTodoManager()
        

        這個接口將以這樣的方式展現:

        >>> Todo.incomplete.all()
        >>> Todo.high_priority.all()
        

        這個方法有幾個問題。


        第一,這種實現方式比較啰嗦。你要為每一個query自定義功能定義一個class。

        第二,這將會弄亂你的命名空間。Django開發者吧Model.objects看做表的入口。這樣做會破壞命名規則。

        第三,不可鏈接的。這樣做不能將managers組合在一起,獲得不完整,高優先級的todos,還是回到低等級的ORM代碼:Todo.incomplete.filter(priority=1) 或Todo.high_priority.filter(is_done=False)
        綜上,使用多managers的方法,不是最優選擇。


        方法2: Manager 方法

        現在,我們試下其他Django允許的方法:在單個自定義Manager中的多個方法

        class TodoManager(models.Manager):
         def incomplete(self):
         return self.filter(is_done=False)
         
         def high_priority(self):
         return self.filter(priority=1)
         
        class Todo(models.Model):
         content = models.CharField(max_length=100)
         # other fields go here..
         
         objects = TodoManager()
        

        我們的API 現在看起來是這樣:

        >>> Todo.objects.incomplete()
        >>> Todo.objects.high_priority()
        

        這個方法顯然更好。它沒有太多累贅(只有一個Manager類)并且這種查詢方法很好地在對象后預留命名空間。(譯注:可以很形象、方便地添加更多的方法)
        不過這還不夠全面。 Todo.objects.incomplete() 返回一個普通查詢,但我們無法使用 Todo.objects.incomplete().high_priority() 。我們卡在 Todo.objects.incomplete().filter(is_done=False),沒有使用。


        方法3:自定義QuerySet

        現在我們已進入Django尚未開放的領域,Django文檔中找不到這些內容。。

        class TodoQuerySet(models.query.QuerySet):
         def incomplete(self):
         return self.filter(is_done=False)
         
         def high_priority(self):
         return self.filter(priority=1)
         
        class TodoManager(models.Manager):
         def get_query_set(self):
         return TodoQuerySet(self.model, using=self._db)
         
        class Todo(models.Model):
         content = models.CharField(max_length=100)
         # other fields go here..
         
         objects = TodoManager()
        

        我們從以下調用的視圖代碼中可以看出端倪:


        >>> Todo.objects.get_query_set().incomplete()
        >>> Todo.objects.get_query_set().high_priority()
        >>> # (or)
        >>> Todo.objects.all().incomplete()
        >>> Todo.objects.all().high_priority()
        

        差不多完成了!這并沒有比第2個方法多多少累贅,卻得到方法2同樣的好處,和額外的效果(來點鼓聲吧...),它終于可鏈式查詢了!

        >>> Todo.objects.all().incomplete().high_priority()
        

        然而它還不夠完美。這個自定義的Manager僅僅是一個樣板而已,而且 all() 還有瑕疵,在使用時不好把握,而更重要的是不兼容,它讓我們的代碼看起來有點怪異。


        方法3a:復制Django,代理做所有事

        現在我們讓以上”假冒Manager API“討論變得有用:我們知道如何解決這個問題。我們簡單地在Manager中重新定義所有QuerySet方法,然后代理它們返回我們自定義QuerySet:

        class TodoQuerySet(models.query.QuerySet):
         def incomplete(self):
         return self.filter(is_done=False)
         
         def high_priority(self):
         return self.filter(priority=1)
         
        class TodoManager(models.Manager):
         def get_query_set(self):
         return TodoQuerySet(self.model, using=self._db)
         
         def incomplete(self):
         return self.get_query_set().incomplete()
         
         def high_priority(self):
         return self.get_query_set().high_priority()
        

        這個能更好地提供我們想要的API:

        >>> Todo.objects.incomplete().high_priority() # yay!
        

        除上面那些輸入部分、且非常不DRY,每次你新增一個文件到QuerySet,或是更改現有的方法標記,你必須記住在你的Manager中做相同的更改,否則它可能不會正常工作。這是配置的問題
        方法3b: django-model-utils

        Python 是一種動態語言。 我們就一定能避免所有模塊?一個名叫Django-model-utils的第三方應用帶來的一點小忙,就會有點不受控制了。先運行 pip install django-model-utils ,然后……

        from model_utils.managers import PassThroughManager
         
        class TodoQuerySet(models.query.QuerySet):
         def incomplete(self):
         return self.filter(is_done=False)
         
         def high_priority(self):
         return self.filter(priority=1)
         
        class Todo(models.Model):
         content = models.CharField(max_length=100)
         # other fields go here..
         
         objects = PassThroughManager.for_queryset_class(TodoQuerySet)()
        

        這要好多了。我們只是象之前一樣 簡單地定義了自定義QuerySet子類,然后通過django-model-utils提供的PassThroughManager類附加這些QuerySet到我們的model中。

        PassThroughManager 是由__getattr__ 實現的,它能阻止訪問到django定義的“不存在的方法”,并且自動代理它們到QuerySet。這里需要小心一點,檢查確認我們沒有在一些特性中沒有無限遞歸(這是我為什么推薦使用django-model-utils所提供的用不斷嘗試測試的方法,而不是自己手工重復寫)。

        做這些有什么幫助?

        記得上面早些定義的視圖代碼么?

        def dashboard(request):
         
         todos = Todo.objects.filter(
         owner=request.user
         ).filter(
         is_done=False
         ).filter(
         priority=1
         )
         
         return render(request, 'todos/list.html', {
         'todos': todos,
         })
        

        加點小改動,我們讓它看起來象這樣:

        def dashboard(request):
         
         todos = Todo.objects.for_user(
         request.user
         ).incomplete().high_priority()
         
         return render(request, 'todos/list.html', {
         'todos': todos,
         })
        

        希望你也能同意第二個版本比第一個更簡便,清晰并且更有可讀性。
        Django能幫忙么?


        讓這整個事情更容易的方法,已經在django開發郵件列表中討論過,并且得到一個相關票據(譯注:associated ticket叫啥名更好?)。Zachary Voase則建議如下:

        class TodoManager(models.Manager):
         
         @models.querymethod
         def incomplete(query):
         return query.filter(is_done=False)
        

        通過這個簡單的裝飾方法的定義,讓Manager和QuerySet都能使不可用的方法神奇地變為可用。

        我個人并不完全贊同使用基于裝飾方法。它略過了詳細的信息,感覺有點“嘻哈”。我感覺好的方法,增加一個QuerSet子類(而不是Manager子類)是更好,更簡單的途徑。
        或者我們更進一步思考。退回到在爭議中重新審視Django的API設計決定時,也許我們能得到真實更深的改進。能不再爭吵Managers和QuerySet的區別嗎(至少澄清一下)?


        我很確信,不管以前是否曾經有過這么大的重構工作,這個功能必然要在Django 2.0 甚至更后的版本中。

        因此,簡單概括一下:

        在視圖和其他高級應用中使用源生的ORM查詢代碼不是很好的主意。而是用django-model-utils中的PassThroughManager將我們新加的自定義QuerySet API加進你的模型中,這能給你以下好處:

      1. 啰嗦代碼少,并且更健壯。
      2. 增加DRY,增強抽象級別。
      3. 將所屬的業務邏輯推送至對應的域模型層。
      4. 感謝閱讀!

        聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

        利用Python的Django框架中的ORM建立查詢API

        利用Python的Django框架中的ORM建立查詢API: 摘要 在這篇文章里,我將以反模式的角度來直接討論Django的低級ORM查詢方法的使用。作為一種替代方式,我們需要在包含業務邏輯的模型層建立與特定領域相關的查詢API,這些在Django中做起來不是非常容易,但通過深入地了解ORM的內容原理,我將告訴你一些簡捷
        推薦度:
        標簽: 查詢 API python
        • 熱門焦點

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top 主站蜘蛛池模板: 亚洲国产成人精品91久久久 | 性色av极品无码专区亚洲| 亚洲一区免费观看| 国产美女亚洲精品久久久综合| 最好2018中文免费视频| 国产免费午夜a无码v视频| 特级毛片A级毛片免费播放| 免费久久精品国产片香蕉| 精品在线免费视频| 全部免费毛片在线| 国产精品免费久久| 亚洲成年人免费网站| 91精品全国免费观看含羞草| 久久精品国产亚洲AV高清热| 中文字幕免费高清视频| 亚洲另类古典武侠| 浮力影院第一页小视频国产在线观看免费 | 久99精品视频在线观看婷亚洲片国产一区一级在线 | 亚洲激情电影在线| 成人毛片18女人毛片免费视频未| 亚洲综合色一区二区三区| 国产免费观看a大片的网站| 午夜亚洲国产精品福利| 国产国拍亚洲精品福利 | 区久久AAA片69亚洲| 免费成人在线电影| 亚洲国产日产无码精品| 成年女人免费视频播放77777| 国产亚洲蜜芽精品久久| 亚洲国产日韩在线视频| 久久九九兔免费精品6| 亚洲sm另类一区二区三区| 国产福利电影一区二区三区,亚洲国模精品一区 | 最近2019中文免费字幕在线观看| 成年大片免费视频| 羞羞视频免费观看| 亚洲AV人人澡人人爽人人夜夜 | 国外成人免费高清激情视频| 美女视频黄频a免费大全视频| 国产亚洲精品a在线无码| 亚洲高清中文字幕免费|