|
from datetime import datetime |
|
from app import db |
|
from flask_login import UserMixin |
|
from werkzeug.security import generate_password_hash, check_password_hash |
|
from sqlalchemy.ext.associationproxy import association_proxy |
|
|
|
|
|
class Role: |
|
MEMBER = 'member' |
|
MODERATOR = 'moderator' |
|
ADMIN = 'admin' |
|
|
|
|
|
topic_tag = db.Table('topic_tag', |
|
db.Column('topic_id', db.Integer, db.ForeignKey('topic.id'), primary_key=True), |
|
db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'), primary_key=True) |
|
) |
|
|
|
|
|
class User(UserMixin, db.Model): |
|
id = db.Column(db.Integer, primary_key=True) |
|
username = db.Column(db.String(64), unique=True, nullable=False, index=True) |
|
email = db.Column(db.String(120), unique=True, nullable=False, index=True) |
|
password_hash = db.Column(db.String(256), nullable=False) |
|
role = db.Column(db.String(20), nullable=False, default=Role.MEMBER) |
|
avatar = db.Column(db.String(120), nullable=True, default='default.png') |
|
signature = db.Column(db.String(200), nullable=True) |
|
location = db.Column(db.String(100), nullable=True) |
|
website = db.Column(db.String(120), nullable=True) |
|
bio = db.Column(db.Text, nullable=True) |
|
created_at = db.Column(db.DateTime, default=datetime.utcnow) |
|
last_seen = db.Column(db.DateTime, default=datetime.utcnow) |
|
is_active = db.Column(db.Boolean, default=True) |
|
is_banned = db.Column(db.Boolean, default=False) |
|
ban_reason = db.Column(db.Text, nullable=True) |
|
|
|
|
|
topics = db.relationship('Topic', backref='author', lazy='dynamic') |
|
posts = db.relationship('Post', backref='author', lazy='dynamic', foreign_keys='Post.author_id') |
|
reactions = db.relationship('Reaction', backref='user', lazy='dynamic') |
|
reports = db.relationship('Report', backref='reporter', lazy='dynamic', foreign_keys='Report.reporter_id') |
|
|
|
def set_password(self, password): |
|
self.password_hash = generate_password_hash(password) |
|
|
|
def check_password(self, password): |
|
return check_password_hash(self.password_hash, password) |
|
|
|
def is_admin(self): |
|
return self.role == Role.ADMIN |
|
|
|
def is_moderator(self): |
|
return self.role == Role.MODERATOR or self.role == Role.ADMIN |
|
|
|
def update_last_seen(self): |
|
self.last_seen = datetime.utcnow() |
|
db.session.commit() |
|
|
|
def __repr__(self): |
|
return f'<User {self.username}>' |
|
|
|
|
|
class Category(db.Model): |
|
id = db.Column(db.Integer, primary_key=True) |
|
name = db.Column(db.String(100), nullable=False) |
|
description = db.Column(db.Text, nullable=True) |
|
order = db.Column(db.Integer, default=0) |
|
|
|
|
|
topics = db.relationship('Topic', backref='category', lazy='dynamic') |
|
|
|
def topic_count(self): |
|
return self.topics.count() |
|
|
|
def post_count(self): |
|
count = 0 |
|
for topic in self.topics: |
|
count += topic.posts.count() |
|
return count |
|
|
|
def __repr__(self): |
|
return f'<Category {self.name}>' |
|
|
|
|
|
class Topic(db.Model): |
|
id = db.Column(db.Integer, primary_key=True) |
|
title = db.Column(db.String(200), nullable=False) |
|
created_at = db.Column(db.DateTime, default=datetime.utcnow) |
|
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) |
|
views = db.Column(db.Integer, default=0) |
|
is_locked = db.Column(db.Boolean, default=False) |
|
is_pinned = db.Column(db.Boolean, default=False) |
|
|
|
|
|
category_id = db.Column(db.Integer, db.ForeignKey('category.id'), nullable=False) |
|
author_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) |
|
|
|
|
|
posts = db.relationship('Post', backref='topic', lazy='dynamic', cascade='all, delete-orphan') |
|
tags = db.relationship('Tag', secondary=topic_tag, backref=db.backref('topics', lazy='dynamic')) |
|
reports = db.relationship('Report', backref='topic', lazy='dynamic', |
|
primaryjoin="and_(Report.topic_id==Topic.id, Report.post_id==None)") |
|
|
|
def reply_count(self): |
|
return self.posts.count() - 1 |
|
|
|
def last_post(self): |
|
return self.posts.order_by(Post.created_at.desc()).first() |
|
|
|
def increment_view(self): |
|
self.views += 1 |
|
db.session.commit() |
|
|
|
def __repr__(self): |
|
return f'<Topic {self.title}>' |
|
|
|
|
|
class Post(db.Model): |
|
id = db.Column(db.Integer, primary_key=True) |
|
content = db.Column(db.Text, nullable=False) |
|
created_at = db.Column(db.DateTime, default=datetime.utcnow) |
|
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) |
|
edited_by_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=True) |
|
|
|
|
|
topic_id = db.Column(db.Integer, db.ForeignKey('topic.id'), nullable=False) |
|
author_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) |
|
|
|
|
|
reactions = db.relationship('Reaction', backref='post', lazy='dynamic', cascade='all, delete-orphan') |
|
reports = db.relationship('Report', backref='post', lazy='dynamic', |
|
primaryjoin="Report.post_id==Post.id") |
|
|
|
edited_by = db.relationship('User', foreign_keys=[edited_by_id]) |
|
|
|
def get_reaction_count(self, reaction_type=None): |
|
if reaction_type: |
|
return self.reactions.filter_by(reaction_type=reaction_type).count() |
|
return self.reactions.count() |
|
|
|
def __repr__(self): |
|
return f'<Post {self.id}>' |
|
|
|
|
|
class Tag(db.Model): |
|
id = db.Column(db.Integer, primary_key=True) |
|
name = db.Column(db.String(50), nullable=False, unique=True) |
|
|
|
def __repr__(self): |
|
return f'<Tag {self.name}>' |
|
|
|
|
|
class Reaction(db.Model): |
|
id = db.Column(db.Integer, primary_key=True) |
|
reaction_type = db.Column(db.String(20), nullable=False, default='like') |
|
created_at = db.Column(db.DateTime, default=datetime.utcnow) |
|
|
|
|
|
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) |
|
post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=False) |
|
|
|
|
|
__table_args__ = ( |
|
db.UniqueConstraint('user_id', 'post_id', name='_user_post_reaction_uc'), |
|
) |
|
|
|
def __repr__(self): |
|
return f'<Reaction {self.reaction_type}>' |
|
|
|
|
|
class Report(db.Model): |
|
id = db.Column(db.Integer, primary_key=True) |
|
reason = db.Column(db.Text, nullable=False) |
|
created_at = db.Column(db.DateTime, default=datetime.utcnow) |
|
is_resolved = db.Column(db.Boolean, default=False) |
|
resolved_at = db.Column(db.DateTime, nullable=True) |
|
resolved_by_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=True) |
|
|
|
|
|
reporter_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) |
|
topic_id = db.Column(db.Integer, db.ForeignKey('topic.id'), nullable=True) |
|
post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=True) |
|
|
|
|
|
resolved_by = db.relationship('User', foreign_keys=[resolved_by_id]) |
|
|
|
def __repr__(self): |
|
return f'<Report {self.id}>' |
|
|