Создание отношения M2M с саморегулированием в SQLAlchemy (+ Flask)

Пытаясь изучить Flask, я создаю простой клон Twitter. Это будет включать возможность для пользователя подписаться на других пользователей. Я пытаюсь настроить реляционную базу данных через SQLAlchemy, чтобы это разрешить.

Я подумал, что мне понадобятся отношения «многие ко многим» для пользователя с самооценкой. Следуя документации SQLAlchemy Я пришел к:

#imports omitted    

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///twitclone.db'
db = SQLAlchemy(app)

Base = declarative_base()

user_to_user = Table("user_to_user", Base.metadata,
    Column("follower_id", Integer, ForeignKey("user.id"), primary_key=True),
    Column("followed_id", Integer, ForeignKey("user.id"), primary_key=True)
)

class User(db.Model):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)
    name = Column(String, unique=False)
    handle = Column(String, unique=True)
    password = Column(String, unique=False)
    children = relationship("tweet")
    following = relationship("user",
                    secondary=user_to_user,
                    primaryjoin=id==user_to_user.c.follower_id,
                    secondaryjoin=id==user_to_user.c.followed_id,
                    backref="followed_by"
    )

#Tweet class goes here

db.create_all()

if __name__ == "__main__":
    app.run()

Запуск этого кода приводит к созданию базы данных без сообщений об ошибках. Однако вся часть (таблица), соединяющая пользователя с пользователем, просто опускается. Это определение таблицы User:

CREATE TABLE user (
    id INTEGER NOT NULL, 
    name VARCHAR, 
    handle VARCHAR, 
    password VARCHAR, 
    PRIMARY KEY (id), 
    UNIQUE (handle)
)

Почему SQLAlchemy не создает для пользователя отношения со ссылками на себя?

примечание: я новичок как во Flask, так и в SQLAlchemy, и здесь может отсутствовать что-то очевидное.


person Roy Prins    schedule 29.12.2013    source источник


Ответы (1)


Хорошо, похоже, я перепутал два разных стиля использования SQLAlchemy с Flask: декларативное расширение SQLAlchemy и расширение flask-sqlalchemy. Оба схожи по возможностям, с той разницей, что у расширения flask есть некоторые полезности, такие как обработка сеанса. Вот как я переписал свой код, чтобы строго использовать flask-sqlalchemy.

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from datetime import datetime

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///kwek.db'
db = SQLAlchemy(app)

#Table to handle the self-referencing many-to-many relationship for the User class:
#First column holds the user who follows, the second the user who is being followed.
user_to_user = db.Table('user_to_user',
    db.Column("follower_id", db.Integer, db.ForeignKey("user.id"), primary_key=True),
    db.Column("followed_id", db.Integer, db.ForeignKey("user.id"), primary_key=True)
)


class User(db.Model):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=False)
    handle = db.Column(db.String(16), unique=True)
    password = db.Column(db.String, unique=False)
    kweks = db.relationship("Kwek", lazy="dynamic")
    following = db.relationship("User",
                    secondary=user_to_user,
                    primaryjoin=id==user_to_user.c.follower_id,
                    secondaryjoin=id==user_to_user.c.followed_id,
                    backref="followed_by"
    )

    def __repr__(self):
        return '<User %r>' % self.name


class Kwek(db.Model):
    __tablename__ = 'kwek'
    id = db.Column(db.Integer, primary_key=True)
    content = db.Column(db.String(140), unique=False)
    post_date = db.Column(db.DateTime, default=datetime.now())
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))

    def __repr__(self):
        return '<Kwek %r>' % self.content

if __name__ == "__main__":
    app.run()
person Roy Prins    schedule 29.12.2013
comment
Не должно быть никакой разницы (поскольку Flask-SQLAlchemy просто проксирует декларативную SQLAlchemy здесь). Взгляните, например, здесь. - person Sean Vieira; 30.12.2013
comment
Вы правы, хотя в документации Flask это не было ясно. Я обновлю свой ответ, чтобы отразить ваш отзыв. - person Roy Prins; 31.12.2013
comment
Потрясающий! Спасибо, что вернули ответ, и готовы его обновить. - person Sean Vieira; 31.12.2013
comment
как подключить фолловера? это просто user.following = (some_other_user.id)? - person Alex Waters; 01.03.2016