Как использовать разные типы объединений для sql-alchemy в python - PullRequest
1 голос
/ 26 июня 2019

Я пытаюсь выяснить, как определить тип соединения в sql-алхимии ORM. Как использовать левое соединение и левое внешнее соединение? А как насчет внутреннего соединения?

Это для запроса, который может выбрать все crm_lead без связанной с ним crm_task. Я пробовал существующий фильтр, но не смог отфильтровать существующие crm_leads с этим условием.

Требуемый SQL:

select *
from crm_lead l
join crm_task t on l.id = t.lead_id
left outer join crm_pipeline_status cps on l.pipeline_status_id = cps.id
where l.pipeline_status_id not in (142, 143) 
  and (t.id is null or t.is_completed is false);

ИЛИ: (если существует предложение, лучше для этого случая)

select *
from crm_lead l
left outer join crm_pipeline_status cps on l.pipeline_status_id = cps.id
where cps.crm_id not in (142, 143)
  and not exists (select id from crm_task t where l.id = t.lead_id and t.is_completed is false);

Моя лучшая попытка была:

session = sessionmaker(bind=engine, autocommit=True)()
with session.begin():
    leads = session.query(CrmLead).outerjoin(CrmTask).outerjoin(CrmPipelineStatus).filter(
        and_(CrmLead.account_id == 2,
        CrmPipelineStatus.crm_id not in (142, 143),
        or_(CrmTask.is_completed is False, CrmTask.id is None))
    )

но оно преобразуется в:

SELECT *
FROM crm_lead 
LEFT OUTER JOIN crm_task ON crm_lead.id = crm_task.lead_id 
LEFT OUTER JOIN crm_pipeline_status ON crm_pipeline_status.id = crm_lead.pipeline_status_id 
WHERE false

АЛЬТЕРНАТИВНОЕ РЕШЕНИЕ: Мой случай может быть решен с помощью необработанного SQL, как показано здесь [https://stackoverflow.com/a/22084672/2822537]

Пример:

    query_text = '''
    select *
    from crm_lead l
    left outer join crm_pipeline_status cps on l.pipeline_status_id = cps.id
    where cps.crm_id not in (:success_final_status, :failed_final_status)
      and l.account_id = :account_id
      and not exists (select id from crm_task t where l.id = t.lead_id and t.is_completed is false);
    '''
    leads = session.execute(query_text, {
        'account_id': crm_configuration["instance_id"],
        'success_final_status': 142,
        'failed_final_status': 143
    })

Ответы [ 3 ]

1 голос
/ 27 июня 2019

Выражение

CrmPipelineStatus.crm_id not in (142, 143)

оценивается как False или True в Python, поскольку __contains__() нельзя использовать так же, как некоторые другие перегрузки.Если это False, вся включающая конструкция and_() компилируется в просто false.В этом случае правильным способом является использование метода notin_():

CrmPipelineStatus.crm_id.notin_([142, 143])

. Похожая проблема возникает

or_(CrmTask.is_completed is False, CrmTask.id is None)

, так как оператор идентификации is не может быть перегружен, итаким образом, у вас есть

or_(False, False)

, который снова компилируется в false.Для первого вы должны просто использовать логическое значение как логическое (при условии, что это NOT NULL), а проверку NULL можно записать либо с использованием метода is_(), либо в специальном случае в перегрузках операторов:

or_(not_(CrmTask.is_completed), CrmTask.id == None)
1 голос
/ 26 июня 2019

Смотри, может быть этот пост

q = session.query(Table1.field1, Table1.field2)\
.outerjoin(Table2)\ # use in case you have relationship defined
# .outerjoin(Table2, Table1.id == Table2.table_id)\ # use if you do not have relationship defined
.filter(Table2.tbl2_id == None)

должен сделать это, предполагая, что field1 и field2 взяты из Table1, и что вы определяете отношение:

class Table2(Base):
# ...
table1 = relationship(Table1, backref="table2s")
0 голосов
/ 26 июня 2019

CamelCase используется для сопоставленных классов имен таблиц в нижнем регистре:

from sqlalchemy.sql import exists

Session().query(CrmLead).join(CrmTask).outerjoin(CrmPipelineStatus).filter(CrmLead.pipeline_status_id == CrmPipelineStatus.id).filter(CrmPipelineStatus.crm_id.notin_([142, 143])).filter(~exists().where(and_(CrmTask.is_completed==False, CrmLead.id==CrmTask.lead_id)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...