Django 1.8 ArrayField добавляет и расширяет

Django 1.8 будет поставляться с новыми расширенными типами полей, включая ArrayField, которые полагаются на PostgreSQL и реализуются на уровне DB.

Поле массива PostgreSQL реализует метод добавления.

Однако я не могу найти документацию о добавлении элемента в ArrayField. Это было бы очень полезно, так как это позволило бы обновлять поле без переноса всего содержимого из db, а затем обратно на него.

Возможно ли это? Если это будет невозможно в будущем? Любые указатели на документы, которые я пропустил, будут высоко оценены.

Чтобы уточнить, о чем я прошу, это было бы здорово:

Примечание: это фэнтезийный код, и я предполагаю, что он не будет работать (я не пробовал)

# 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

Я думаю, что функции, которые вы ищете, в настоящее время не реализованы (и могут не планироваться). Многие из вкладков Postgres содержат созданный на основе этого проекта kickstarter.

Я нахожу наиболее полезную документацию для новых функций, исходящих из самого самого исходного кода. Которая включает ссылку на исходный запрос на перенос для многих из этих функций.

Важное замечание в отношении упомянутых функций массива - это функции и, возможно, вне рамки типичного ORM.

Я надеюсь, что эта информация полезна, и вы найдете хорошее решение этой проблемы.

Ответ 2

Примечание: код 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.

Ответ 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'])))

Ответ 4

Вы можете использовать django_postgres_extensions. Он поддерживает множество функций, таких как append, prepend, remove, concatenate.

Но если вы используете Django 1.8, как я, вы должны использовать только необходимые классы из этого пакета. Таким образом, вам также не придется менять базу данных базы данных. Я вставил требуемые классы здесь. Используйте их, как описано в первой ссылке.

Ответ 5

Другое решение использует пользовательское выражение. Я проверил следующий код с Django 1.11 и Python 3.6 (f-строки).

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