Django 1.8 数组字段追加和扩展

Posted

技术标签:

【中文标题】Django 1.8 数组字段追加和扩展【英文标题】:Django 1.8 ArrayField append & extend 【发布时间】:2015-05-14 21:58:54 【问题描述】:

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 的情况下做到这一点?

【问题讨论】:

【参考方案1】:

注意: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列表的结构和元素。

【讨论】:

它实际上并没有附加到数据库级别。它将检索数组中的所有元素,追加一个列表,然后将整个列表发送回数据库。【参考方案2】:

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

我发现新功能最有用的文档来自source code itself。其中包括指向原始 pull request 的链接,以了解其中的许多功能。

关于提到的数组函数的重要说明,它们是函数,可以说是outside the scope of a typical ORM。

我希望这些信息有用,并且您可以找到解决此问题的好方法。

【讨论】:

谢谢,这就是我所期望/担心的。如果有人在代码中提供了一个优雅的解决方案,我会留下这个问题。 这些期货已实现。请查看我的回答。 @Daniil 你所说的一切都是完全准确的。我可能误解了这个问题,但我认为 SColvin 正在寻找使用 Postgres 函数而不是执行 UPDATE 并将整个数组从 Python 发送回 Postgres。 @erik-e,我的错,对不起!【参考方案3】:

这行得通:

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'])))

【讨论】:

这对我不起作用。我在 postgres 方面收到一个类型错误:它将附加的值('old')在你的例子中)作为类型文本(],并且已经在数据库中的值作为类型可变 [],然后抱怨它可以'不要对这些类型进行操作。关于如何解决这个问题的任何想法?我尝试为 Value 指定一个 ArrayField(CharField(..)) 的 output_field,同样的行为。【参考方案4】:

另一种解决方案是使用自定义表达式。我用 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'))

【讨论】:

【参考方案5】:

您可以使用django_postgres_extensions。它支持很多功能,如追加、前置、删除、连接。

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

【讨论】:

以上是关于Django 1.8 数组字段追加和扩展的主要内容,如果未能解决你的问题,请参考以下文章

csharp C#:在.NET中合并,追加,扩展两个数组(csharp,mono)

thinkphp 单图上传组建成数组然后追加到一个字段

有人可以在快速数组中给出“如果不存在则追加”方法的片段吗?

Django nginx 和追加斜杠问题

Vue 计算数组追加值而不是重写

递归函数和列表追加/扩展