简体   繁体   中英

Storing reference to other rows in SQLAlchemy Many-to-Many association table

I'm working on a project in SQLAlchemy. Let's say (to simplify the problem) there are three tables at play-- Blog_posts, Users, and Users_daily_posts. The first two look like this:

class Blog_posts(db.Model):
    __tablename__ = 'Blog_posts'
    post_id = db.Column(db.Integer(), primary_key = True)
    date = db.Column(db.Integer())
    creator_id = db.Column(db.Integer())

class Users(db.Model):
    __tablename__ = 'Users'
    user_id = db.Column(db.Integer(), primary_key = True)
    likes = db.Column(db.Integer())
    users_daily_posts = db.relationship('Users_daily_posts', lazy='dynamic', backref=db.backref('user', lazy='dynamic'))

For the third table, the expectation is that it has as primary keys a user_id and a date (say date is an integer), a column for likes, and a column that should keep track of which blog posts are on that particular date. When a post is liked, the like propagates to this table and to the Users table, but Blog_posts does not store it. I originally thought date should be a foreign key, but it turns out there is a uniqueness constraint on this in the original table, and they won't be unique in the Blog_posts table, so I would think the design would be something like:

class Users_daily_posts(db.Model):
    __tablename__ = 'Users_daily_posts'
    user_id = db.Column(db.Integer(), db.ForeignKey('Users.user_id', ondelete="CASCADE"), primary_key = True)
    date = db.Column(db.Integer(), primary_key = True)
    likes = db.Column(db.Integer())
    blog_posts = ?

For blog_posts, I tried:

db.relationship('Blog_posts', lazy='dynamic', backref=db.backref('posts', lazy='dynamic'))

thinking that this might allow me to add a list of blog posts, but this gives the error:

ArgumentError: Could not determine join condition between parent/child tables on relationship Users_daily_posts.blog_posts.  Specify a 'primaryjoin' expression.  If 'secondary' is present, 'secondaryjoin' is needed as well.

I've seen a couple of posts about this error, but I really don't understand what the primaryjoin expression would be, as I'm about the furthest you can get from a databases whiz. Could somebody explain what the primary join would be and why? Or at least what I'm doing wrong? If there is a different SQLAlchemy idiom that is better suited to this purpose, I'd love to hear about it. Thanks so much for your time!

There are many cases that you need an association table between two classes. But looking at your case, I believe that you need just a column_property or do a simple query is enough.

In your case, you will have:

  • Each post have just a one creator or author, so is Many to one relationship;

  • The "likes" can't no be on users, but you can have a
    column property that count how many likes;

  • Finally, "likes" is more related to the visitors and posts. So you
    will need another model in order to keep information about the
    visitors. And then make a relationship with users.

Try this:

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

import datetime

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

class Blog_post(db.Model):
    __tablename__ = 'Blog_posts'
    post_id = db.Column(db.Integer, primary_key = True)
    creator_id = db.Column(db.Integer, db.ForeignKey('Users.user_id'), nullable=False)
    headline = db.Column(db.String(255), nullable=False)
    date = db.Column(db.Date, nullable=False)
    likes = db.Column(db.Integer)
    def __init__(self, headline, body,creator_id):
        self.headline = headline
        self.creator_id = creator_id
        self.date = datetime.datetime.today() 

class User(db.Model):
    __tablename__ = 'Users'
    user_id = db.Column(db.Integer(), primary_key = True)
    user_name = db.Column(db.String(80), unique=True)
    users_posts = db.column_property(
        db.select([db.func.count(Blog_post.post_id)]).\
            where(Blog_post.creator_id==user_id).\
            correlate_except(Blog_post)
    )
    def __init__(self, user_name):
        self.user_name = user_name

Testing...

>>> db.create_all()
>>> u = User('dedeco')
>>> db.session.add(u)
>>> db.session.commit()
>>> 
>>> p = Blog_post('I am dedeco 1','About dedeco 1',u.user_id)
>>> db.session.add(p)
>>> p = Blog_post('I am dedeco 2','About dedeco 2',u.user_id)
>>> db.session.add(p)
>>> p = Blog_post('I am dedeco 3','About dedeco 3',u.user_id)
>>> db.session.add(p)
>>> db.session.commit()
>>> u.users_posts
3

Query:

>>> d = db.session.query(db.func.count(Blog_post.post_id),db.func.strftime('%d/%m/%Y', Blog_post.date)).filter(Blog_post.creator_id==u.user_id).group_by(db.func.strftime('%d/%m/%Y', Blog_post.date))
>>> d.all()
[(3, u'04/11/2015')]

You can see some information about Many to Many on Sqlalchemy documentation

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM