|
@@ -8,9 +8,10 @@
|
|
|
:copyright: (c) 2013 by the FlaskBB Team.
|
|
|
:license: BSD, see LICENSE for more details.
|
|
|
"""
|
|
|
+import sys
|
|
|
from datetime import datetime
|
|
|
|
|
|
-from flaskbb.extensions import db
|
|
|
+from flaskbb.extensions import db, cache
|
|
|
from flaskbb.helpers import DenormalizedText
|
|
|
from helpers import get_forum_ids
|
|
|
|
|
@@ -27,6 +28,14 @@ class Post(db.Model):
|
|
|
date_created = db.Column(db.DateTime, default=datetime.utcnow())
|
|
|
date_modified = db.Column(db.DateTime)
|
|
|
|
|
|
+ # Methods
|
|
|
+ def __repr__(self):
|
|
|
+ """
|
|
|
+ Set to a unique key specific to the object in the database.
|
|
|
+ Required for cache.memoize() to work across requests.
|
|
|
+ """
|
|
|
+ return "<{} {})>".format(self.__class__.__name__, self.id)
|
|
|
+
|
|
|
def save(self, user=None, topic=None):
|
|
|
# update/edit the post
|
|
|
if self.id:
|
|
@@ -44,16 +53,10 @@ class Post(db.Model):
|
|
|
db.session.add(self)
|
|
|
db.session.commit()
|
|
|
|
|
|
- # Now lets update the last post id
|
|
|
- # TODO: Invalidate relevant caches.
|
|
|
- #topic.last_post_id = self.id
|
|
|
- #topic.forum.last_post_id = self.id
|
|
|
-
|
|
|
- # Update the post counts
|
|
|
- # TODO: Invalidate relevant caches.
|
|
|
- #user.post_count += 1
|
|
|
- #topic.post_count += 1
|
|
|
- #topic.forum.post_count += 1
|
|
|
+ # Invalidate relevant caches
|
|
|
+ user.invalidate_cache()
|
|
|
+ topic.invalidate_cache()
|
|
|
+ topic.forum.invalidate_cache()
|
|
|
|
|
|
# And commit it!
|
|
|
db.session.add(topic)
|
|
@@ -66,19 +69,10 @@ class Post(db.Model):
|
|
|
self.topic.delete()
|
|
|
return self
|
|
|
|
|
|
- # Delete the last post
|
|
|
- if self.topic.last_post.id == self.id:
|
|
|
- # Now the second last post will be the last post
|
|
|
- # TODO: Invalidate relevant caches
|
|
|
- #self.topic.last_post_id = self.topic.second_last_post
|
|
|
- #self.topic.forum.last_post_id = self.topic.second_last_post
|
|
|
- db.session.commit()
|
|
|
-
|
|
|
- # Update the post counts
|
|
|
- # TODO: Invalidate relevant caches
|
|
|
- #self.user.post_count -= 1
|
|
|
- #self.topic.post_count -= 1
|
|
|
- #self.topic.forum.post_count -= 1
|
|
|
+ # Invalidate relevant caches
|
|
|
+ self.user.invalidate_cache()
|
|
|
+ self.topic.invalidate_cache()
|
|
|
+ self.topic.forum.invalidate_cache()
|
|
|
|
|
|
# Is there a better way to do this?
|
|
|
db.session.delete(self)
|
|
@@ -104,42 +98,33 @@ class Topic(db.Model):
|
|
|
primaryjoin="Post.topic_id == Topic.id",
|
|
|
cascade="all, delete-orphan", post_update=True)
|
|
|
|
|
|
- def __init__(self, title=None):
|
|
|
- if title:
|
|
|
- self.title = title
|
|
|
-
|
|
|
-
|
|
|
+ # Properties
|
|
|
@property
|
|
|
def post_count(self):
|
|
|
"""
|
|
|
- Returns the amount of posts within the current topic.
|
|
|
+ Property interface for get_post_count method.
|
|
|
+
|
|
|
+ Method seperate for easy invalidation of cache.
|
|
|
"""
|
|
|
- # TODO: Cache
|
|
|
- return Post.query.\
|
|
|
- filter(Post.topic_id == self.id).\
|
|
|
- count()
|
|
|
+ return self.get_post_count()
|
|
|
|
|
|
@property
|
|
|
def first_post(self):
|
|
|
"""
|
|
|
- Returns the first post within the current topic.
|
|
|
+ Property interface for get_first_post method.
|
|
|
+
|
|
|
+ Method seperate for easy invalidation of cache.
|
|
|
"""
|
|
|
- # TODO: Cache this method.
|
|
|
- return Post.query.\
|
|
|
- filter(Post.topic_id == self.id).\
|
|
|
- order_by(Post.date_created.asc()).\
|
|
|
- first()
|
|
|
+ return self.get_first_post()
|
|
|
|
|
|
@property
|
|
|
def last_post(self):
|
|
|
"""
|
|
|
- Returns the latest post within the current topic.
|
|
|
+ Property interface for get_last_post method.
|
|
|
+
|
|
|
+ Method seperate for easy invalidation of cache.
|
|
|
"""
|
|
|
- # TODO: Cache this method.
|
|
|
- return Post.query.\
|
|
|
- filter(Post.topic_id == self.id).\
|
|
|
- order_by(Post.date_created.desc()).\
|
|
|
- first()
|
|
|
+ return self.get_last_post()
|
|
|
|
|
|
@property
|
|
|
def second_last_post(self):
|
|
@@ -148,6 +133,18 @@ class Topic(db.Model):
|
|
|
"""
|
|
|
return self.posts[-2].id
|
|
|
|
|
|
+ # Methods
|
|
|
+ def __init__(self, title=None):
|
|
|
+ if title:
|
|
|
+ self.title = title
|
|
|
+
|
|
|
+ def __repr__(self):
|
|
|
+ """
|
|
|
+ Set to a unique key specific to the object in the database.
|
|
|
+ Required for cache.memoize() to work across requests.
|
|
|
+ """
|
|
|
+ return "<{} {})>".format(self.__class__.__name__, self.id)
|
|
|
+
|
|
|
def save(self, user=None, forum=None, post=None):
|
|
|
# Updates the topic - Because the thread title (by intention)
|
|
|
# isn't change able, so we are just going to update the post content
|
|
@@ -160,9 +157,6 @@ class Topic(db.Model):
|
|
|
self.forum_id = forum.id
|
|
|
self.user_id = user.id
|
|
|
|
|
|
- # Update the topic count
|
|
|
- forum.topic_count += 1
|
|
|
-
|
|
|
# Insert and commit the topic
|
|
|
db.session.add(self)
|
|
|
db.session.commit()
|
|
@@ -170,50 +164,66 @@ class Topic(db.Model):
|
|
|
# Create the topic post
|
|
|
post.save(user, self)
|
|
|
|
|
|
- # Update the first post id
|
|
|
- # TODO: Invalidate first_post cache
|
|
|
- #self.first_post_id = post.id
|
|
|
+ # Invalidate relevant caches
|
|
|
+ self.invalidate_cache()
|
|
|
+ self.forum.invalidate_cache()
|
|
|
+
|
|
|
db.session.commit()
|
|
|
|
|
|
return self
|
|
|
|
|
|
def delete(self, users=None):
|
|
|
- # TODO: Invalidate forum last post caches
|
|
|
- #topic = Topic.query.filter_by(forum_id=self.forum_id).\
|
|
|
- # order_by(Topic.last_post_id.desc())
|
|
|
- #
|
|
|
- #if topic and topic[0].id == self.id:
|
|
|
- # try:
|
|
|
- # self.forum.last_post_id = topic[1].last_post_id
|
|
|
- #Catch an IndexError when you delete the last topic in the forum
|
|
|
- #except IndexError:
|
|
|
- # self.forum.last_post_id = 0
|
|
|
-
|
|
|
- # These things needs to be stored in a variable before they are deleted
|
|
|
- forum = self.forum
|
|
|
+ # Invalidate relevant caches
|
|
|
+ self.invalidate_cache()
|
|
|
+ self.forum.invalidate_cache()
|
|
|
+
|
|
|
+ # Invalidate user post counts
|
|
|
+ if users:
|
|
|
+ for user in users:
|
|
|
+ user.invalidate_cache()
|
|
|
|
|
|
# Delete the topic
|
|
|
db.session.delete(self)
|
|
|
db.session.commit()
|
|
|
|
|
|
- # Update the post counts
|
|
|
- # TODO: Invalidate relevant caches
|
|
|
- #if users:
|
|
|
- # If someone knows a better method for this,
|
|
|
- # feel free to improve it :)
|
|
|
- #for user in users:
|
|
|
- # user.post_count = Post.query.filter_by(user_id=user.id).count()
|
|
|
- # db.session.commit()
|
|
|
- #forum.topic_count = Topic.query.filter_by(
|
|
|
- # forum_id=self.forum_id).count()
|
|
|
- #
|
|
|
- #forum.post_count = Post.query.filter(
|
|
|
- # Post.topic_id == Topic.id,
|
|
|
- # Topic.forum_id == self.forum_id).count()
|
|
|
+ return self
|
|
|
|
|
|
- db.session.commit()
|
|
|
+ @cache.memoize(timeout=sys.maxint)
|
|
|
+ def get_post_count(self):
|
|
|
+ """
|
|
|
+ Returns the amount of posts within the current topic.
|
|
|
+ """
|
|
|
+ return Post.query.\
|
|
|
+ filter(Post.topic_id == self.id).\
|
|
|
+ count()
|
|
|
|
|
|
- return self
|
|
|
+ #@cache.memoize(timeout=sys.maxint) # TODO: DetachedInstanceError if we return a Flask-SQLAlchemy model.
|
|
|
+ def get_first_post(self):
|
|
|
+ """
|
|
|
+ Returns the first post within the current topic.
|
|
|
+ """
|
|
|
+ return Post.query.\
|
|
|
+ filter(Post.topic_id == self.id).\
|
|
|
+ order_by(Post.date_created.asc()).\
|
|
|
+ first()
|
|
|
+
|
|
|
+ #@cache.memoize(timeout=sys.maxint) # TODO: DetachedInstanceError if we return a Flask-SQLAlchemy model.
|
|
|
+ def get_last_post(self):
|
|
|
+ """
|
|
|
+ Returns the latest post within the current topic.
|
|
|
+ """
|
|
|
+ return Post.query.\
|
|
|
+ filter(Post.topic_id == self.id).\
|
|
|
+ order_by(Post.date_created.desc()).\
|
|
|
+ first()
|
|
|
+
|
|
|
+ def invalidate_cache(self):
|
|
|
+ """
|
|
|
+ Invalidates this objects cached metadata.
|
|
|
+ """
|
|
|
+ cache.delete_memoized(self.get_post_count, self)
|
|
|
+ #cache.delete_memoized(self.get_first_post, self) # TODO: Cannot use til we can cache this object.
|
|
|
+ #cache.delete_memoized(self.get_last_post, self) # TODO: Cannot use til we can cache this object.
|
|
|
|
|
|
|
|
|
class Forum(db.Model):
|
|
@@ -232,13 +242,74 @@ class Forum(db.Model):
|
|
|
|
|
|
moderators = db.Column(DenormalizedText) # TODO: No forum_moderators column?
|
|
|
|
|
|
+ # Properties
|
|
|
+ @property
|
|
|
+ def post_count(self):
|
|
|
+ """
|
|
|
+ Property interface for get_post_count method.
|
|
|
+
|
|
|
+ Method seperate for easy invalidation of cache.
|
|
|
+ """
|
|
|
+ return self.get_post_count()
|
|
|
+
|
|
|
+ @property
|
|
|
+ def topic_count(self):
|
|
|
+ """
|
|
|
+ Property interface for get_topic_count method.
|
|
|
+
|
|
|
+ Method seperate for easy invalidation of cache.
|
|
|
+ """
|
|
|
+ return self.get_topic_count()
|
|
|
+
|
|
|
@property
|
|
|
- def post_count(self, include_children=True):
|
|
|
+ def last_post(self):
|
|
|
+ """
|
|
|
+ Property interface for get_last_post method.
|
|
|
+
|
|
|
+ Method seperate for easy invalidation of cache.
|
|
|
+ """
|
|
|
+ return self.get_last_post()
|
|
|
+
|
|
|
+ # Methods
|
|
|
+ def __repr__(self):
|
|
|
+ """
|
|
|
+ Set to a unique key specific to the object in the database.
|
|
|
+ Required for cache.memoize() to work across requests.
|
|
|
+ """
|
|
|
+ return "<{} {})>".format(self.__class__.__name__, self.id)
|
|
|
+
|
|
|
+ def add_moderator(self, user_id):
|
|
|
+ self.moderators.add(user_id)
|
|
|
+
|
|
|
+ def remove_moderator(self, user_id):
|
|
|
+ self.moderators.remove(user_id)
|
|
|
+
|
|
|
+ def save(self):
|
|
|
+ db.session.add(self)
|
|
|
+ db.session.commit()
|
|
|
+ return self
|
|
|
+
|
|
|
+ def delete(self):
|
|
|
+ db.session.delete(self)
|
|
|
+ db.session.commit()
|
|
|
+ return self
|
|
|
+
|
|
|
+ def get_breadcrumbs(self):
|
|
|
+ breadcrumbs = []
|
|
|
+ parent = self.parent
|
|
|
+ while parent is not None:
|
|
|
+ breadcrumbs.append(parent)
|
|
|
+ parent = parent.parent
|
|
|
+
|
|
|
+ breadcrumbs.reverse()
|
|
|
+ return breadcrumbs
|
|
|
+
|
|
|
+ @cache.memoize(timeout=sys.maxint)
|
|
|
+ def get_post_count(self, include_children=True):
|
|
|
"""
|
|
|
Returns the amount of posts within the current forum or it's children.
|
|
|
Children can be excluded by setting the second parameter to 'false'.
|
|
|
"""
|
|
|
- # TODO: Cache
|
|
|
|
|
|
if include_children:
|
|
|
return Post.query.\
|
|
@@ -251,13 +322,12 @@ class Forum(db.Model):
|
|
|
filter(Topic.forum_id == self.id).\
|
|
|
count()
|
|
|
|
|
|
- @property
|
|
|
- def topic_count(self, include_children=True):
|
|
|
+ @cache.memoize(timeout=sys.maxint)
|
|
|
+ def get_topic_count(self, include_children=True):
|
|
|
"""
|
|
|
Returns the amount of topics within the current forum or it's children.
|
|
|
Children can be excluded by setting the second parameter to 'false'.
|
|
|
"""
|
|
|
- # TODO: Cache
|
|
|
|
|
|
if include_children:
|
|
|
return Topic.query.\
|
|
@@ -268,13 +338,12 @@ class Forum(db.Model):
|
|
|
filter(Topic.forum_id == self.id).\
|
|
|
count()
|
|
|
|
|
|
- @property
|
|
|
- def last_post(self, include_children=True):
|
|
|
+ #@cache.memoize(timeout=sys.maxint) # TODO: DetachedInstanceError if we return a Flask-SQLAlchemy model.
|
|
|
+ def get_last_post(self, include_children=True):
|
|
|
"""
|
|
|
Returns the latest post within the current forum or it's children.
|
|
|
Children can be excluded by setting the second parameter to 'false'.
|
|
|
"""
|
|
|
- # TODO: Cache this method.
|
|
|
|
|
|
if include_children:
|
|
|
return Post.query.\
|
|
@@ -289,37 +358,22 @@ class Forum(db.Model):
|
|
|
order_by(Post.date_created.desc()).\
|
|
|
first()
|
|
|
|
|
|
-
|
|
|
- def add_moderator(self, user_id):
|
|
|
- self.moderators.add(user_id)
|
|
|
-
|
|
|
- def remove_moderator(self, user_id):
|
|
|
- self.moderators.remove(user_id)
|
|
|
-
|
|
|
- def save(self):
|
|
|
- db.session.add(self)
|
|
|
- db.session.commit()
|
|
|
- return self
|
|
|
-
|
|
|
- def delete(self):
|
|
|
- db.session.delete(self)
|
|
|
- db.session.commit()
|
|
|
- return self
|
|
|
-
|
|
|
+ def invalidate_cache(self):
|
|
|
+ """
|
|
|
+ Invalidates this objects, and it's parents', cached metadata.
|
|
|
+ """
|
|
|
+ _forum = self
|
|
|
+ while _forum.parent:
|
|
|
+ cache.delete_memoized(self.get_post_count, _forum)
|
|
|
+ cache.delete_memoized(self.get_topic_count, _forum)
|
|
|
+ #cache.delete_memoized(self.get_last_post, _forum) # TODO: Cannot use til we can cache this object.
|
|
|
+ _forum = _forum.parent
|
|
|
+
|
|
|
+ # Class methods
|
|
|
@classmethod
|
|
|
def get_categories(cls):
|
|
|
return cls.query.filter(cls.is_category)
|
|
|
|
|
|
- def get_breadcrumbs(self):
|
|
|
- breadcrumbs = []
|
|
|
- parent = self.parent
|
|
|
- while parent is not None:
|
|
|
- breadcrumbs.append(parent)
|
|
|
- parent = parent.parent
|
|
|
-
|
|
|
- breadcrumbs.reverse()
|
|
|
- return breadcrumbs
|
|
|
-
|
|
|
|
|
|
"""
|
|
|
A topic can be tracked by many users
|
|
@@ -343,6 +397,13 @@ class Tracking(db.Model):
|
|
|
topic_id = db.Column(db.Integer, db.ForeignKey("topics.id"))
|
|
|
last_read = db.Column(db.DateTime, default=datetime.utcnow())
|
|
|
|
|
|
+ def __repr__(self):
|
|
|
+ """
|
|
|
+ Set to a unique key specific to the object in the database.
|
|
|
+ Required for cache.memoize() to work across requests.
|
|
|
+ """
|
|
|
+ return "<{} {})>".format(self.__class__.__name__, self.id)
|
|
|
+
|
|
|
def save(self):
|
|
|
db.session.add(self)
|
|
|
db.session.commit()
|