我的数据库关系如何? Flask sqlalchemy

时间:2017-04-27 11:10:01

标签: python pandas sqlalchemy

我希望有一位可以为不同电影评分的用户。电影可以有很多评级;用户可以为很多电影评分。

我认为它看起来像这样:

class User(db.Model, UserMixin):
    __tablename__ = 'users'
    id = db.Column(db.Integer(), primary_key=True)
    username = db.Column(db.String(), nullable=True, unique=True)
    password = db.Column(db.String(), nullable=True)



   def __init__(self, *args, **kwargs):
    super(User, self).__init__(*args, **kwargs)


class Movie(db.Model):
    __tablename__ = 'movies'
    id = db.Column(db.Integer(), primary_key=True)
    title = db.Column(db.String(), nullable=True)
    release_year = db.Column(db.String(), nullable=True)
    imdb_url = db.Column(db.String(), nullable=True)
    poster = db.Column(db.String(), nullable=True)
    description = db.Column(db.String(), nullable=True)
    genre = db.Column(db.String(), nullable=True)
    rating = db.Column(db.String(), nullable=True)


    def __init__(self, *args, **kwargs):
        super(Movie, self).__init__(*args, **kwargs)

class Rating(db.Model):
    __tablename__ = 'ratings'
    id = db.Column(db.Integer(), primary_key=True)
    movie_id = db.Column(db.Integer(), db.ForeignKey('movies.id'))
    user_id = db.Column(db.Integer(), db.ForeignKey('users.id'))
    rating = db.Column(db.Float(), default='0')

    user = db.relationship("User", backref=backref("ratings", order_by=id))
    movie = db.relationship("Movie", backref=backref("ratings", order_by=id))

如果这是正确的,我将如何查询这些表以获取每部电影上的所有用户及其评级,然后生成一个pandas数据框,其中所有用户的userIds为列,所有movieId为行,各自的评级为价值?

  UserId 1  2   3   4   5   6   7   8   9   10  
 MovieId                                                                                    

   1    5.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 
   2    0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 5.0 
   3    0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 4.5 0.0 
   4    0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 
   5    0.0 0.0 0.0 0.0 2.0 0.0 0.0 0.0 0.0 0.0

如果用户没有对电影进行评级,我仍然希望它们在矩阵中,例如用户编号8,他没有评价过一部电影。

1 个答案:

答案 0 :(得分:2)

哇,这是很多问题!让我们一次做一件事。

所以,你想要用户和电影。酷酷。

以下是我如何定义模型:

from werkzeug.security import check_password_hash, generate_password_hash
from . import db  # grab sqlalchemy

class User(db.Model, UserMixin):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(), unique=True)  # Can something be nullable and unique? I think it can be, or that this would be allowed, but still, probably want this to be not null
    password_hash = db.Column(db.String(128))  # Likewise, make password HASH not nullable. Don't store plaintext passwords, seriously.

    @property
    def password(self):
        raise (AttributeError('"password" is not a readable attribute'))

    @password.setter
    def password(self, password):
        self.password_hash = generate_password_hash(password)

    def verify_password(self, password):
        return (check_password_hash(self.password_hash, password))

    movies = db.relationship('Movie', secondary='ratings')  # n:m relationship


class Movie(db.Model):
    __tablename__ = 'movies'
    id = db.Column(db.Integer, primary_key=True)
    # ...
    users = db.relationship('User', secondary='ratings')  # n:m relationship
    # ...


class Rating():
    __tablename__ = 'ratings'
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), primary_key=True)
    movie_id = db.Column(db.Integer, db.ForeignKey('movies.id'), primary_key=True)
    # The following defines a User.ratings attribute that points to this object
    # The backref loads the object dynamically, but accessing the link object directly joins both the user and movie
    user = db.relationship(User, backref=db.backref("ratings", lazy='dynamic'), lazy='joined')
    # Likewise for Movies
    movie = db.relationship(Movie, backref=db.backref("ratings", lazy='dynamic'), lazy='joined')
    # Store User rating information here, as a part of link:
    rating = db.Column(db.Float, default='0')  # Could be nullable, or default could be something impossible like "-1" to distinguish, if needed.

好吧,很酷......所以现在你可以做这样的事情:

user = current_user or User.query.filter_by(id=3087).first()
Rating.query.filter(Rating.user == user).all()  # all ratings by a user (current user or user id = 3087, in this case)
# Same as:
user.ratings  # All ratings by a user

User.query.filter_by(id=user.id).first().ratings  # list of all ratings for a user

great_movie = Movie.query.filter_by(name="Birdemic: Shock and Terror").first()
Movie.query.filter_by(id=great_movie.id).first().ratings  # all ratings for a movie (Birdemic: Shock and Terror, in this case)
# Same as:
great_movie.ratings

您可以将矩阵方法定义为视图,或者甚至可以通过将矩阵生成代码作为模型类的静态方法来为用户和影片创建子矩阵:

因此,您可以将行为编码为:

<User Object>.ratings_matrix()

示例(仅限伪代码):

@app.route('/matrix', methods=['GET'])
def matrix():
    # define pandas matrix, and iteratively fill it with ratings:
    matrix = Matrix() # ?? Idk

    for user in User.query.all():
        for movie in Movie.query.all():
            rating = Rating.query.filter_by(user_id=user.id, movie_id=movie.id).first().rating  # find by ids, then get actual rating value (Rating.rating)

            if rating:
                # add rating to matrix at correct position (user.id, movie.id)
                matrix[user.id, movie.id] = rating
            else:
                matrix[user.id, movie.id] = 0

    return(render_template('ratings_matrix.html', matrix=matrix))  # implies 'templates/ratings_matrix.html'

或者,您可以预先创建,存储(可能会腌制?)然后从db或缓存整体中检索矩阵。真的是你!

希望这有帮助!

相关问题