【问题标题】:Django get default value of field if its none如果没有,Django会获取字段的默认值
【发布时间】:2014-09-28 00:27:26
【问题描述】:

我有一个旧数据库,我为它创建了一个模型。如下所示。

class Account(models.Model):
  balance = models.DecimalField(max_digits=42, decimal_places=1, default=Decimal('0.0'))

当我运行以下查询时,我得到 balance field 作为 None insted 使用字段上的 default 值。

Account.objects.get(pk=1).balance #returns None instead of Decimal(0.0).

当 SQL 数据库上的旧余额为 NULL 时,我想获得 default Decimal(0.0)。 有没有办法告诉 django orm 给我默认的Decimal(0.0) 而不是None。

提前致谢。

编辑: 我知道我们可以添加自定义方法或@property 并使其成为返回余额或默认值的方法,但我还运行一些其他查询,如下所示。

  Account.objects.values_list('balance') #returns [(1,),(None,),(None,)]

不使用默认值。

【问题讨论】:

  • 将现有的空值转换为0.00 并更改架构以防止空值可能更容易。
  • @Alasdair:我希望但显然这不在我的控制之下。 :(
  • 你可以尝试写一个custom model field。
  • @Alasdair:我会试一试。谢谢,不知道自定义模型字段。惊人的! ;)

标签: python sql django django-models django-queryset


【解决方案1】:

也许您可以定义自己的自定义字段类,在其中覆盖 to_python 方法来处理您的这种特殊情况。 这将变成以下代码行中的内容:

import decimal


class DecimalField(models.DecimalField):

  def to_python(self, value):
    if value is None:
      return decimal.Decimal('0.0')
    super(DecimalField, self).to_python(value)

希望这与您的问题相关。干杯:)

【讨论】:

    【解决方案2】:

    Python 允许您找到解决问题的许多不同方法,因此您可以创建模型方法:

    class MyAppModel(models.Model):
        ....
    
        def get_balace(self):
            return self.balance if self.balance else Decimal(0.0)
        ....
    

    或者您可以创建一个经理(根据您的需要进行更改):

    class MyAppManager(models.Manager):
    
        def get_queryset(self):
            return super(MyAppManager, self).get_queryset()
    
        def get_balances(self):
            results = []
            items = self.get_queryset().all()
            for item in items:
                balance = item.balance if item.balance else Decimal(0,0)
                results.append({'pk':item.pk,"balance":balance})
            return results
    

    然后在你的模型中:

    class MyAppModel(models.Model):
        ...
        objects = MyAppManager()
        ...
    

    而你查询:

    balances = MyAppModel.objects.get_balances()
    

    【讨论】:

    • 这行得通,我有一个比这更好的解决方案,方法是在方法中添加@property 并使其成为属性而不是方法。但是当您运行 Account.objects.values_list('balance') 之类的查询时,它并没有帮助
    • 你可以创建一个Manager :)
    • 我知道您之前已经发布了解决方案,正在编辑答案,您可以再发布一次吗?谢谢:)
    • 添加,重构你的需要:)
    • 这是一个老问题(和答案),但为了将来有兴趣的人着想,我觉得 self.balance if self.balance else Decimal(0.0) 在查询集上加上注释会更好地解决,把努力工作推到数据库而不是python。这也将避免重新获取查询集并返回一个奇怪的自定义对象。
    【解决方案3】:

    在列上指定默认值仅意味着如果您不传递值,则将使用默认值。您是否将此列的值显式设置为NULL?如果是这样,则将存储NULL。您尚未提供用于填充模型的查询。

    考虑一下这张表..

    CREATE TABLE testtbl 
    (
        id NOT NULL PRIMARY KEY,
        balance NUMERIC NULL DEFAULT 0
    )
    

    现在考虑这两个插入语句

    INSERT INTO testtbl (id, balance) VALUES (1, NULL);
    INSERT INTO testtbl (id) VALUES (2);
    

    在第一种情况下,即使您指定了默认值,NULL 也会被插入。在第二种情况下,0 将用于balance。

    希望这会有所帮助。

    【讨论】:

    • 是的,但它是一个遗留数据库,它已经插入了带有NULL 列的数据,而且插入不在我的控制之下,它们是由我的 ETL 完成的。
    • 所以您只想将NULLs 显示为0.0?
    • 是的,但是使用django models default
    • 你不能在渲染模板时将Nones 转换为 0 ({{ value|default_if_none:0.0 }})。或者,如果在视图中,if a is None: a = 5?
    • 是的,这是一个选项,但它不是一个优雅的选项,我必须在视图级别为每个包含小数字段的模型执行此操作。 petkostas 的答案可能是 DRY,并且可能会处理所有包含 Decimal 字段的模型。 ;)
    【解决方案4】:

    设置editable=False。更多信息请查看this。

    balance = models.DecimalField(max_digits=42, decimal_places=1, default=Decimal('0.0'), editable=False)
    

    【讨论】:

    • 作为记录,这没有用,也没有任何意义。
    • 作为记录,当你写editable=False时,这意味着它不会出现在管理面板中,并且每次在数据库中输入新记录时都会分配默认值。您在模型中提到的默认值将分配给它。努力尝试它会开始有意义...:-)
    • 在我也没有使用它的问题中,我没有提到django-admin。我希望您了解遗留数据库的含义。在回答之前阅读并理解问题。
    • @Pannu 不用生气,放轻松。我们在这里互相帮助。现在来评论,我解释了为什么我写了editable=False,就是这样。如果您觉得答案不能解决您的问题,您可以投反对票。这就是 SO 的工作原理。
    猜你喜欢
    • 1970-01-01
    • 2017-01-28
    • 1970-01-01
    • 2012-01-23
    • 1970-01-01
    • 2018-02-13
    • 2010-11-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多