如何通过Django QuerySet基于日期和股票代码关联两个模型?
问题描述
在Django中有两张表,分别存储股票数据(ohlcdata模型)和股票元数据(BulkData模型),每个模型中date与ticker的组合具有唯一性。模型定义如下:
class BulkData(models.Model): date = models.DateField() ticker = models.CharField(max_length=10) name = models.CharField(max_length=400) type = models.CharField(max_length= 20) exchange_short_name = models.CharField(max_length=5) MarketCapitalization = models.DecimalField(max_digits=40, decimal_places=2) request_exchange = models.CharField(max_length=20) class ohlcdata(models.Model): date = models.DateField() ticker = models.CharField(max_length=10, null=False) open = models.DecimalField(max_digits=15, decimal_places=5, null = True) high = models.DecimalField(max_digits=15, decimal_places=5, null = True) low = models.DecimalField(max_digits=15, decimal_places=5, null = True) close = models.DecimalField(max_digits=15, decimal_places=5, null = True) volume = models.DecimalField(max_digits=40, decimal_places=0, null = True)
需要基于date和ticker字段执行左连接操作,目前使用原生SQL查询:
query3 = "SELECT * FROM stocklist_bulkdata LEFT JOIN stocklist_ohlcdata ON `stocklist_bulkdata`.`date` = `stocklist_ohlcdata`.`date` AND `stocklist_bulkdata`.`ticker` = `stocklist_ohlcdata`.`ticker` WHERE `stocklist_bulkdata`.`date` = '2023-03-30'" qs3 = BulkData.objects.raw(query3)
希望改用Django QuerySet实现该操作。
解决方案
由于两个模型未定义外键关联,可通过以下两种方式实现基于date和ticker的左连接查询:
方法1:使用Subquery+Annotate(无需修改模型)
通过子查询将ohlcdata的字段作为注释字段附加到BulkData查询集,模拟左连接效果:
from django.db.models import Subquery, OuterRef # 定义子查询,匹配当前BulkData的date和ticker ohlc_subquery = ohlcdata.objects.filter( date=OuterRef('date'), ticker=OuterRef('ticker') ).values( 'open', 'high', 'low', 'close', 'volume' )[:1] # 构建目标QuerySet,筛选指定日期并关联ohlc数据 qs = BulkData.objects.filter(date='2023-03-30').annotate( ohlc_open=Subquery(ohlc_subquery.values('open')), ohlc_high=Subquery(ohlc_subquery.values('high')), ohlc_low=Subquery(ohlc_subquery.values('low')), ohlc_close=Subquery(ohlc_subquery.values('close')), ohlc_volume=Subquery(ohlc_subquery.values('volume')) )
查询后可通过obj.ohlc_open、obj.ohlc_high等属性访问对应股票数据,无匹配记录时返回None,与左连接行为一致。
方法2:添加复合外键关联(长期推荐方案)
若允许修改模型,建议给ohlcdata添加基于date+ticker的复合外键关联,后续查询会更简洁:
修改模型定义
from django.db.models import UniqueConstraint class BulkData(models.Model): date = models.DateField() ticker = models.CharField(max_length=10) name = models.CharField(max_length=400) type = models.CharField(max_length= 20) exchange_short_name = models.CharField(max_length=5) MarketCapitalization = models.DecimalField(max_digits=40, decimal_places=2) request_exchange = models.CharField(max_length=20) class Meta: constraints = [ UniqueConstraint(fields=['date', 'ticker'], name='unique_date_ticker') ] class ohlcdata(models.Model): bulk_data = models.ForeignKey( BulkData, on_delete=models.CASCADE, related_name='ohlc_data', null=True, blank=True ) date = models.DateField() ticker = models.CharField(max_length=10, null=False) open = models.DecimalField(max_digits=15, decimal_places=5, null = True) high = models.DecimalField(max_digits=15, decimal_places=5, null = True) low = models.DecimalField(max_digits=15, decimal_places=5, null = True) close = models.DecimalField(max_digits=15, decimal_places=5, null = True) volume = models.DecimalField(max_digits=40, decimal_places=0, null = True) class Meta: constraints = [ UniqueConstraint(fields=['date', 'ticker'], name='ohlc_unique_date_ticker') ]
执行数据库迁移后,使用select_related查询
qs = BulkData.objects.filter(date='2023-03-30').select_related('ohlc_data')
此时可通过obj.ohlc_data.open直接访问对应股票数据,无匹配记录时obj.ohlc_data为None。
内容的提问来源于stack exchange,提问作者spacer212
相关产品推荐
相关产品推荐

