Django ORM 相当于此 SQL..从相关表派生的计算字段

发布于 2024-09-18 07:57:46 字数 909 浏览 3 评论 0原文

我的模型结构如下:

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 对其进行编码?

I have the following model structure below:

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)

The goal is to produce a queryset that returns all the fields from MLog plus a calculated field (item_height) based on the related data in Master

using Django's raw 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
''')                                

How do I code this using Django's ORM?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

时光磨忆 2024-09-25 07:57:46

我可以想到两种不依赖 raw() 的方法来解决这个问题。第一个与 @tylerl 建议。像这样的事情:

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 之差。有关属性的更多信息此处

然后,您可以执行以下操作:

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_lated() 的使用。如果没有这个,Master 表将不会与查询连接,并且您将收到错误。

I can think of two ways to go about this without relying on raw(). The first is pretty much the same as what @tylerl suggested. Something like this:

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)

In this case I am defining a custom (derived) property for MLog called item_height. This property is calculated as the difference of the sensor_reading of an instance and the mounting_height of its related master instance. More on property here.

You can then do something like this:

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')

The second way to do this is to use the extra() method and have the database do the calculation for you.

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')

You'll note the use of select_related(). Without this the Master table will not be joined with the query and you will get an error.

度的依靠╰つ 2024-09-25 07:57:46

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

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

顺便说一句,Property只是一个标准的属性装饰器,在本例中编码如下(我不记得它来自哪里):

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)

I always do the calculations in the app rather than in the DB.

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

Then you can manipulate it just as you would any other field, and it does whatever you defined with the underlying data. For example:

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

Property, by the way, is just a standard property decorator, in this case coded as follows (I don't remember where it came from):

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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文