class Comment(db.Model): N = 3 id = db.Column(db.Integer, primary_key=True) post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=False) user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) content = db.Column(db.String(256)) timestamp = db.Column(db.DateTime(), default=datetime.utcnow, index=True) path = db.Column(db.Text, index=True) parent_id = db.Column(db.Integer, db.ForeignKey('comment.id')) replies = db.relationship('Comment', backref=db.backref('parent', remote_side=[id]), lazy='dynamic') def save(self): db.session.add(self) db.session.commit() prefix = self.parent.path + '.' if self.parent else '' self.path = prefix + '{:0{}d}'.format(self.id, self._N) db.session.commit() def level(self): return len(self.path) // self._N - 1 def __repr__(self): return f"Comment('{self.timestamp}')"
class Download(db.Model): __bind_key__ = 'sb_db' id = db.Column(db.Integer, primary_key=True) date = db.Column(db.DateTime()) version = db.Column(db.String(64)) build = db.Column(db.String(64))
class Review(db.Model): id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) movie_id = db.Column(db.String(20), nullable=False) title = db.Column(db.String(50), nullable=False) data = db.Column(db.Text, nullable=False) date = db.Column(db.DateTime(timezone=True), default=datetime.now()) rating = db.Column(db.String(70), nullable=False) spoiler_tag = db.Column(db.Boolean)
class Download(db.Model): __bind_key__ = 'sb_db' id = db.Column(db.Integer, primary_key=True) date = db.Column(db.DateTime()) version = db.Column(db.String(64)) build = db.Column(db.String(64)) location_info = db.Column(db.String(256)) user_agent = db.Column(db.String(256))
class Note(db.Model): id = db.Column(db.Integer, primary_key=True) data = db.Column(db.String(10000)) date = db.Column(db.DateTime(timezone=True), default=func.now()) user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
class Comment(db.Model): # type: ignore __tablename__ = "comments" id = db.Column(db.Integer(), primary_key=True) body = db.Column(db.String(200), nullable=False) path = db.Column(db.Text(), index=True) created_at = db.Column( db.DateTime(timezone=True), server_default=sql.func.current_timestamp(), nullable=False, ) updated_at = db.Column( db.DateTime(timezone=True), onupdate=sql.func.current_timestamp(), ) thread_at = db.Column( db.DateTime(timezone=True), server_default=sql.func.current_timestamp(), nullable=False, ) replies = db.relationship( "Comment", cascade="all, delete", backref=db.backref("parent", remote_side=[id]), lazy="dynamic", ) author_id = db.Column(db.Integer(), db.ForeignKey("accounts.id")) blog_id = db.Column(db.Integer(), db.ForeignKey("blogs.id"), nullable=False) parent_id = db.Column(db.Integer(), db.ForeignKey("comments.id")) def __repr__(self): return f"<Comment {self.id} - {self.body}>" # Factories @classmethod def new( cls, *, body: str, author: Any, blog: Any, parent: Optional[Any] = None, thread_at: Optional[datetime.datetime] = None, ): # TODO: Make this a transaction comment = cls( body=body, author=author, blog=blog, parent=parent, thread_at=thread_at, ) db.session.add(comment) db.session.commit() comment.updated_at = None prefix = f"{comment.parent.path}." if comment.parent else "" comment.path = prefix + "{:0{}d}".format(comment.id, NUMBER_OF_DIGITS) db.session.commit() return comment # Mutators def update(self, **kwargs): """ Update Comment. """ allowed_attributes = [ "body", "author", ] for key, value in kwargs.items(): assert key in allowed_attributes setattr(self, key, value) db.session.commit() def delete(self): """ Delete Comment. """ db.session.delete(self) db.session.commit() # Properties @property def level(self): return len(self.path) // NUMBER_OF_DIGITS - 1
class Category(db.Model): # type: ignore __tablename__ = "categories" id = db.Column(db.Integer(), primary_key=True) title = db.Column(db.String(100), index=True, unique=True, nullable=False) slug = db.Column(db.String(200), unique=True, nullable=False) description = db.Column(db.String(150), nullable=False) created_at = db.Column( db.DateTime(timezone=True), server_default=sql.func.current_timestamp(), nullable=False, ) updated_at = db.Column(db.DateTime(timezone=True), onupdate=sql.func.current_timestamp()) blogs = db.relationship("Blog", secondary=association_table, backref="categories") def __repr__(self): return f"<Category {self.title}>" @classmethod def new( cls, title: str, slug: str, description: str, ): """ Create a new Category in the database. Returns a Category. """ category = cls( title=title, slug=slug, description=description, ) db.session.add(category) db.session.commit() return category # Mutators def update(self, **kwargs): """ Update Category. """ allowed_attributes = ["title", "description"] for key, value in kwargs.items(): assert key in allowed_attributes setattr(self, key, value) db.session.commit() def delete(self): """ Delete Category. """ db.session.delete(self) db.session.commit()
class Blog(db.Model): # type: ignore __tablename__ = "blogs" id = db.Column(db.Integer(), primary_key=True) title = db.Column(db.String(100), index=True, nullable=False) slug = db.Column(db.String(200), unique=True, nullable=False) description = db.Column(db.String(150)) body = db.Column(db.Text(), nullable=False) published = db.Column(db.Boolean(), nullable=False, default=False) comment = db.Column(db.Boolean(), nullable=False, default=False) created_at = db.Column( db.DateTime(timezone=True), server_default=sql.func.current_timestamp(), nullable=False, ) updated_at = db.Column(db.DateTime(timezone=True), onupdate=sql.func.current_timestamp()) author_id = db.Column(db.Integer(), db.ForeignKey("accounts.id"), nullable=True) comments = db.relationship("Comment", cascade="all, delete", backref="blog", lazy=True) def __repr__(self): return f"<Blog {self.title}>" # TODO: We need to prevent blank values. # @orm.validates("title") # def validate(self, key, value): # assert value # return value # Factories @classmethod def new( cls, title: str, slug: str, body: str, author: Any, description: Optional[str] = None, categories: Optional[List[Any]] = None, published: bool = False, comment: bool = False, ): """ Create a new Blog in the database. Returns a Blog. """ blog = cls( title=title, slug=slug, description=description, body=body, author=author, published=published, comment=comment, ) if categories: blog.categories = categories db.session.add(blog) db.session.commit() return blog # Mutators def update(self, **kwargs): """ Update Blog. """ allowed_attributes = [ "title", "description", "body", "author", "categories", "published", "comment", ] for key, value in kwargs.items(): assert key in allowed_attributes setattr(self, key, value) db.session.commit() def delete(self): """ Delete Blog. """ db.session.delete(self) db.session.commit() # Properties @property def is_published(self) -> bool: return self.published
class Note(db.Model): id = db.Column(db.Integer, primary_key=True) data = db.Column(db.String(10000)) date = db.Column(db.DateTime(timezone=True), default=func.now()) # here database name should be in lowercase user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
class Account(db.Model, flask_login.UserMixin): # type: ignore __tablename__ = "accounts" id = db.Column(db.Integer(), primary_key=True) username = db.Column(db.String(64), index=True, unique=True, nullable=False) display = db.Column(db.String(100), nullable=False) email = db.Column(db.String(120), index=True, unique=True, nullable=False) password = db.Column(db.String(255), nullable=False) about = db.Column(db.String(300)) # Authorisation statuses. admin = db.Column(db.Boolean(), nullable=False, default=False, server_default="0") confirmed = db.Column(db.Boolean(), nullable=False, default=False) created_at = db.Column( db.DateTime(timezone=True), server_default=sql.func.current_timestamp(), nullable=False, ) updated_at = db.Column(db.DateTime(timezone=True), onupdate=sql.func.current_timestamp()) seen_at = db.Column(db.DateTime(timezone=True), onupdate=sql.func.current_timestamp()) blogs = db.relationship("Blog", backref="author", lazy=True) comments = db.relationship("Comment", backref="author", lazy=True) def __repr__(self): return f"<Account {self.username}>" @login.user_loader def load_account(account_id): return Account.query.get(ident=int(account_id)) @classmethod def new( cls, username: str, display: str, email: str, password: str, admin: bool = False, confirmed: bool = False, ): """ Create a new Account in the database. Password is hashed before being saved to the database. Returns an Account. """ hashed_password = cls._hash_password(password=password) account = cls( username=username, display=display if display else username, email=email, password=hashed_password, admin=admin, confirmed=confirmed, ) db.session.add(account) db.session.commit() return account # Mutators def update(self, **kwargs): """ Update Account. """ allowed_attributes = [ "username", "display", "email", "password", "about", "admin", "confirmed", "seen_at", ] for key, value in kwargs.items(): assert key in allowed_attributes if key == "display": if not value: value = self.username if key == "password": value = Account._hash_password(password=value) setattr(self, key, value) db.session.commit() def delete(self, delete_blogs: bool = False): """ Delete Account. Params: - `delete_blogs` whether the blogs associated to the Account should be deleted. If set to `False`, the blogs will instead be transferred to the default Account. """ # TODO: Make this an atomic transaction - i.e. all-or-nothing. for blog in self.blogs: if delete_blogs: blog.delete() else: blog.update(author=None) db.session.delete(self) db.session.commit() def check_password(self, password: str) -> bool: """ Check the password against saved hashed password. """ return security.check_password_hash(pwhash=self.password, password=password) # Properties @property def is_admin(self) -> bool: return self.admin @property def is_confirmed(self) -> bool: if flask.current_app.config["ACCOUNT_ALWAYS_CONFIRMED"]: return True return self.confirmed # Queries # Private @classmethod def _hash_password(cls, password: str) -> str: """ Return hashed password. """ return security.generate_password_hash(password=password)