Нужна помощь в доступе к данным строки ForeignKey при выполнении выбора с помощью SQLAlchemy - PullRequest
1 голос
/ 14 декабря 2011

Фоновая схема:

class Checkpoint(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    creator = db.Column(db.Integer, db.ForeignKey('user.id'))
    name = db.Column(db.String(255))
    description = db.Column(db.String(255), nullable=True)
    price = db.Column(db.Float, nullable=True)
    expiry = db.Column(db.DateTime, nullable=True)
    date_created = db.Column(db.DateTime)
    type = db.Column(db.String(255))
    image = db.Column(db.String(255))
    longitude = db.Column(db.Float)
    latitude = db.Column(db.Float)

class UserCheckpoint(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    user = db.relationship("User")
    checkpoint_id = db.Column(db.Integer, db.ForeignKey('checkpoint.id'))
    checkpoint = db.relationship("Checkpoint")

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(255))
    facebook_info = db.Column(db.String(255), db.ForeignKey('facebook_user.id'))
    facebook_user = db.relationship("FacebookUser")

class FriendConnection(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    fb_user_from = db.Column(db.String(255), db.ForeignKey('facebook_user.id'))
    fb_user_to = db.Column(db.String(255), db.ForeignKey('facebook_user.id'))

class FacebookUser(db.Model):
    id = db.Column(db.String(255), primary_key=True)
    name = db.Column(db.String(255))
    first_name = db.Column(db.String(255), nullable=True)
    middle_name = db.Column(db.String(255), nullable=True)
    last_name = db.Column(db.String(255), nullable=True)
    gender = db.Column(db.String(255), nullable=True)
    username = db.Column(db.String(255), nullable=True)
    link = db.Column(db.String(255), nullable=True)

У меня есть пользователь, и, как вы можете видеть, у каждого пользователя есть профиль в Facebook, а также таблица, изображающая дружбу между профилями в Facebook. Поэтому, учитывая пользователя, у него будет список друзей в Facebook. Я хотел бы получить все UserCheckpoints, которые принадлежат как пользователю, так и его друзьям, с заданным условием Checkpoint:

coord_conditions = and_(Checkpoint.longitude <= longitude + exp_no,
                                Checkpoint.longitude >= longitude - exp_no,
                                Checkpoint.latitude <= latitude + exp_no,
                                Checkpoint.latitude >= latitude - exp_no,
                                )

Как я могу сделать это, используя ORM из SQLAlchemy? Спасибо!

Сводка: Как выбрать UserCheckpoints, учитывая, что user_id принадлежит списку друзей / себя; в то время как UserCheckpoint.checkpoint имеет ряд условий для выполнения.

Ответы [ 2 ]

2 голосов
/ 15 декабря 2011

Каждое отношение имеет два метода для определенных условий на связанных объектах: .has() для одного упомянутого объекта и .any() для коллекций. Эти методы позволяют осуществлять прямой перевод вашей задачи в выражение SQLAlchemy. Давайте добавим отсутствующие отношения к FacebookUser:

class FacebookUser(Model):
    # Definitions from question are here
    user = relationship(User, uselist=False)
    friends = relationship('FacebookUser',
                           secondary=FriendConnection.__table__,
                           primaryjoin=(id==FriendConnection.fb_user_from),
                           secondaryjoin=(FriendConnection.fb_user_to==id))

Я определил FacebookUser.user, предполагая отношение «один к одному» (которое обычно дополняется уникальным ограничением на столбец внешнего ключа). Просто удалите uselist=False и измените имя, если вы разрешаете нескольким пользователям подключаться к одной учетной записи Facebook.

Более короткое определение вашего состояния для координат:

coord_conditions = Checkpoint.longitude.between(longitude - exp_no,
                                                longitude + exp_no) & \
                   Checkpoint.latitude.between(latitude - exp_no,
                                               latitude + exp_no)

Это условие определенно неверно даже для аппроксимации (-179,9 ° и 179,9 ° очень близки, хотя разница огромна), но это не главная тема вопроса.

Условие для интересующих пользователей (пользователь с идентификатором, равным user_id, и его друзья):

user_cond = (User.id==user_id) | \
            User.facebook_user.has(
                FacebookUser.friends.any(FacebookUser.user.has(id=user_id)))

Теперь запрос довольно прост:

session.query(UserCheckpoint).filter(
        UserCheckpoint.checkpoint.has(coord_conditions) & \
        UserCheckpoint.user.has(user_cond))

Если у вас нет (или вы ожидаете) проблем с производительностью, я бы рекомендовал не оптимизировать их за счет читабельности.

1 голос
/ 14 декабря 2011

По сути, ваш запрос можно разделить на две части:

  1. Учитывая user_id, создайте список пользователей, который будет содержать как самого пользователя, так и всех прямых друзей
  2. Учитывая список пользователей из 1., получите все UserCheckpoint, чьи Checkpoint будут удовлетворять критериям.

Не проверенный код:

# get direct user for given user_id
u1 = (session.query(User.id.label("user_1_id"), User.id.label("user_id"))
     )

# get friends of the user in one direction (from other user to this one)
User2 = aliased(User)
FacebookUser2 = aliased(FacebookUser)
u2 = (session.query(User2.id.label("user_1_id"), User.id.label("user_id")).
        join(FacebookUser2, User2.facebook_info == FacebookUser2.id).
        join(FriendConnection, FacebookUser2.id == FriendConnection.fb_user_from).
        join(FacebookUser, FacebookUser.id == FriendConnection.fb_user_to).
        join(User, User.facebook_info == FacebookUser.id)
     )

# get friends of the user in other direction (from this user to the other)
User2 = aliased(User)
FacebookUser2 = aliased(FacebookUser)
u3 = (session.query(User2.id.label("user_1_id"), User.id.label("user_id")).
        join(FacebookUser2, User2.facebook_info == FacebookUser2.id).
        join(FriendConnection, FacebookUser2.id == FriendConnection.fb_user_to).
        join(FacebookUser, FacebookUser.id == FriendConnection.fb_user_from).
        join(User, User.facebook_info == FacebookUser.id)
     )

# create a union to have all pairs (me_or_friend_id, user_id)
u_all = union_all(u1, u2, u3)
# **edit-1: added alias **
u_all = u_all.alias("user_list_view")
# final query which adds filters requirested (by user_id and the checkpoint condition)
q = (session.query(UserCheckpoint).
        join(Checkpoint).filter(coord_conditions).
        join(u_all, UserCheckpoint.user_id == u_all.c.user_1_id).
        filter(u_all.c.user_id == user_id)
    )
for u_cp in q.all():
    print u_cp

Обратите внимание, , что вы могли бы несколько упростить запрос, если в своей модели вы определили больше relationships, а затем можете удалить некоторые условия primaryjoin из предложений join.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...