国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Python > 正文

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

2019-11-25 17:41:42
字體:
供稿:網(wǎng)友

 摘要

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

概覽

當(dāng)編寫Django應(yīng)用程序時,我們已經(jīng)習(xí)慣通過添加方法到模型里以此達(dá)到封裝業(yè)務(wù)邏輯并隱藏實(shí)現(xiàn)細(xì)節(jié)。這種方法看起來是非常的自然,而且實(shí)際上它也用在Django的內(nèi)建應(yīng)用中。
 

>>> 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模型中的方法,它隱藏了對密碼進(jìn)行哈希操作的具體實(shí)現(xiàn)。相應(yīng)的代碼看起來應(yīng)該是這樣:
 

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,建立一個特定領(lǐng)域的頂部通用接口,低等級的ORM工具。在此基礎(chǔ)上,增加抽象等級,減少交互代碼。這樣做的好處是使代碼更具可讀性、重用性和健壯性。

我們已經(jīng)在單獨(dú)的例子中這樣做了,下面將會把它用在獲取數(shù)據(jù)庫信息的例子中。

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

注意:這是一個例子。因?yàn)楹茈y用少量的代碼展示一個真實(shí)的例子。不要過多的關(guān)心todo list繼承他自己,而要把重點(diǎn)放在如何讓這個方法運(yùn)行。
下面就是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


想像一下,我們將要傳遞這些數(shù)據(jù),建立一個view,來為當(dāng)前用戶展示不完整的,高優(yōu)先級的 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)。但是這里只是一個實(shí)驗(yàn)。

為什么這樣寫不好呢?

首先,代碼冗長。七行代碼才能完成,正式的項(xiàng)目中,將會更加復(fù)雜。

其次,泄露實(shí)現(xiàn)細(xì)節(jié)。比如代碼中的is_done是BooleanField,如果改變了他的類型,代碼就不能用了。

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

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


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

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

Django 有兩個關(guān)系密切的與表級別操作相關(guān)的構(gòu)圖:managers 和 querysets

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

啊?你還不明白?

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


人們會被所熟知的Manager接口搞糊涂,因?yàn)樗⒉皇强瓷先ツ菢印?/p>

Manager接口就是個謊言。

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

但是當(dāng)Model.objects 是一個 Manager時,就出現(xiàn)問題了。我們需要調(diào)用objects作為開始,然后鏈接到結(jié)果的QuerySet上去。

那么Django又是如何解決呢?

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

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,增加自定義方法。

下面讓我們實(shí)驗(yàn)一下這幾種方法:

方法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()

這個接口將以這樣的方式展現(xiàn):
 

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

這個方法有幾個問題。


第一,這種實(shí)現(xiàn)方式比較

主站蜘蛛池模板: 耿马| 崇义县| 娄底市| 龙胜| 贵溪市| 大连市| 北宁市| 嘉义市| 隆回县| 仁布县| 凤庆县| 海原县| 松桃| 施甸县| 天峻县| 武定县| 肇庆市| 嫩江县| 柯坪县| 开封市| 龙州县| 吉林省| 故城县| 攀枝花市| 黑龙江省| 九龙县| 太仓市| 余庆县| 惠来县| 横山县| 通海县| 自治县| 福清市| 五台县| 彝良县| 上林县| 宁安市| 和硕县| 汤原县| 武隆县| 安宁市|