Django查询加入

时间:2015-10-08 12:25:14

标签: python mysql django join sqlalchemy

我需要编写一个复杂的查询,它从一堆表中检索大量数据。基本上我需要找到模型的所有实例

  • 客户
  • 付款
  • 发票

其中关系以特定方式相交。在SqlAlchemy中,我可以做类似

的操作
for c, p, i in session.query(Customer, Payment, Invoice).\
        filter(User.id==Payment.customer_id).\
        filter(Invoice.id==Payment.invoice_id).\
        filter(Payment.date==...).\
        filter(Customer.some_property==...)
        all():
    # Do stuff ...

这将允许我设置几个约束并一次检索所有约束。在Django中,我目前做的事情就像

一样愚蠢
customers = Customer.objects.filter(...)
payments = Payment.objects.filter(customer=customer)
invoices = Invoice.objects.filter(customer=customer, payment_set=payments)

现在,我们已经有三个不同的查询(省略了一些细节以保持简单)。我能把它减少到一个吗?好吧,我本可以做点像

customers = Customer.objects.filter(...).prefetch_related(
    'payments', 'payments__invoices'
)

但是现在我必须遍历一个疯狂的数据树,而不是像SqlAlchemy一样整齐排列。有没有什么方法Django可以做这样的事情?或者我是否必须直接使用自定义SQL?

2 个答案:

答案 0 :(得分:1)

在阅读了不同的解决方案后,我决定在我的Django模型之上使用SqlAlchemy。有些人试图用SqlAlchemy完全取代Django ORM,但这几乎完全违背了使用Django的目的,因为大部分框架都依赖于ORM。

相反,我使用SqlAlchemy simple来查询Django ORM定义的表。我遵循类似于此的配方

# Setup sqlalchemy bindings
import sqlalchemy as s
from sqlalchemy.orm import sessionmaker
engine = s.create_engine('postgresql://<user>:<password>@<host>:<port>/<db_name>')
# Automatically read the database tables and create metadata
meta = s.MetaData()
meta.reflect(bind=engine)
Session = sessionmaker(bind=engine)
# Create a session, which can query the tables
session = Session()

# Build table instances without hardcoding tablenames
s_payment = meta.tables[models.Payment()._meta.db_table]
s_allocation = meta.tables[models.Allocation()._meta.db_table]
s_customer = meta.tables[models.Customer()._meta.db_table]
s_invoice = meta.tables[models.Invoice()._meta.db_table]

report = session.query(s_payment.c.amount, ...).all()

此配方有一些改进空间,例如:创建Django模型的空实例以找到它们的表名称并不是很优雅,但是,通过几行代码,我可以获得SqlAlchemy的完全灵活性而不会影响Django ORM层。这意味着两者可以幸福地生活在一起。

有一点需要注意,SqlAlchemy不会使用与Django ORM相同的连接,这意味着如果我在同一个上下文中使用这两种方法,那么事物视图可能看起来不一致。这对我来说不会成为问题,因为我只是想从数据库中读取一堆数据。

答案 1 :(得分:-1)

for customer in customers:
    for payment in customer.payments:
        for invoice in payment.invoices:
            # Do stuff

SQLAlchemy版本有什么不同?

在我看来,这是更好的,因为你走的是一个真实的结构,而不是一张桌子。你可以(而且你应该)有单独的子程序来处理每个级别。