2016-03-30 2 views
0

У меня есть SQLAlchemy декларативную модель, которая выглядит следующим образом:Почему это выражение sqlalchemy or_ не работает с истинным предложением?

class User(Base): 
    __tablename__ = 'user' 
    id = Column(Integer, primary_key=True) 
    name = Column(String(32), unique=True, nullable=False) 

    posts = relationship('Post', cascade='all, delete-orphan', back_populates='owner') 


class Post(Base): 
    __tablename__ = 'post' 
    id = Column(Integer, primary_key=True) 
    owner_id = Column(Integer, ForeignKey('user.id', ondelete="CASCADE"), nullable=False) 
    name = Column(String(256), nullable=False) 
    public = Column(Boolean, default=True) 

    owner = relationship('User', back_populates='posts', single_parent=True) 
    shared = relationship('User', secondary=shared_post) 

В моей тестовой базе данных, у меня есть пользователь User1, который имеет пост Post1. У Post1 нет связанных с ним общих пользователей. Я попробовал запрос:

expr = or_(Post.owner == current_user, # current_user is ORM User1 
      Post.public == True, 
      Post.shared.contains(another_user)) 

posts = session.query(Post).filter(expr).all() 

Однако posts является пустым списком. Вот отдельные запросы:

session.query(Post).filter(Post.owner == current_user).all() # returns Post1 
session.query(Post).filter(Post.public == True).all() # returns Post1 
session.query(Post).filter(Post.shared.contains(another_user)).all() # empty list as expected 

Почему не оригинальный or_ работы, если два из трех отдельных запросов подтвердятся работает?

Edit # 1:

Замена Post.shared.contains(another_user) с Post.shared.any(User.id == another_user_id) исправляет проблему or_ запроса. Однако теперь я хотел бы знать, почему один работает, а другой нет.

ответ

1

Вы не включили ваш второстепенную определение таблицы, но я предполагаю, что это что-то вроде этого:

shared_post = Table(
    'shared_post', Base.metadata, 
    Column('user_id', Integer, 
      ForeignKey('user.id', ondelete='cascade'), nullable=False), 
    Column('post_id', Integer, 
      ForeignKey('post.id', ondelete='cascade'), nullable=False) 
) 

Хорошее начало для отладки запросов SQLAlchemy, чтобы попытаться напечатать части выражений или даже полные запросы:

print(expr) # where expr is the original or_(...) 

дает

:param_1 = post.owner_id OR post.public = 1 OR post.id = shared_post_1.post_id AND :param_2 = shared_post_1.user_id 

Мы видим, что .contains() метода производство неявного присоединиться и к AND из 2-х выражений без скобок: предикат

post.id = shared_post_1.post_id 

и второе выражением является фактической проверкой для данного пользователя

:param_2 = shared_post_1.user_id 

Так что ваши предикаты в настоящее время состоят OR, за которыми следует AND, который никогда не будет удовлетворен.

Полный запрос

SELECT post.id AS post_id, post.owner_id AS post_owner_id, post.name AS post_name, post.public AS post_public 
FROM post, shared_post AS shared_post_1 
WHERE ? = post.owner_id OR post.public = 1 OR post.id = shared_post_1.post_id AND ? = shared_post_1.user_id 
+0

Спасибо за ответ и советы отладки в глубину. Скажете ли вы, что исходный запрос 'or_' работает так, как предполагалось, или если sqlalchemy вставили круглые скобки вокруг' .contains() '? – trianta2

+0

@ trianta2 интуитивно чувствовал, что он должен иметь круглые скобки. Чтение того, что ['RelationshipProperty.Comparator.contains'] (http://docs.sqlalchemy.org/en/rel_1_0/orm/internals.html#sqlalchemy.orm.properties.RelationshipProperty.Comparator.contains) должно дополнять это чувство , но, с другой стороны, в этом случае из-за объединения он все равно потерпит неудачу и как таковой будет неправильным выбором. '' '' '' '' '', Который вы сами выяснили, выдает правильное выражение 'EXISTS'. –

+0

Связанная документация фактически перечисляет этот точный случай как предостережение об использовании 'contains()' с отношениями «многие ко многим». Он не будет работать со сложными выражениями OR и т. Д. –

Смежные вопросы