跳转到主要内容

一个对象,它使多个可迭代对象能够以查询集兼容的方式懒加载。

项目描述

https://secure.travis-ci.org/ambv/dj.chain.png

本模块提供了一种将多个有限可迭代对象链接起来,以便作为查询集兼容对象消费的方法。

快速入门

让我们从一个例子开始。假设我们有一些抽象数据库模型,我们可以稍后重用这些字段

class Titled(db.Model):
    title = db.CharField(max_length=100)

    class Meta:
        abstract = True

class Dynamic(db.Model):
    duration = db.PositiveIntegerField()

    class Meta:
        abstract = True

我们还拥有一些具有这些字段之一的具体数据库模型

class Video(Titled, Dynamic):
    RESOLUTION = (
            (1, '240p'), (2, '320p'), (3, '480p'),
            (4, '720p'), (5, '1080p')
    )

    author = db.CharField(max_length=100)
    resolution = db.IntegerField(choices=RESOLUTION)

class Song(Titled, Dynamic):
    GENRE = (
            (1, 'Country'), (2, 'Folk'), (3, 'Polka'),
            (4, 'Western'), (5, 'World')
    )

    artist = db.CharField(max_length=100)
    genre = db.IntegerField(choices=GENRE)

我们的数据库已经包含了一些数据

>>> Video.objects.all()
[<Video: Psy - Gangnam Style (253 s at 1080p)>,
 <Video: Justin Bieber - Baby (225 s at 720p)>,
 <Video: Lady Gaga - Bad Romance (308 s at 320p)>,
 <Video: Shakira - Waka Waka (211 s at 480p)>]
>>> Song.objects.all()
[<Song: Gotye feat. Kimbra - Somebody That I Used to Know (244 s; Folk)>,
 <Song: Coldplay - Clocks (307 s; Polka)>,
 <Song: Muse - Madness (279 s; Country)>,
 <Song: Florence + The Machine - Spectrum (218 s; Folk)>]

基本链

让我们创建一个简单的链

>>> from dj.chain import chain
>>> media = chain(Video.objects.all(), Song.objects.all())

我们可以对其调用相关的查询集方法

>>> media.count()
8

我们还可以进一步过滤它

>>> list(media.filter(duration__gt=250))
[<Video: Psy - Gangnam Style (253 s at 1080p)>,
 <Video: Lady Gaga - Bad Romance (308 s at 320p)>,
 <Song: Coldplay - Clocks (307 s; Polka)>,
 <Song: Muse - Madness (279 s; Country)>]

检查累积长度

>>> media.filter(duration__gt=250).count()
4

使用索引和切片

>>> media.filter(duration__gt=250)[1]
<Video: Lady Gaga - Bad Romance (308 s at 320p)>
>>> list(media[3:6])
[<Video: Shakira - Waka Waka (211 s at 480p)>,
 <Song: Gotye feat. Kimbra - Somebody That I Used to Know (244 s; Folk)>,
 <Song: Coldplay - Clocks (307 s; Polka)>]
>>> list(media[1::3])
[<Video: Justin Bieber - Baby (225 s at 720p)>,
 <Song: Gotye feat. Kimbra - Somebody That I Used to Know (244 s; Folk)>,
 <Song: Florence + The Machine - Spectrum (218 s; Folk)>]

使用累积排序和过滤

>>> list(media.order_by('title'))
[<Video: Justin Bieber - Baby (225 s at 720p)>,
 <Video: Lady Gaga - Bad Romance (308 s at 320p)>,
 <Song: Coldplay - Clocks (307 s; Polka)>,
 <Video: Psy - Gangnam Style (253 s at 1080p)>,
 <Song: Muse - Madness (279 s; Country)>,
 <Song: Gotye feat. Kimbra - Somebody That I Used to Know (244 s; Folk)>,
 <Song: Florence + The Machine - Spectrum (218 s; Folk)>,
 <Video: Shakira - Waka Waka (211 s at 480p)>]
>>> list(media.order_by('-duration').filter(duration__lt=300))
[<Song: Muse - Madness (279 s; Country)>,
 <Video: Psy - Gangnam Style (253 s at 1080p)>,
 <Song: Gotye feat. Kimbra - Somebody That I Used to Know (244 s; Folk)>,
 <Video: Justin Bieber - Baby (225 s at 720p)>,
 <Song: Florence + The Machine - Spectrum (218 s; Folk)>,
 <Video: Shakira - Waka Waka (211 s at 480p)>]

等等。

链异构可迭代对象

我们可以将不是查询集的可迭代对象添加到集合中

>>> from collections import namedtuple
>>> Book = namedtuple('Book', "author title page_count")
>>> books=(
... Book(author='Charles Dickens', title='A Tale of Two Cities', page_count=869),
... Book(author='Miguel de Cervantes', title='Don Quixote', page_count=1212),
... )
>>> media=chain(Video.objects.all(), books)
>>> media.count()
6
>>> list(media)
[<Video: Psy - Gangnam Style (253 s at 1080p)>,
 <Video: Justin Bieber - Baby (225 s at 720p)>,
 <Video: Lady Gaga - Bad Romance (308 s at 320p)>,
 <Video: Shakira - Waka Waka (211 s at 480p)>,
 Book(author='Charles Dickens', title='A Tale of Two Cities', page_count=869),
 Book(author='Miguel de Cervantes', title='Don Quixote', page_count=1212)]

