postgresql 列“xx_xx”只能更新为DEFAULT - DJANGO

uplii1fm  于 12个月前  发布在  PostgreSQL
关注(0)|答案(2)|浏览(102)

我试图更新Postgres上包含Generated列的表中的一些值(通过django)。
这是我得到的错误:列“xx_xx”只能更新为DEFAULT
See complete error here

cygmwpex

cygmwpex1#

我知道这个问题很老了,但以后可能有人会发现它。
尽管在Postgres12(4年前)中引入了生成列,但我在Django中找不到对它的适当支持,所以我自己构建了一个
这个答案的灵感来自https://code.djangoproject.com/ticket/21454
Django文档https://docs.djangoproject.com/en/4.2/howto/custom-model-fields/
这是一个通用字段[接受类似于ArrayField的base_field],您可以将其用作

line_total = GeneratedField(
    base_field=models.IntegerField(),
    expression=F("price") * F("quantity"),
    help_text=_("line total, calculated as = price * quantity"),
)

字符串
请注意,您不能引用其他模型中的字段,也不能在表达式中使用子查询或其他生成的字段,如postgresql docs中所述

from django.db.models import Field
from django.db.models.expressions import BaseExpression
from django.db.models.expressions import Combinable
from django.db.models.fields.mixins import CheckFieldDefaultMixin
from psycopg2.extensions import AsIs
from psycopg2.extensions import register_adapter

class PostgresDefaultValueType:
    pass

register_adapter(PostgresDefaultValueType, lambda _: AsIs("DEFAULT"))

class GeneratedField(CheckFieldDefaultMixin, Field):
    def __init__(self, base_field, expression: BaseExpression | Combinable, **kwargs):
        self.base_field = base_field
        self.expression = expression
        kwargs["editable"] = False
        kwargs["blank"] = True
        kwargs["default"] = None
        if hasattr(self.base_field, "from_db_value"):
            self.from_db_value = self._from_db_value
        super().__init__(**kwargs)

    @property
    def model(self):
        try:
            return self.__dict__["model"]
        except KeyError:
            raise AttributeError("'%s' object has no attribute 'model'" % self.__class__.__name__)

    @model.setter
    def model(self, model):
        self.__dict__["model"] = model
        self.base_field.model = model

    def set_attributes_from_name(self, name):
        super().set_attributes_from_name(name)
        self.base_field.set_attributes_from_name(name)

    @property
    def description(self):
        return "Generated Field of %s" % self.base_field.description

    def has_default(self):
        return True

    def _from_db_value(self, value, expression, connection):
        if value is None:
            return value
        return self.base_field.from_db_value(value, expression, connection)

    def db_type(self, connection):
        from django.db.models.sql.query import Query

        base_type = self.base_field.db_type(connection)

        query = Query(model=self.model, alias_cols=False)
        compiler = query.get_compiler(connection=connection)
        sql, params = self.expression.resolve_expression(query).as_sql(compiler=compiler, connection=connection)
        expr = sql % tuple(connection.schema_editor().quote_value(p) for p in params)
        return f"{base_type} GENERATED ALWAYS AS ({expr}) STORED"

    def deconstruct(self):
        name, path, args, kwargs = super().deconstruct()

        kwargs.update({"base_field": self.base_field.clone(), "expression": self.expression})
        return name, path, args, kwargs

    def formfield(self, **kwargs):
        kwargs.setdefault("form_class", self.base_field.formfield())
        return super().formfield(**kwargs)

    def get_prep_value(self, value):
        # inspired by https://code.djangoproject.com/ticket/21454
        if value is None:  # avoid adding DEFAULT to the query when migrating
            return super().get_prep_value(value)
        return PostgresDefaultValueType()


免责声明:正如你的问题中提到的,这个答案只适用于PostgreSQL,我猜稍微修改一下就可以让它适用于MySQL了。
如果您觉得有用,请将其标记为接受的答案

kgsdhlau

kgsdhlau2#

您不能以保存/更新其他列的方式编辑生成的列
正如PostgreSQL documentation所述
不能直接写入生成的列。在UPDATE或UPDATE命令中,不能为生成的列指定值,但可以指定关键字DEFAULT。

相关问题