django-model-utils

一个普通例子:

todos = Todo.objects.filter(owner=request.user).filter(is_done=False).filter(priority=1)

弊端:

首先,代码冗长,正式的项目中,将会更加复杂。

其次,泄露实现细节。比如代码中的is_done是BooleanField,如果改变了他的类型,代码就不能用了。

或者就是,意图不清晰,很难理解。

最后,使用中会有重复。

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中查询。

Manager接口就是个谎言。

QuerySet方法是可链接的。每一次调用QuerySet的方法(如:filter)都会返回一个复制的queryset等待下一次的调用。这也是Django ORM 流畅之美的一部分。

但是当Model.objects 是一个 Manager时,就出现问题了。我们需要调用objects作为开始,然后链接到结果的QuerySet上去。

那么Django又是如何解决呢?

接口的谎言由此暴露,所有的QuerySet 方法基于Manager。

更多:http://www.oschina.net/translate/higher-level-query-api-django-orm

 

models.py:

from model_utils.managers import PassThroughManager

 

class NewsQuerySet(models.query.QuerySet):
    def display(self):
        return self.filter(status=1).order_by('-create_time')

    def exece(self, question_id):
        return self.exclude(pk=question_id).display()

 

class News(models.Model):
    STATUS = (
        (0, u'不展示'),
        (1, u'展示'),
    )
    title = models.CharField(max_length=20)
    author = models.CharField(max_length=20)
    img = models.ImageField(upload_to='upload/news/%Y/%m/%d', blank=True, null=True)
    content = models.TextField(blank=True, null=True)
    create_time = models.DateTimeField(auto_now_add=True)
    source = models.CharField(max_length=20)
    status = models.IntegerField(default=1, null=True, blank=True, choices=STATUS)

    objects = PassThroughManager.for_queryset_class(NewsQuerySet)()

views.py:

原先:

q = News.objects.all().exclude(id=news_id).filter(status=1).order_by('-create_time')
现在:

q = News.objects.exece(news_id)

 

在视图和其他高级应用中使用源生的ORM查询代码不是很好的主意。而是用django-model-utils中的PassThroughManager将我们新加的自定义QuerySet API加进你的模型中,这能给你以下好处:

    啰嗦代码少,并且更健壮。
    增加DRY,增强抽象级别。

  将所属的业务逻辑推送至对应的域模型层。

 


 

2015.1.6

StatusField

from model_utils.fields import StatusField
from model_utils import Choices

class Article(models.Model):
    STATUS = Choices('draft', 'published')
    # ...
    status = StatusField()


from model_utils.fields import StatusField
from model_utils import Choices

class Article(models.Model):
    ANOTHER_CHOICES = Choices('draft', 'published')
    # ...
    another_field = StatusField(choices_name='ANOTHER_CHOICES')

MonitorField

from model_utils.fields import MonitorField, StatusField

class Article(models.Model):
    STATUS = Choices('draft', 'published')

    status = StatusField()
    status_changed = MonitorField(monitor='status')


from model_utils.fields import MonitorField, StatusField

class Article(models.Model):
    STATUS = Choices('draft', 'published')

    status = StatusField()
    published_at = MonitorField(monitor='status', when=['published'])

SplitField

from django.db import models
from model_utils.fields import SplitField

class Article(models.Model):
    title = models.CharField(max_length=100)
    body = SplitField()

content:
The full field contents.
excerpt:
The excerpt of content (read-only).
has_more:
True if the excerpt and content are different, False otherwise.

>>> a = Article.objects.all()[0]
>>> a.body.content
u'some text\n\n<!-- split -->\n\nmore text'
>>> a.body.excerpt
u'some text\n'
>>> unicode(a.body)
u'some text\n\n<!-- split -->\n\nmore text'

By default, SplitField looks for the marker <!-- split --> alone on a line and takes everything before that marker as the excerpt. This marker can be customized by setting the SPLIT_MARKER setting.

If no marker is found in the content, the first two paragraphs (where paragraphs are blocks of text separated by a blank line) are taken to be the excerpt. This number can be customized by setting the SPLIT_DEFAULT_PARAGRAPHS setting.

 Choices

from model_utils import Choices

GENERIC_CHOICES = Choices((0, 'draft', _('draft')), (1, 'published', _('published')))

class Article(models.Model):
    STATUS = GENERIC_CHOICES + [(2, 'featured', _('featured'))]
    status = models.IntegerField(choices=STATUS, default=STATUS.draft)

 Field Tracker

from django.db import models
from model_utils import FieldTracker

class Post(models.Model):
    title = models.CharField(max_length=100)
    body = models.TextField()

    tracker = FieldTracker()


>>> a = Post.objects.create(title='First Post')
>>> a.title = 'Welcome'
>>> a.tracker.previous('title')

>>> a = Post.objects.create(title='First Post')
>>> a.title = 'Welcome'
>>> a.tracker.has_changed('title')
True
>>> a.tracker.has_changed('body')
False

>>> a = Post.objects.create(title='First Post')
>>> a.title = 'Welcome'
>>> a.body = 'First post!'
>>> a.tracker.changed()
{'title': 'First Post', 'body': ''}

 

from django.db import models
from model_utils import FieldTracker

class Post(models.Model):
    title = models.CharField(max_length=100)
    body = models.TextField()

    title_tracker = FieldTracker(fields=['title'])

>>> a = Post.objects.create(title='First Post')
>>> a.body = 'First post!'
>>> a.title_tracker.changed()
{'title': None}

 

https://django-model-utils.readthedocs.org/en/latest/models.html

posted on 2014-08-27 00:17  颓废的悠然  阅读(1791)  评论(0编辑  收藏  举报

导航