繁体   English   中英

Django 1.8 ArrayField 追加和扩展

[英]Django 1.8 ArrayField append & extend

Django 1.8 将带有新的高级字段类型,包括 ArrayField这些依赖于 PostgreSQL 并在数据库级别实现。

PostgreSQL 的数组字段 实现了一个 append 方法

但是我找不到任何关于将项目附加到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 的情况下做到这一点?

注意: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 列表

代码参考

可以用列表做的所有事情,你都可以用 ArrayField 做。 甚至排序

Django 将ArrayField保存为 python 列表

代码参考

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

我认为您正在寻找的功能目前尚未实现(并且可能没有计划)。 许多 Postgres contrib 特性源自这个 kickstarter 项目

我发现新功能最有用的文档来自源代码本身 其中包括指向其中许多功能的原始拉取请求的链接。

关于提到的数组函数的一个重要说明,它们是函数并且可以说超出了典型 ORM 的范围

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

这有效:

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

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

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

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

这 100% 有效

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

Post.objects.filter(created_on__lt=now() - timespan(days=30)).update(tags=CombinedExpression(F('tags'), '||', Value('{hello}')))

注意我的变种和 Yotam Ofek 的区别:

Value(["hello"]) should be changed to Value("{hello}")

此外,如果您需要排除带有某些特定标签的条目,请使用:

.exclude(tags__contains="{hello}")

再过一会,字段“标签”应该有 default=list

tags = ArrayField(models.CharField(max_length=200), default=list)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM