#python #sql #join #sqlalchemy
#python #sql #Присоединиться #sqlalchemy
Вопрос:
У меня есть следующие сопоставленные классы SQLAlchemy:
class User(Base):
__tablename__ = 'users'
email = Column(String, primary_key=True)
name = Column(String)
class Document(Base):
__tablename__ = "documents"
name = Column(String, primary_key=True)
author = Column(String, ForeignKey("users.email"))
class DocumentsPermissions(Base):
__tablename__ = "documents_permissions"
readAllowed = Column(Boolean)
writeAllowed = Column(Boolean)
document = Column(String, ForeignKey("documents.name"))
Мне нужно получить такую таблицу для user.email = "user@email.com"
:
email | name | document_name | document_readAllowed | document_writeAllowed
Как это можно сделать, используя один запрос запроса для SQLAlchemy? Приведенный ниже код у меня не работает:
result = session.query(User, Document, DocumentPermission).filter_by(email = "user@email.com").all()
Спасибо,
Комментарии:
1. Я обнаружил, что для объединения двух таблиц работает следующее:
result = session.query(User, Document).select_from(join(User, Document)).filter(User.email=='user@email.com').all()
Но мне пока не удалось, как сделать аналогичную работу для трех таблиц (включить DocumentPermissions). Есть идеи?2. Когда я выполняю аналогичную задачу, я получаю SyntaxError: ключевое слово не может быть выражением
Ответ №1:
Попробуйте это
q = Session.query(
User, Document, DocumentPermissions,
).filter(
User.email == Document.author,
).filter(
Document.name == DocumentPermissions.document,
).filter(
User.email == 'someemail',
).all()
Комментарии:
1. Что
join
это делает? внутренний, внешний, перекрестный или какой?2. На самом деле это вообще не выполняет объединение, оно возвращает объекты строк в виде кортежа. В этом случае это вернет
[(<user>, <document>, <documentpermissions>),...]
/3. «вообще не выполняет объединение» — это немного вводит в заблуждение. Он будет иметь sql like
select x from a, b ,c
, который является перекрестным соединением. Затем фильтры преобразуют это во внутреннее соединение.4. Вы можете распечатать запрос, пропустив
.all()
. Итак,print Session.query....
чтобы точно увидеть, что он делает.5. Я новичок в SQLAlchemy. Я заметил, что
.filter()
можно получить несколько критериев, если разделять их запятой. Предпочтительнее использовать одну.filter()
с разделением запятой внутри круглых скобок или использовать несколько.filter()
, как в приведенном выше ответе?
Ответ №2:
Как сказал @letitbee, лучше всего назначать первичные ключи таблицам и правильно определять связи, чтобы обеспечить надлежащее выполнение запросов ORM. Это, как говорится…
Если вы заинтересованы в написании запроса в соответствии с:
SELECT
user.email,
user.name,
document.name,
documents_permissions.readAllowed,
documents_permissions.writeAllowed
FROM
user, document, documents_permissions
WHERE
user.email = "user@email.com";
Тогда вам следует выбрать что-то вроде:
session.query(
User,
Document,
DocumentsPermissions
).filter(
User.email == Document.author
).filter(
Document.name == DocumentsPermissions.document
).filter(
User.email == "user@email.com"
).all()
Если вместо этого вы хотите сделать что-то вроде:
SELECT 'all the columns'
FROM user
JOIN document ON document.author_id = user.id AND document.author == User.email
JOIN document_permissions ON document_permissions.document_id = document.id AND document_permissions.document = document.name
Затем вы должны сделать что-то вроде:
session.query(
User
).join(
Document
).join(
DocumentsPermissions
).filter(
User.email == "user@email.com"
).all()
Одно замечание по этому поводу…
query.join(Address, User.id==Address.user_id) # explicit condition
query.join(User.addresses) # specify relationship from left to right
query.join(Address, User.addresses) # same, with explicit target
query.join('addresses') # same, using a string
Для получения дополнительной информации посетите документы.
Комментарии:
1. СДЕЛАЙТЕ ЭТО. Смотрите — в объединениях указано не так уж много всего. Это потому, что если tables / db-model уже настроены с соответствующими внешними ключами между этими таблицами — SQLAlchemy позаботится о соединении В соответствующих столбцах для вас.
2. Это наиболее правильный ответ и правильное использование sqlalchemy. Однако мне пришлось использовать решение с фильтром, потому что SQLAlchemy не принимает инструкции delete с помощью join()
Ответ №3:
Хорошим стилем было бы настроить некоторые отношения и первичный ключ для разрешений (на самом деле, обычно хорошим стилем является настройка целых первичных ключей для всего, но неважно):
class User(Base):
__tablename__ = 'users'
email = Column(String, primary_key=True)
name = Column(String)
class Document(Base):
__tablename__ = "documents"
name = Column(String, primary_key=True)
author_email = Column(String, ForeignKey("users.email"))
author = relation(User, backref='documents')
class DocumentsPermissions(Base):
__tablename__ = "documents_permissions"
id = Column(Integer, primary_key=True)
readAllowed = Column(Boolean)
writeAllowed = Column(Boolean)
document_name = Column(String, ForeignKey("documents.name"))
document = relation(Document, backref = 'permissions')
Затем выполните простой запрос с объединениями:
query = session.query(User, Document, DocumentsPermissions).join(Document).join(DocumentsPermissions)
Комментарии:
1. Что
query
установлено в последней строке и как получить доступ к объединенным записям в ней?2. @pate Я не уверен, что вы подразумеваете под «на что задан запрос», но он будет объединяться в соответствии с отношениями и даст 3 кортежа. Аргументы вызова query() по сути являются списком выбора в sqlalchemy.
3. Как мне получить доступ к
Document
(значению 2-го кортежа) в наборе результатов запроса?4. @PetrusTheron Как я уже сказал, запрос выдаст 3 кортежа. Вы можете индексировать элементы или просто распаковать:
for (user, doc, perm) in query: print "Document: %s" % doc
5. Вау, взрыв из прошлого. ‘permissions’ — это атрибут объекта Document, который предоставляет вам набор объектов DocumentPermission. Отсюда обратная ссылка.
Ответ №4:
Развивая ответ Абдула, вы можете получить KeyedTuple
вместо дискретной коллекции строк, объединив столбцы:
q = Session.query(*User.__table__.columns Document.__table__.columns).
select_from(User).
join(Document, User.email == Document.author).
filter(User.email == 'someemail').all()
Комментарии:
1. Это работает. Однако при сериализации объекта имена столбцов отсутствуют.
Ответ №5:
Эта функция создаст требуемую таблицу в виде списка кортежей.
def get_documents_by_user_email(email):
query = session.query(
User.email,
User.name,
Document.name,
DocumentsPermissions.readAllowed,
DocumentsPermissions.writeAllowed,
)
join_query = query.join(Document).join(DocumentsPermissions)
return join_query.filter(User.email == email).all()
user_docs = get_documents_by_user_email(email)