Last active
July 7, 2024 09:00
-
-
Save tachyondecay/e0fe90c074d6b6707d8f1b0b1dcc8e3a to your computer and use it in GitHub Desktop.
Tags in Flask via SQLalchemy and association proxies
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from app import db | |
from sqlalchemy import desc, event, func, orm | |
from sqlalchemy.ext.associationproxy import association_proxy | |
from sqlalchemy.ext.declarative import declared_attr | |
from sqlalchemy_utils import ArrowType, auto_delete_orphans | |
from slugify import slugify_unicode | |
tags = db.Table('tag_associations', | |
db.Column('tag_id', db.Integer, db.ForeignKey('tags.id')), | |
db.Column('article_id', db.Integer, db.ForeignKey('articles.id'))) | |
class Article(db.Model): | |
"""Blog posts.""" | |
__tablename__ = 'articles' | |
_tags = db.relationship('Tag', secondary=tags, backref='articles') | |
tag_list = association_proxy('_tags', 'label') | |
@property | |
def tags(self): | |
"""Return list of tag objects associated with this article.""" | |
tag_list = getattr(self, 'tag_list', []) | |
return tag_list if (tag_list != ['']) else [] | |
@tags.setter | |
def tags(self, tag_list): | |
"""Set list of tag objects associated with this article.""" | |
self._tags = [self._find_or_create_tag(t) for t in tag_list] | |
# Model continues | |
class Tag(db.Model): | |
"""Flexible categories for articles.""" | |
__tablename__ = 'tags' | |
id = db.Column(db.Integer, primary_key=True) | |
handle = db.Column(db.String(100), unique=True) | |
label = db.Column(db.String(100)) | |
def __init__(self, label): | |
"""Create a handle for a tag based on the supplied label.""" | |
self.label = label | |
self.handle = self.slugify(label) | |
@classmethod | |
def frequency(cls): | |
"""Returns tuples of tags and their frequencies.""" | |
return db.session.query(cls, func.count()) \ | |
.outerjoin((tags, tags.c.tag_id == cls.id), | |
(Article, Article.id == tags.c.article_id)) \ | |
.filter(Article.status == 'published') \ | |
.group_by(cls.id) \ | |
.order_by(func.lower(cls.label)) | |
@classmethod | |
def slugify(cls, text): | |
"""Create a unique handle for this tag.""" | |
return slugify_unicode(text, to_lower=True, max_length=100) | |
# Tags no longer associated with any article should be removed | |
auto_delete_orphans(Article._tags) |
Thanks for this, helped me out a lot!!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
See SQLalchemy docs on many-to-many relationships and association proxies