django model filter_Django分表的两个方案
由來
知乎上的一個問題:Django 分表 怎么實現?
這個問題戳到了Django ORM的痛點,對于多數據庫/分庫的問題,Django提供了很好的支持,通過using和db router可以很好的完成多數據庫的操作。但是說到分表的問題,就有點不那么友好了。但也不是那么難處理,只是處理起來不太優雅。
解析
在Django中,數據庫訪問的邏輯基本上是在Queryset中完成的,一個查詢請求,比如:User.objects.filter(group_id=10)。
其中的objects其實就是models.Manager,而Manager又是對QuerySet的一個包裝。而QuerySet又是最終要轉換為sql的一個中間層(就是ORM種,把Model操作轉換為SQL語句的部分)。所以當我們寫下User.objects的時候,就已經確定了要訪問的是哪個表了,這是由class Meta中的db_table決定的。
class理論上講,我們可以通過在運行時修改db_table來完成分表CRUD的邏輯,但是the5fire在看了又看源碼之后,還是沒找到如何下手。還是上面的問題,當執行到User.objects的時候,表已經確定了,當執行到User.objects.filter(group=10)的時候只不過是在已經生成好的sql語句中增加了一個where部分語句。所以并沒有辦法在執行filter的時候來動態設置db_table。
對于問題中說的get也是一樣,因為get本身就是在執行完filter之后從_result_cache列表中獲取的數據(_result_cache[0])。
方案一
根據the5fire上面的分析,要想在執行具體查詢時修改db_table已經是不可能了(當然,如果你打算去重寫Model中Meta部分的邏輯以及Queryset部分的邏輯,就當我沒說,我只能表示佩服)。
所以只能從定義層面下手了。也就是我需要定義多個Model,同樣的字段,不同的db_table。大概是這樣。
class這樣在User.objects.get(id=3)的時候,如果按照模2計算,那就是User01.objects.get(id=3),笨點的方法就是寫一個dict:
user_sharding_map如果真的這么寫那Python作為動態語言,還有啥用,你分128張表試試。我們應該動態創建出User01,User02,....UserN這樣的表。
class嗯,這樣看起來似乎好了一下,但是還有問題,id=3需要傳兩次,如果兩次不一致,那就麻煩了。Model層要為上層提供統一的入口才行。
class MyUser(models.Model):# 增加方法 BY the5fire@classmethoddef sharding_get(cls, id=None, **kwargs):assert id, 'id is required!'Model = cls.get_sharding_model(id=id)return Model.objects.get(id=id, **kwargs)對上層來書,只需要執行MyUser.sharding_get(id=10)即可。不過這改變了之前的調用習慣 objects.get 。
不管怎么說吧,這也是個方案,更完美的方法就不繼續探究了,在Django的ORM中鉆來鉆去尋找可以hook的點實在憋屈。
我們來看方案二吧
方案二
ORM的過程是這樣的,Model——> SQL ——> Model,在方案一中我們一直在處理Model——> SQL的部分。其實我們可以拋開這一步,直接使用raw sql。
QuerySet提供了raw這樣的接口,用來讓你忽略第一層轉換,但是有可以使用從SQL到Model的轉換。只針對SELECT的案例:
class大概這么個意思吧,代碼可以再嚴謹些。
總結
單純看方案一的話,可能會覺得這么大量數據的項目,就別用Django了。其實the5fire第一次嘗試找一個優雅的方式hack db_table時,也是一頭灰。但是,所有的項目都是由小到大的,隨著數據/業務的變大,技術人員應該也會更加了解Django,等到一定階段之后,可能發現,用其他更靈活的框架,跟直接定制Django成本差不多。
補充兩個github repo: - JBKahn/django-sharding - disqus/sharding-example
----EOF-----
掃碼關注,或者搜索微信公眾號:Python程序員雜談
總結
以上是生活随笔為你收集整理的django model filter_Django分表的两个方案的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: wxpython 安装_下载和安装wxP
- 下一篇: python读excel字体颜色_pyt