【问题标题】:Can't query from django where field name has double underscore无法从字段名称有双下划线的 django 查询
【发布时间】:2019-09-25 12:12:40
【问题描述】:

无法从字段名称有双下划线的 django 查询。 这是因为在 django 查询语言中__ 有自己的含义

那么如何查询实际名称为"my__fyeild__name" 的字段?

template.fields.filter(my__fyeild__name="aaa")

在数据库中,该列的名称带有两个下划线__,我不允许重命名该列。

【问题讨论】:

  • 为什么要定义一个带双下划线的字段名呢?据我所知,您不能查询带有双下划线的字段,因为 Django 会首先将这些字段按__ 部分分开。
  • 对“我如何做 X?”的最糟糕的评价SO中的问题是“你为什么想做X”?但是,如果您必须知道,我正在处理由其他人创建的数据库表,该表的字段名称为“__”。
  • 不,不是,因为 Django 可以完美处理名称与 Django 中名称不同的字段,所以这就是著名的 XY 问题 :) en.wikipedia.org/wiki/XY_problem
  • 其实很常见的现象是,如果有人问“如何做 Y”,答案是“你一开始就不应该做 Y,你可以在更好的方法”。这就是为什么要问“你为什么想做 Y?”很有用,因为它有时可以以更好的方式解决。

标签: django django-models django-orm


【解决方案1】:

你不能,因为 Django 总是在 __ 部分分裂。但我认为你无论如何都不需要这个。您可以在 Django 级别定义一个字段,该字段在数据库级别具有不同的名称。

我们可以定义一个模型,例如使用一个字段,我们在数据库级别使用db_column=... parameter [Django-doc] 指定列的名称:

class SomeModel(models.Model):
    my_field_name = models.CharField(max_length=128, db_column='my__field__name')

所以这里可以查询my_field_name,Django会自动在查询中使用my__field__name

【讨论】:

    【解决方案2】:

    我不确定我们为什么要在模型字段中添加 双下划线。 Django 使用双下划线表示法进行查找。

    From the doc of Field name restrictions

    由于 Django 的查询查找语法的工作方式,字段名称不能在一行中包含多个下划线。例如:

    class Example(models.Model):
        foo__bar = models.IntegerField() # 'foo__bar' has two underscores!
    

    【讨论】:

      【解决方案3】:

      免责声明:当我说这是一个“解决方案”时,我是在讽刺。这是一个糟糕的想法,在我看来,永远不应该实施。我只是为了更清楚地说明为什么你不能将__添加到模型列,希望你能重新考虑手头的问题。

      作为对您不能有双下划线字段名称的回答的回应:

      是的,你可以

      这很简单,只需转到django/db/models/constants.py 并将LOOKUP_SEP 更改为您想要的任何内容(在此示例中,我将使用_some_new_sep_

      这里有一些例子:

      我的应用程序/模型:

      from django.db import models
      
      class Foo(models.Model):
          test__col = models.IntegerField()
      
      class Bar(models.Model):
          foo = models.ForeignKey(Foo, related_name='bar', on_delete=models.CASCADE)
          title = models.CharField(max_length=45)
      

      用法:

      >>> from myapp.models import Foo, Bar
      >>> Foo.objects.create(test__col=1)
      <Foo: Foo object (1)>
      >>> Bar.objects.create(foo_id=1, title='test')
      <Bar: Bar object (1)>
      >>>
      >>> Foo.objects.filter(bar_some_new_sep_title='test')
      <QuerySet [<Foo: Foo object (1)>]>
      

      只有一个小缺点,它将导致任何将默认LOOKUP_SEP 硬编码为中断的第三方应用程序。事实证明,这几乎是每个使用它的应用程序(包括默认的 django 应用程序)。

      正如 WilliamVanOsem 在 cmets 中所说:

      事实上,如果一个人问“如何做 Y”,答案是“你一开始就不应该做 Y,你可以用更好的方式解决原来的问题”,这是一个非常普遍的现象。

      【讨论】:

      • 我会谨慎声明这一点,因为它不是文档的一部分 (docs.djangoproject.com/en/2.2/search/?q=LOOKUP_SEP)。从技术上讲,确实可以做到这一点,但由于没有记录,实施者可以轻松改变主意,而且我不确定所有 Django 工具是否都适用于这个常量。 +1 离 :)
      • @WillemVanOnsem 感谢您的评论。我在答案的顶部添加了免责声明,因此不会误导任何人认为这是一个的想法。
      • 由于LOOKUP_SEP 没有记录在案,它可能不应该改变。
      • 我喜欢这个答案在技术上是正确的,但仍然是一个糟糕的主意。