【问题标题】:Django Model field read only for a MySql generated columnDjango 模型字段只读用于 MySql 生成的列
【发布时间】:2019-09-08 22:38:54
【问题描述】:

我想将generated column 添加到 MySql 数据库中,并且正在寻找一种能够从 Django 模型对象中读取该列的方法,但没有曾经尝试编写一个插入/更新时该列的值,MySql 将对此抛出错误。

我知道您可以在管理站点的表单上轻松地将字段设置为只读,但有没有办法在模型/字段级别进行设置?

【问题讨论】:

  • 你可以给你的字段一个“私人名称”,比如_my_field,并为它创建一个getter(属性)my_field,而不需要setter。如果有人尝试设置它,您还可以覆盖 __setattr__() 以引发异常。但是您仍然需要在迁移文件中使用原始 sql 来创建该列以便生成它。
  • 这会阻止 ORM 在调用 save() 时尝试更新字段?好像不是这样,但我想我可以试试。
  • 不,不会的。 “......并且 MySql 会抛出一个错误”你的意思是说“否则 MySql 会抛出一个错误”还是“我想要抛出一个错误”?您还可以覆盖模型的 save() 方法,调用其 super 方法并始终设置 update_fields 以排除此特定字段。
  • 如果您尝试插入/更新生成的列值,MySql 将抛出错误。我会检查 update_fields。

标签: mysql django django-models django-orm


【解决方案1】:

另一种选择是覆盖默认管理器以返回始终使用该列中的值注释的QuerySet

from django.db.models.expressions import RawSQL

class MyManager(models.Manager):

    def get_queryset(self):
        return super().get_queryset().annotate(
            my_field=RawSQL('''"{table}"."myfield"'''.format(
                table=self.model._meta.db_table
            ), ())
        )

然后在你的模型中设置objects = MyManager(),每当你获取对象时,它们都会被你想要的字段注释:

instance = MyModel.objects.first()
instance.my_field  # value from db annotation

【讨论】:

  • 这似乎可以解决问题,谢谢!唯一的抱怨是refresh_from_db() 似乎没有刷新这个字段,但我可能可以忍受
【解决方案2】:

这是另一个选择:

def get_deferred_fields(self):
    deferred_fields = super(MyModel, self).get_deferred_fields()

    # Exclude MySQL generated columns, which cannot be saved
    deferred_fields.update({
        "my_generated_column_field1",
        "my_generated_column_field2",
    })

    return deferred_fields

注意:get_deferred_fields() 是一个公共方法,它可以被覆盖,但可能会有意想不到的副作用。在我的 django 应用程序中,它只在 save() 期间被调用,而在 refresh_from_db() 期间从未被调用,但它可以。

【讨论】:

    【解决方案3】:

    您应该能够添加一个执行原始 SQL 查询的方法。

    https://docs.djangoproject.com/en/2.2/topics/db/sql/

    【讨论】:

    • 这如何防止模型在您调用 save() 时尝试设置字段?
    • 我认为@MarkBailey 建议不要在模型上创建一个字段,而是一个执行原始 SQL 查询以获取值的方法/属性。这确实意味着每次您需要访问它时都会额外查询一次(您可以使用@cached_property 将其限制为每个请求一次)。
    • 是的,@dirkgroten 是对的。我根本不会为它创建一个字段,正如他在其他地方所说,您将不得不手动创建 SQL 列。我实际上还没有这样做,但这是我会尝试的。
    • 知道了,每次都做一个额外的查询不会扩展,因此不是我想做的事情。
    • 另外 - 它不允许您过滤此字段设置的查询
    猜你喜欢
    • 2021-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-21
    • 1970-01-01
    • 2020-05-30
    • 1970-01-01
    相关资源
    最近更新 更多