将 LEFT OUTER JOIN 查询转换为 Django orm queryset/query
Converting LEFT OUTER JOIN query to Django orm queryset/query
鉴于 PostgreSQL 9.2.10、Django 1.8、python2.7.5 和以下模型:
class restProdAPI(models.Model):
rest_id = models.PositiveIntegerField(primary_key=True)
rest_host = models.CharField(max_length=20)
rest_ip = models.GenericIPAddressField(default='0.0.0.0')
rest_mode = models.CharField(max_length=20)
rest_state = models.CharField(max_length=20)
class soapProdAPI(models.Model):
soap_id = models.PositiveIntegerField(primary_key=True)
soap_host = models.CharField(max_length=20)
soap_ip = models.GenericIPAddressField(default='0.0.0.0')
soap_asset = models.CharField(max_length=20)
soap_state = models.CharField(max_length=20)
以及以下 returns 正是我要查找的原始查询:
SELECT
app_restProdAPI.rest_id, app_soapProdAPI.soap_id, app_restProdAPI.rest_host, app_restProdAPI.rest_ip, app_soapProdAPI.soap_asset, app_restProdAPI.rest_mode, app_restProdAPI.rest_state
FROM
app_soapProdAPI
LEFT OUTER JOIN
app_restProdAPI
ON
((app_restProdAPI.rest_host = app_soapProdAPI.soap_host)
OR
(app_restProdAPI.rest_ip = app_soapProdAPI.soap_ip))
WHERE
app_restProdAPI.rest_mode = 'Excluded';
哪个returns像这样:
rest_id | soap_id | rest_host | rest_ip | soap_asset | rest_mode | rest_state
---------+---------+---------------+----------------+------------+-----------+-----------
1234 | 12345 | 1G24019123ABC | 123.123.123.12 | A1234567 | Excluded | Up
使用 Django 的模型和 orm 结构进行这项工作的最佳方法是什么?
我一直在寻找完全没有关系地连接两个表的可能方法,但似乎没有一种干净或有效的方法来做到这一点。我也曾尝试寻找在 django 中进行左外连接的方法,但文档又很少或难以破译。
我知道我可能不得不使用 Q
对象来执行我在其中的 or 子句。此外,我查看了关系,看起来 foreignkey()
可能有效,但我不确定这是否是最好的方法。任何和所有帮助将不胜感激。提前谢谢你。
** 编辑 1 **
到目前为止,Todor 已经提供了一个使用 INNER JOIN 的有效解决方案。我可能已经找到了解决方案 HERE 如果任何人都可以破译内联原始文件 html.
** 编辑 2 **
有没有一种方法可以像我上面给出的查询,Todor 的回答那样,在某个字段(其中某些内容 = 'something')上进行过滤?我尝试了以下但它仍然包括所有记录,即使我的等效 postresql 查询按预期工作。似乎我无法将所有内容都放在我所做的地方,因为当我删除其中一个 or 语句并只执行一个 and 语句时,它会应用排除的过滤器。
soapProdAPI.objects.extra(
select = {
'rest_id' : 'app_restprodapi.rest_id',
'rest_host' : 'app_restprodapi.rest_host',
'rest_ip' : 'app_restprodapi.rest_ip',
'rest_mode' : 'app_restprodapi.rest_mode',
'rest_state' : 'app_restprodapi.rest_state'
},
tables = ['app_restprodapi'],
where = ['app_restprodapi.rest_mode=%s \
AND app_restprodapi.rest_host=app_soapprodapi.soap_host \
OR app_restprodapi.rest_ip=app_soapprodapi.soap_ip'],
params = ['Excluded']
)
** 编辑 3 / 现有解决方案 **
迄今为止,Todor 使用 INNER JOIN 提供了最完整的答案,但希望这个问题能够引发对如何实现这一点的思考。由于这似乎并非天生就可能,因此欢迎任何和所有建议,因为它们可能会导致更好的解决方案。也就是说,使用 Todor 的回答,我能够完成我需要的确切查询:
restProdAPI.objects.extra(
select = {
'soap_id' : 'app_soapprodapi.soap_id',
'soap_asset' : 'app_soapprodapi.soap_asset'
},
tables = ['app_soapprodapi'],
where = ['app_restprodapi.rest_mode = %s',
'app_soapprodapi.soap_host = app_restprodapi.rest_host OR \
app_soapprodapi.soap_ip = app_restprodapi.rest_ip'
],
params = ['Excluded']
)
** TLDR **
我想将此 PostGreSQL 查询转换为 Django 提供的 ORM,而不使用 .raw()
或任何原始查询代码。如果可以促进这一点,并且从性能的角度来看,这是最好的方法,我完全愿意将模型更改为具有外键。如果这对设计有帮助,我将使用与 django-datatables-view 一起返回的对象。
我认为这可能对您有用!实际上,您可以使用 Q 来构造您的查询。
我尝试使用 Django shell,我创建了一些数据并做了如下操作:
restProdAPI.objects.filter(Q(rest_host=s1.soap_host)|Q(rest_ip=s1.soap_ip))
其中 s1 是一个 soapProdAPI。
这就是我写的所有代码,你可以试试看能不能帮到你
from django.db.models import Q
from core.models import restProdAPI, soapProdAPI
s1 = soapProdAPI.objects.get(soap_id=1)
restProdAPI.objects.filter(Q(rest_id=s1.soap_id)|Q(rest_ip=s1.soap_ip))
用 INNER JOIN 解决
如果您只能使用包含相应 restProdAPI
的 soapProdAPI's
(根据您的连接语句 -> 通过主机或 ip 链接) .您可以尝试以下方法:
soapProdAPI.objects.extra(
select = {
'rest_id' : "app_restProdAPI.rest_id",
'rest_host' : "app_restProdAPI.rest_host",
'rest_ip' : "app_restProdAPI.rest_ip",
'rest_mode' : "app_restProdAPI.rest_mode",
'rest_state': "app_restProdAPI.rest_state"
},
tables = ["app_restProdAPI"],
where = ["app_restProdAPI.rest_host = app_soapProdAPI.soap_host \
OR app_restProdAPI.rest_ip = app_soapProdAPI.soap_ip"]
)
如何筛选更多?
因为我们使用的是 .extra
我建议仔细阅读文档。通常我们不能将 .filter
与 select
dict 中的某些字段一起使用,因为它们不是 soapProdAPI
的一部分并且 Django 无法解析它们。我们必须坚持使用 .extra
中的 where
kwarg
,因为它是一个列表,我们最好再添加一个元素。
where = ["app_restProdAPI.rest_host = app_soapProdAPI.soap_host \
OR app_restProdAPI.rest_ip = app_soapProdAPI.soap_ip",
"app_restProdAPI.rest_mode=%s"
],
params = ['Excluded']
重复子查询
如果你真的需要所有 soapProdAPI's
,不管它们是否有相应的 restProdAPI
我只能想到一个丑陋的例子,其中 subquery
对你需要的每个字段重复。
soapProdAPI.objects.extra(
select = {
'rest_id' : "(select rest_id from app_restProdAPI where app_restProdAPI.rest_host = app_soapProdAPI.soap_host OR app_restProdAPI.rest_ip = app_soapProdAPI.soap_ip)",
'rest_host' : "(select rest_host from app_restProdAPI where app_restProdAPI.rest_host = app_soapProdAPI.soap_host OR app_restProdAPI.rest_ip = app_soapProdAPI.soap_ip)",
'rest_ip' : "(select rest_ip from app_restProdAPI where app_restProdAPI.rest_host = app_soapProdAPI.soap_host OR app_restProdAPI.rest_ip = app_soapProdAPI.soap_ip)",
'rest_mode' : "(select rest_mode from app_restProdAPI where app_restProdAPI.rest_host = app_soapProdAPI.soap_host OR app_restProdAPI.rest_ip = app_soapProdAPI.soap_ip)",
'rest_state': "(select rest_state from app_restProdAPI where app_restProdAPI.rest_host = app_soapProdAPI.soap_host OR app_restProdAPI.rest_ip = app_soapProdAPI.soap_ip)"
},
)
鉴于 PostgreSQL 9.2.10、Django 1.8、python2.7.5 和以下模型:
class restProdAPI(models.Model):
rest_id = models.PositiveIntegerField(primary_key=True)
rest_host = models.CharField(max_length=20)
rest_ip = models.GenericIPAddressField(default='0.0.0.0')
rest_mode = models.CharField(max_length=20)
rest_state = models.CharField(max_length=20)
class soapProdAPI(models.Model):
soap_id = models.PositiveIntegerField(primary_key=True)
soap_host = models.CharField(max_length=20)
soap_ip = models.GenericIPAddressField(default='0.0.0.0')
soap_asset = models.CharField(max_length=20)
soap_state = models.CharField(max_length=20)
以及以下 returns 正是我要查找的原始查询:
SELECT
app_restProdAPI.rest_id, app_soapProdAPI.soap_id, app_restProdAPI.rest_host, app_restProdAPI.rest_ip, app_soapProdAPI.soap_asset, app_restProdAPI.rest_mode, app_restProdAPI.rest_state
FROM
app_soapProdAPI
LEFT OUTER JOIN
app_restProdAPI
ON
((app_restProdAPI.rest_host = app_soapProdAPI.soap_host)
OR
(app_restProdAPI.rest_ip = app_soapProdAPI.soap_ip))
WHERE
app_restProdAPI.rest_mode = 'Excluded';
哪个returns像这样:
rest_id | soap_id | rest_host | rest_ip | soap_asset | rest_mode | rest_state
---------+---------+---------------+----------------+------------+-----------+-----------
1234 | 12345 | 1G24019123ABC | 123.123.123.12 | A1234567 | Excluded | Up
使用 Django 的模型和 orm 结构进行这项工作的最佳方法是什么?
我一直在寻找完全没有关系地连接两个表的可能方法,但似乎没有一种干净或有效的方法来做到这一点。我也曾尝试寻找在 django 中进行左外连接的方法,但文档又很少或难以破译。
我知道我可能不得不使用 Q
对象来执行我在其中的 or 子句。此外,我查看了关系,看起来 foreignkey()
可能有效,但我不确定这是否是最好的方法。任何和所有帮助将不胜感激。提前谢谢你。
** 编辑 1 **
到目前为止,Todor 已经提供了一个使用 INNER JOIN 的有效解决方案。我可能已经找到了解决方案 HERE 如果任何人都可以破译内联原始文件 html.
** 编辑 2 **
有没有一种方法可以像我上面给出的查询,Todor 的回答那样,在某个字段(其中某些内容 = 'something')上进行过滤?我尝试了以下但它仍然包括所有记录,即使我的等效 postresql 查询按预期工作。似乎我无法将所有内容都放在我所做的地方,因为当我删除其中一个 or 语句并只执行一个 and 语句时,它会应用排除的过滤器。
soapProdAPI.objects.extra(
select = {
'rest_id' : 'app_restprodapi.rest_id',
'rest_host' : 'app_restprodapi.rest_host',
'rest_ip' : 'app_restprodapi.rest_ip',
'rest_mode' : 'app_restprodapi.rest_mode',
'rest_state' : 'app_restprodapi.rest_state'
},
tables = ['app_restprodapi'],
where = ['app_restprodapi.rest_mode=%s \
AND app_restprodapi.rest_host=app_soapprodapi.soap_host \
OR app_restprodapi.rest_ip=app_soapprodapi.soap_ip'],
params = ['Excluded']
)
** 编辑 3 / 现有解决方案 **
迄今为止,Todor 使用 INNER JOIN 提供了最完整的答案,但希望这个问题能够引发对如何实现这一点的思考。由于这似乎并非天生就可能,因此欢迎任何和所有建议,因为它们可能会导致更好的解决方案。也就是说,使用 Todor 的回答,我能够完成我需要的确切查询:
restProdAPI.objects.extra(
select = {
'soap_id' : 'app_soapprodapi.soap_id',
'soap_asset' : 'app_soapprodapi.soap_asset'
},
tables = ['app_soapprodapi'],
where = ['app_restprodapi.rest_mode = %s',
'app_soapprodapi.soap_host = app_restprodapi.rest_host OR \
app_soapprodapi.soap_ip = app_restprodapi.rest_ip'
],
params = ['Excluded']
)
** TLDR **
我想将此 PostGreSQL 查询转换为 Django 提供的 ORM,而不使用 .raw()
或任何原始查询代码。如果可以促进这一点,并且从性能的角度来看,这是最好的方法,我完全愿意将模型更改为具有外键。如果这对设计有帮助,我将使用与 django-datatables-view 一起返回的对象。
我认为这可能对您有用!实际上,您可以使用 Q 来构造您的查询。 我尝试使用 Django shell,我创建了一些数据并做了如下操作:
restProdAPI.objects.filter(Q(rest_host=s1.soap_host)|Q(rest_ip=s1.soap_ip))
其中 s1 是一个 soapProdAPI。
这就是我写的所有代码,你可以试试看能不能帮到你
from django.db.models import Q
from core.models import restProdAPI, soapProdAPI
s1 = soapProdAPI.objects.get(soap_id=1)
restProdAPI.objects.filter(Q(rest_id=s1.soap_id)|Q(rest_ip=s1.soap_ip))
用 INNER JOIN 解决
如果您只能使用包含相应 restProdAPI
的 soapProdAPI's
(根据您的连接语句 -> 通过主机或 ip 链接) .您可以尝试以下方法:
soapProdAPI.objects.extra(
select = {
'rest_id' : "app_restProdAPI.rest_id",
'rest_host' : "app_restProdAPI.rest_host",
'rest_ip' : "app_restProdAPI.rest_ip",
'rest_mode' : "app_restProdAPI.rest_mode",
'rest_state': "app_restProdAPI.rest_state"
},
tables = ["app_restProdAPI"],
where = ["app_restProdAPI.rest_host = app_soapProdAPI.soap_host \
OR app_restProdAPI.rest_ip = app_soapProdAPI.soap_ip"]
)
如何筛选更多?
因为我们使用的是 .extra
我建议仔细阅读文档。通常我们不能将 .filter
与 select
dict 中的某些字段一起使用,因为它们不是 soapProdAPI
的一部分并且 Django 无法解析它们。我们必须坚持使用 .extra
中的 where
kwarg
,因为它是一个列表,我们最好再添加一个元素。
where = ["app_restProdAPI.rest_host = app_soapProdAPI.soap_host \
OR app_restProdAPI.rest_ip = app_soapProdAPI.soap_ip",
"app_restProdAPI.rest_mode=%s"
],
params = ['Excluded']
重复子查询
如果你真的需要所有 soapProdAPI's
,不管它们是否有相应的 restProdAPI
我只能想到一个丑陋的例子,其中 subquery
对你需要的每个字段重复。
soapProdAPI.objects.extra(
select = {
'rest_id' : "(select rest_id from app_restProdAPI where app_restProdAPI.rest_host = app_soapProdAPI.soap_host OR app_restProdAPI.rest_ip = app_soapProdAPI.soap_ip)",
'rest_host' : "(select rest_host from app_restProdAPI where app_restProdAPI.rest_host = app_soapProdAPI.soap_host OR app_restProdAPI.rest_ip = app_soapProdAPI.soap_ip)",
'rest_ip' : "(select rest_ip from app_restProdAPI where app_restProdAPI.rest_host = app_soapProdAPI.soap_host OR app_restProdAPI.rest_ip = app_soapProdAPI.soap_ip)",
'rest_mode' : "(select rest_mode from app_restProdAPI where app_restProdAPI.rest_host = app_soapProdAPI.soap_host OR app_restProdAPI.rest_ip = app_soapProdAPI.soap_ip)",
'rest_state': "(select rest_state from app_restProdAPI where app_restProdAPI.rest_host = app_soapProdAPI.soap_host OR app_restProdAPI.rest_ip = app_soapProdAPI.soap_ip)"
},
)