pythonflaskflask-sqlalchemyalchemy

Flask-SQLAlchemy,How to sort results in Many-to-Many Relationship?


I am using flask-sqlalchemy.

I want to add an ORDER column in Many-to-Many Relationship.

my models:

image_category_association = db.Table('category_image_relation',
    db.Column('id', db.Integer, primary_key=True),
    db.Column('category_id', db.Integer, db.ForeignKey('image_category.category_id')),
    db.Column('image_id', db.Integer, db.ForeignKey('image.image_id')),
    db.Column('order', db.Integer, unique=True, autoincrement=True)
)

class ImageCategory(db.Model):
    __bind_key__ = 'ads'
    __tablename__ = 'image_category'

    category_id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(255), default='')
    add_time = db.Column(db.Integer, default=0)
    images = db.relationship('Image', secondary=image_category_relation, backref='categories', lazy='dynamic')


class Image(db.Model):
    __bind_key__ = 'ads'
    __tablename__ = 'image'

    image_id = db.Column(db.Integer, primary_key=True)
    add_time = db.Column(db.Integer, default=0)
    hash = db.Column(db.String(64), default='')
    url = db.Column(db.String(255), default='')

category = ImageCategory.query.get(1)
category.images.append(image1, image2, image3)
images = category.images.all()  # order by the ORDER column

Is it possible to update the ORDER column value in table image_category_association?

And how do i sort the category.images.all() order by the ORDER column.


Solution

  • Use an association object - see self-contained example below (I've left out some of your fields). The order is created/updated in the ImageCategoryToImageAssociation class - see the build_db method to see how it's used.

    Also note the order_by="ImageCategoryToImageAssociation.order" in the ImageCategory images attribute db.relationship definition - category_1 has its images inserted out-of-order but querying the database returns them in sorted order.

    from flask import Flask
    from flask.ext.sqlalchemy import SQLAlchemy
    
    app = Flask(__name__)
    app.config['DEBUG'] = True
    app.config['SECRET_KEY'] = 'super-secret'
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:'
    app.config['SQLALCHEMY_ECHO'] = True
    db = SQLAlchemy(app)
    
    
    class ImageCategoryToImageAssociation(db.Model):
        __tablename__ = 'image_categories_to_image_association'
        image_category_id = db.Column(db.Integer, db.ForeignKey('image_categories.id'), primary_key=True)
        image_id = db.Column(db.Integer, db.ForeignKey('images.id'), primary_key=True)
        order = db.Column(db.Integer, default=0)
        image = db.relationship("Image", back_populates="categories")
        category = db.relationship("ImageCategory", back_populates="images")
    
    
    class ImageCategory(db.Model):
        __tablename__ = 'image_categories'
        id = db.Column(db.Integer, primary_key=True)
        name = db.Column(db.String(255), unique=True)
        images = db.relationship(ImageCategoryToImageAssociation, back_populates="category", order_by="ImageCategoryToImageAssociation.order")
    
    
    class Image(db.Model):
        __tablename__ = 'images'
        id = db.Column(db.Integer, primary_key=True)
        url = db.Column(db.String(255), default='')
        categories = db.relationship(ImageCategoryToImageAssociation, back_populates="image")
    
    
    @app.route('/')
    def index():
    
        _html = ""
        categories = ImageCategory.query.all()
        for category in categories:
            _html += "<h4>Category : {category}</h4>".format(category=category.name)
            _html += "<ul>"
            for assoc in category.images:
                _html += "<li>Image : {url}; Order: {order}</li>".format(url=assoc.image.url, order=assoc.order)
    
            _html += "</ul>"
    
        return _html
    
    
    def build_db():
    
        db.drop_all()
        db.create_all()
    
        # Add Categories
        category_1 = ImageCategory(name="First Category")
        db.session.add(category_1)
        category_2 = ImageCategory(name="Second Category")
        db.session.add(category_2)
    
        # Add Images
        image_1 = Image(url="First Image")
        db.session.add(image_1)
        image_2 = Image(url="Second Image")
        db.session.add(image_2)
    
        image_3 = Image(url="Third Image")
        db.session.add(image_3)
    
        # Associate images with category 1
        a_1 = ImageCategoryToImageAssociation(order=0)
        a_1.image = image_1
        category_1.images.append(a_1)
    
        a_2 = ImageCategoryToImageAssociation(order=2)
        a_2.image = image_2
        category_1.images.append(a_2)
    
        a_3 = ImageCategoryToImageAssociation(order=1)
        a_3.image = image_3
        category_1.images.append(a_3)
    
        # Associate images with category 2 in reverse order
        a_1 = ImageCategoryToImageAssociation(order=2)
        a_1.image = image_1
        category_2.images.append(a_1)
    
        a_2 = ImageCategoryToImageAssociation(order=1)
        a_2.image = image_2
        category_2.images.append(a_2)
    
        a_3 = ImageCategoryToImageAssociation(order=0)
        a_3.image = image_3
        category_2.images.append(a_3)
    
        db.session.commit()
    
    
    @app.before_first_request
    def before_first_request():
        build_db()
    
    if __name__ == '__main__':
        app.run()