使用与现有字段相同的名称注释新字段

Dio*_*ias 5 django django-models django-orm django-views django-rest-framework

我们现在面临这样的情况,我想获得更有经验的用户\xe2\x80\x99s的意见。我们现在是 Django 4、Python 3.9。

\n

目前的情况:

\n

我们的系统已经在生产中运行了一段合理的时间,我们需要根据用户的选择在后端更改一些返回到我们的前端应用程序(Web、mobile\xe2\x80\xa6)的数据。

\n

重要的是:目前无法选择前端方面的更改。

\n

我们需要根据另一个表上现有或不对应的数据覆盖一个模型中的某些属性。

\n

表格(Django 模型):

\n

AgentSearch(该数据每天都会被 ETL 过程替换)

\n
    \n
  • ID
  • \n
  • 标识符号(唯一)
  • \n
  • 成员名字
  • \n
  • 成员姓氏
  • \n
  • 会员邮箱
  • \n
  • 一些其他属性
  • \n
\n

代理(代理可以定义的可选数据)

\n
    \n
  • ID
  • \n
  • agent_search_identifier_number(FK 一对一)
  • \n
  • \n
  • \n
  • 电子邮件
  • \n
\n

对于我们现在的要求,所有查询集都将从 开始进行AgentSearch。这个想法是获取Agent数据,而不是AgentSearch每次有代理链接到数据时就获取数据。

\n

今天我们得到这样的结果:

\n

AgentSearch.objects.all()

\n

我知道像这样注释新列会更容易:

\n
        # using a different attribute name\n        NEW_member_first_name=Case(\n            When(\n                agent__id__isnull=True,\n                then=F("member_first_name"),\n            ),\n            default=F("agent__first_name"),\n        ),\n
Run Code Online (Sandbox Code Playgroud)\n

这将需要我们对前端进行更改。

\n

如果我们尝试:

\n
        # using the same attribute name\n        member_first_name=Case(\n            When(\n                agent__id__isnull=True,\n                then=F("member_first_name"),\n            ),\n            default=F("agent__first_name"),\n        ),\n
Run Code Online (Sandbox Code Playgroud)\n

Django 会抛出:

\n
`ValueError Exception saying: The annotation 'member_first_name' conflicts with a field on the model.`\n
Run Code Online (Sandbox Code Playgroud)\n

我们已经尝试从基本查询中删除“可覆盖”属性。例子:

\n
        # Tell Django ORM to not get the column from DB\n        AgentSearch.objects.all().defer('member_first_name')\n        .annotate(\n             member_first_name=Case(\n                 When(\n                     agent__id__isnull=True,\n                     then=F("member_first_name"),\n                 ),\n                 default=F("agent__first_name"),\n             ),\n
Run Code Online (Sandbox Code Playgroud)\n

仍然:

\n
`ValueError Exception saying: The annotation 'member_first_name' conflicts with a field on the model.`\n
Run Code Online (Sandbox Code Playgroud)\n

还尝试仅强制基本查询中的不可“覆盖”属性的值。例子:

\n
        # Tell Django ORM to not get the column from DB\n        AgentSearch.objects.all().values('some_other_attrbs')\n        .annotate(\n             member_first_name=Case(\n                 When(\n                     agent__id__isnull=True,\n                     then=F("member_first_name"),\n                 ),\n                 default=F("agent__first_name"),\n             ),\n
Run Code Online (Sandbox Code Playgroud)\n

仍然:

\n
`ValueError Exception saying: The annotation 'member_first_name' conflicts with a field on the model.`\n
Run Code Online (Sandbox Code Playgroud)\n

我还尝试创建一个 django 上下文管理器来执行此操作,但它会抛出相同的错误。

\n

在您看来,什么是一种优雅的方式来做到这一点?我们找到了解决方案,但看起来很丑陋。

\n

目前如何运作:

\n
class BaseAgentSearch(AbstractBaseModel):\n    ...\n    class Meta:\n        abstract = True\n\nclass AgentSearch(BaseAgentSearch):\n    class Meta:\n        db_table = "app_agentsearch"\n        # setting to false, since this model will act only as a "reader"\n        managed = False\n    ###########################################################\n    # Original DB columns are mapped to a temp_ attribute name\n    ###########################################################\n    temp_member_first_name = models.CharField(\n        max_length=64, db_column="member_first_name"\n    )\n    temp_member_last_name = models.CharField(\n        max_length=64, db_column="member_last_name"\n    )\n    temp_member_email = models.EmailField(\n        max_length=64, blank=True, db_column="member_email"\n    )\n\n\nclass AgentSearchWrite(BaseAgentSearch):\n    class Meta:\n        db_table = "app_agentsearch"\n\n    member_first_name = models.CharField(max_length=64)\n    member_last_name = models.CharField(max_length=64)\n    member_email = models.EmailField(max_length=64, blank=True)\n
Run Code Online (Sandbox Code Playgroud)\n

然后查询为:

\n
    AgentSearch.objects.all()\n    .annotate(\n        member_first_name=Case(\n            When(\n                agent__id__isnull=True,\n                then=F("temp_member_first_name"),\n            ),\n            default=F("agent__first_name"),\n        ),\n        member_last_name=Case(\n            When(agent__id__isnull=True, then=F("temp_member_last_name")),\n            default=F("agent__last_name"),\n        ),\n        member_email=Case(\n            When(agent__id__isnull=True, then=F("temp_member_email")),\n            default=F("agent__email"),\n        ),\n
Run Code Online (Sandbox Code Playgroud)\n

这按预期工作。但看起来真的很奇怪而且丑陋。而且,为了添加/更新数据,我们现在应该使用AgentSearchWrite在数据库和模型上具有正确列的模型。

\n