在这种情况下,您也可以使用累积排序。您需要注意的唯一一点是,为了使累积结果按正确顺序排序,非查询集的可迭代对象应该预先排序。示例

>>> list(media.order_by('title'))
[Book(author='Charles Dickens', title='A Tale of Two Cities', page_count=869),
 <Video: Justin Bieber - Baby (225 s at 720p)>,
 <Video: Lady Gaga - Bad Romance (308 s at 320p)>,
 Book(author='Miguel de Cervantes', title='Don Quixote', page_count=1212),
 <Video: Psy - Gangnam Style (253 s at 1080p)>,
 <Video: Shakira - Waka Waka (211 s at 480p)>]

您还可以使用累积的 valuesvalues_list 转换

>>> media = chain(mt.Video.objects.all(), mt.books)
>>> list(media.values('title'))
[{'title': u'Gangnam Style'}, {'title': u'Baby'}, {'title': u'Bad Romance'},
 {'title': u'Waka Waka'}, {'title': u'A Tale of Two Cities'},
 {'title': u'Don Quixote'}]
>>> list(media.values_list('title', 'author'))
[(u'Gangnam Style', u'Psy'), (u'Baby', u'Justin Bieber'),
 (u'Bad Romance', u'Lady Gaga'), (u'Waka Waka', u'Shakira'),
 (u'A Tale of Two Cities', u'Charles Dickens'),
 (u'Don Quixote', u'Miguel de Cervantes')]
>>> list(media.values_list('author', flat=True))
[u'Psy', u'Justin Bieber', u'Lady Gaga', u'Shakira', u'Charles Dickens',
 u'Miguel de Cervantes']

自定义过滤、排序和转换

链提供在产生值时使用的特殊可覆盖的静态方法

  • xfilter(value) - 仅当 xfilter(value) 返回 True 时产生值。请参阅下面的已知问题。

  • xform(value) - 在产生值之前即时转换值。它仅对指定切片内的值和通过 xfilter 的值进行调用。

  • xkey(value) - 如果使用排序,则返回用于元素间比较的值。为了正确排序完整结果,单个可迭代对象应预先排序。任何累积的 order_by 子句都在使用 xkey 方法之前执行。

在不兼容的可迭代对象上静默忽略方法

链可以包含类似查询集的对象和其他可迭代对象。如果链对象上集体调用,则有一些仅适用于前者的方法。这些是

  • defer

  • exclude

  • extra

  • filter

  • only

  • prefetch_related

  • select_for_update

  • select_related

  • using

默认情况下,dj.chain 将任何具有集体调用所需方法的可迭代对象视为类似查询集的对象。例如,如果您的自定义可迭代对象支持 defer 方法,它将在集体 defer 调用中使用。如果这种行为不受欢迎,您应在构建链时传递 strict=True

c = chain(Article.objects.all(), custom_entries, strict=True)

在这种情况下,上述方法仅在实际查询集实例上调用。请注意,具有自定义其他可迭代对象处理方法(如 countorder_by)的方法仍然有效。

不支持的方法

以下方法在异构环境中无法支持

  • create

  • get_or_create

  • bulk_create

以下方法目前尚不支持,但计划在未来的版本中支持

  • aggregate

  • annotate

  • dates

  • delete

  • distinct

  • get

  • in_bulk

  • reverse

  • update

已知问题

  1. 如果使用切片或 xfilter,则通过迭代所有可迭代对象来计算报告的 len(),因此性能较弱。请注意,len() 在将链转换为列表或在使用 Django 模板遍历链时用作 list()。如果这不是预期的,您可以使用如下方法将链转换为列表

    list(e for e in some_chain)
  2. 链上的索引使用迭代,因此性能较弱。此功能仅作为最后的手段提供。另一方面,切片是惰性的。

  3. 集体 filterexclude 静默跳过在不兼容的可迭代对象上的过滤。请使用 xfilter(value) 作为解决方案。

如何运行测试?

最简单的方法是运行

$ DJANGO_SETTINGS_MODULE="dj._chaintestproject.settings" django-admin.py test

变更日志

0.9.2

  • 久违的 Python 3 支持(视为实验性)

0.9.1

  • 支持集体 valuesvalues_list 转换

  • 支持集体 deferextraonlyprefetch_relatedselect_for_updateselect_relatedusing 方法(对于不兼容的可迭代对象静默忽略)

  • 严格模式(非查询集对象不尝试与集体方法兼容)

  • 修复了由于与 lck.django 不完全分离导致的导入错误。

0.9.0

  • 代码已从 lck.django 分离。

  • 支持使用类似 QuerySet 的链式 order_by 进行批量排序。

  • 修复了具有自定义步长的切片问题。

  • 所有源代码都已遵循PEP8规范。

作者

Łukasz Langa 组装。

项目详情


下载文件

下载适合您平台的文件。如果您不确定选择哪个,请了解有关 安装包 的更多信息。

源代码分发

dj.chain-0.9.2.tar.gz (18.0 kB 查看哈希值)

上传时间 源代码

支持