Django 1.8 ArrayField追加&延伸

时间:2015-03-12 16:16:54

标签: python django postgresql django-orm django-1.8

Django 1.8 will come with new advanced field types including ArrayField这些依赖于PostgreSQL并在数据库级别实现。

PostgreSQL的数组字段implements an append method

但是,我无法找到有关将项目附加到ArrayField的任何文档。这显然是非常有用的,因为它允许更新字段而不将其全部内容从数据库传输回它。

这可能吗?如果不是将来可能的话?任何我错过的文档指针都会非常感激。

为了澄清我所问的问题,这会很棒:

注意:这是幻想代码,我认为不会工作(我还没有尝试过)

# on model:
class Post(models.Model):
    tags = ArrayField(models.CharField(max_length=200))

# somewhere else:
p = Post.objects.create(tags=[str(i) for i in range(10000)])
p.tags.append('hello')

目前是否有办法在不使用原始sql的情况下执行此操作?

5 个答案:

答案 0 :(得分:20)

注意:OP代码绝对有用。我们只需要保存模型(因为这些只是模型字段,而不是关系)。我们来看看:

>>> p = Post.objects.create(tags=[str(i) for i in range(10000)])
>>> p.tags.append("working!")
>>> p.save()
>>> working_post = Post.objects.get(tags__contains=["working!"])
<Post: Post object>
>>> working_post.tags[-2:]
[u'9999', u'working!']

更深入

Django将ArrayField作为python列表

Code reference

could do with列出的所有内容,您都可以使用ArrayField。 甚至排序

Django将ArrayField保存为python列表

Code reference

这意味着它保存了python列表的结构和元素。

答案 1 :(得分:10)

我认为您正在寻找的功能目前尚未实施(可能没有计划)。许多Postgres贡献功能originated based on this kickstarter project

我发现新功能最有用的文档来自source code itself。其中包含许多这些功能的原始pull request链接。

关于所提到的数组函数的重要注意事项,它们是函数,可以说是outside the scope of a typical ORM

我希望这些信息很有用,你找到了解决这个问题的好方法。

答案 2 :(得分:9)

这有效:

from django.db.models import F
from django.db.models.expressions import CombinedExpression, Value

post = Post.objects.get(id=1000)
post.tags = CombinedExpression(F('tags'), '||', Value(['hello']))
post.save()

或在更新条款中:

Post.objects.filter(created_on__lt=now() - timespan(days=30))\
    .update(tags=CombinedExpression(F('tags'), '||', Value(['old'])))

答案 3 :(得分:2)

您可以使用django_postgres_extensions。它支持很多函数,如append,prepend,remove,concatenate。

但是如果你像我一样使用Django 1.8,你应该只使用这个包中的必需类。这样,您也不必更改数据库后端。我已粘贴所需的课程here。按照第一个链接中的描述使用它们。

答案 4 :(得分:2)

另一种解决方案是使用自定义表达式。我用Django 1.11和Python 3.6(f-strings)测试了下面的代码。

from django.db.models.expressions import Func

class ArrayAppend(Func):

    function = 'array_append'
    template = "%(function)s(%(expressions)s, %(element)s)"
    arity = 1

    def __init__(self, expression: str, element, **extra):
        if not isinstance(element, (str, int)):
            raise TypeError(
                f'Type of "{element}" must be int or str, '
                f'not "{type(element).__name__}".'
            )

        super().__init__(
            expression,
            element=isinstance(element, int) and element or f"'{element}'",
            **extra,
        )

表达式可用于update()

Post.objects \
    .filter(pk=1) \
    .update(tags=ArrayAppend('tags', 'new tag'))