问题
I am writing web-interface for hydrologists. Hydrologist should see table with different hydrological measurements like this.
+----------------+----------------------+-------+--------------------+-------------+------------------+
| observation_id | observation_datetime | level | water_temperature |precipitation|precipitation_type|
+----------------+----------------------+-------+--------------------+-------------+------------------+
| 1 | 2019-03-11 11:00:00 | 11 | 21 | 31 |
| 2 | 2019-03-12 12:00:00 | 12 | 22 | 32 |
| 3 | 2019-03-13 13:00:00 | 13 | 23 | 33 |
| 4 | 2019-03-14 14:00:00 | 14 | 24 | 34 |
I have these models for describing measurements
class AbstractMeasurement(model.Model):
observation_datetime = models.DateTimeField()
observation = models.ForeignKey(Observation, on_delete = models.DO_NOTHING)
class Meta:
abstract = True
class Level(AbstractMeasurement):
level = models.DecimalField()
class WaterTemperature(AbstractMeasurement):
air_temperature = models.DecimalField()
class Precipitation(AbstractMeasurement):
precipitation = models.DecimalField()
precipitation_type = models.CharField()
etc.
Level the main measurement and measurement cannot be done without level. Level is the basic model.
In mysql I can do it by this query
SELECT level.observation_id,
level.observation_datetime,
level.level,
water_temperature.water_temperature,
precipitation.precipitation,
precipitation.precipitation_type
FROM level
LEFT JOIN precipitation ON
level.observation_datetime = precipitation.observation_datetime
AND
level.observation_id = precipitation.observation_id
LEFT JOIN water_temperature ON
level.observation_datetime = water_temperature.observation_datetime
AND
level.observation_id = water_temperature.observation_id;
How I can LEFT JOIN in django with models without foreign key relationship?
回答1:
You can achieve what you want, but it would be unnecessarily inefficient (even more so than the SQL query you posted). Since your current model structure is rather contorted anyway, if you can change the models, you should.
That said, here's how to bring e.g. Precipitation
data into your Level
query. You need one subquery per field and row:
from django.db.models import Q, OuterRef, Subquery
join_criteria = Q(
observation_id=OuterRef('observation_id'),
observation_datetime = OuterRef('observation_datetime')
)
subquery_precipitation = Subquery(Precipitation.objects
.filter(join_criteria)
.values('precipitation')[:1])
subquery_precipitation_type = Subquery(Precipitation.objects
.filter(join_criteria)
.values('precipitation_type')[:1])
levels = (Level.objects
.annotate(precipitation=subquery_precipitation)
.annotate(precipitation_type=subquery_precipitation_type))
Now try multiplying the number of fields with the expected number of rows in your query – that's the number of subqueries that would need to be executed.
So this is a proof of concept you can use in a pinch for small tables and a few fields. It's not suitable for large data sets and many fields. You should really rethink your models for that.
With proper models, it should be really easy to achieve what you need.
来源:https://stackoverflow.com/questions/55135563/django-orm-left-join-on-fields-with-same-values