【问题标题】:Django ORM equivalent for this SQL..calculated field derived from related table此 SQL 的 Django ORM 等效项。从相关表派生的计算字段
【发布时间】:2010-09-11 08:36:57
【问题描述】:

我的模型结构如下:

class Master(models.Model): 
    name = models.CharField(max_length=50)          
    mounting_height = models.DecimalField(max_digits=10,decimal_places=2)   

class MLog(models.Model):                  
    date = models.DateField(db_index=True)
    time = models.TimeField(db_index=True)      
    sensor_reading = models.IntegerField()      
    m_master = models.ForeignKey(Master)

目标是生成一个查询集,返回 MLog 中的所有字段以及基于 Master 中相关数据的计算字段(item_height)

使用 Django 的原始 sql:

querySet = MLog.objects.raw('''
    SELECT a.id,
           date,
           time,
           sensor_reading,
           mounting_height,
          (sensor_reading - mounting_height) as item_height 
    FROM db_mlog a JOIN db_master b 
                     ON a.m_master_id = b.id
''')                                

如何使用 Django 的 ORM 进行编码?

【问题讨论】:

    标签: django django-orm


    【解决方案1】:

    在不依赖raw() 的情况下,我可以想出两种方法来解决这个问题。第一个与@tylerl suggested 几乎相同。像这样的:

    class Master(models.Model):
        name = models.CharField(max_length=50)
        mounting_height = models.DecimalField(max_digits=10,decimal_places=2)
    
    class MLog(models.Model):
        date = models.DateField(db_index=True)
        time = models.TimeField(db_index=True)
        sensor_reading = models.IntegerField()
        m_master = models.ForeignKey(Master)
    
        def _get_item_height(self):
            return self.sensor_reading - self.m_master.mounting_height
        item_height = property(_get_item_height)
    

    在这种情况下,我为MLog 定义了一个名为item_height 的自定义(派生)属性。该属性计算为实例的sensor_reading 与其相关主实例的mounting_height 之差。更多关于propertyhere

    然后你可以这样做:

    In [4]: q = MLog.objects.all()
    
    In [5]: q[0]
    Out[5]: <MLog: 2010-09-11 8>
    
    In [6]: q[0].item_height
    Out[6]: Decimal('-2.00')
    

    第二种方法是使用extra() 方法并让数据库为您进行计算。

    In [14]: q = MLog.objects.select_related().extra(select = 
              {'item_height': 'sensor_reading - mounting_height'})
    
    In [16]: q[0]
    Out[16]: <MLog: 2010-09-11 8>
    
    In [17]: q[0].item_height
    Out[17]: Decimal('-2.00')
    

    您会注意到select_related() 的用法。如果没有这个,Master 表将不会与查询连接,您将收到错误消息。

    【讨论】:

    • 感谢我几分钟前查看了“额外”,感谢您指出正确的语法。
    【解决方案2】:

    我总是在应用程序中而不是在数据库中进行计算。

    class Thing(models.Model):
        foo = models.IntegerField()
        bar = models.IntegerField()     
        @Property
        def diff():
            def fget(self):
                return self.foo - self.bar
            def fset(self,value):
                self.bar = self.foo - value
    

    然后您可以像操作任何其他字段一样操作它,它会执行您使用基础数据定义的任何操作。例如:

    obj = Thing.objects.all()[0]
    print(obj.diff)  # prints .foo - .bar
    obj.diff = 4     # sets .bar to .foo - 4
    

    顺便说一下,属性只是一个标准的属性装饰器,在这种情况下编码如下(我不记得它来自哪里):

    def Property(function):
        keys = 'fget', 'fset', 'fdel'
        func_locals = {'doc':function.__doc__}
        def probeFunc(frame, event, arg):
            if event == 'return':
                locals = frame.f_locals
                func_locals.update(dict((k,locals.get(k)) for k in keys))
                sys.settrace(None)
            return probeFunc
        sys.settrace(probeFunc)
        function()
        return property(**func_locals)
    

    【讨论】:

    • 但是计算出来的字段是从另一个表中导出的...item height是mlog.sensor_reading和master.mounting_height的差
    • 还有property,一个内置函数(从2.2开始)docs.python.org/library/functions.html#property
    • 为什么需要自定义属性装饰器? Python 内置的有什么问题?
    • 属性装饰器利用 2.2 中的内置属性函数,同时将所有定义放在一起。直到 2.6(一些“企业”系统还没有)如果你使用 property() 作为装饰器,你只能创建只读属性。
    猜你喜欢
    • 2018-06-03
    • 2011-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-25
    • 1970-01-01
    • 1970-01-01
    • 2015-10-10
    相关资源
    最近更新 更多