Browse Source

Replaced some model properties with db queries, first step in moving them into caching layer rather than db layer.

Replaced properties:
- `Topic.post_count`
- `Topic.first_post_id`, `Topic.first_post` -> `Topic.first_post`
- `Topic.last_post`, `Topic.last_post` -> `Topic.last_post`
- `Forum.post_count`
- `Forum.topic_count`
- `Forum.last_post_id`, `Forum.last_post` -> `Forum.last_post`
- `User.post_count`
RJackson 11 years ago
parent
commit
ff0a104ff4

+ 11 - 0
flaskbb/forum/helpers.py

@@ -0,0 +1,11 @@
+def get_forum_ids(forum):
+    """
+    Returns a list of forum ids for the passed `forum` object and its child hierarchy.
+    """
+    forum_ids = [forum.id]
+    if forum.children:
+        for child in forum.children:
+            forum_ids.extend(
+                get_forum_ids(child)
+            )
+    return forum_ids

+ 133 - 56
flaskbb/forum/models.py

@@ -12,6 +12,7 @@ from datetime import datetime
 
 
 from flaskbb.extensions import db
 from flaskbb.extensions import db
 from flaskbb.helpers import DenormalizedText
 from flaskbb.helpers import DenormalizedText
+from helpers import get_forum_ids
 
 
 
 
 class Post(db.Model):
 class Post(db.Model):
@@ -44,13 +45,15 @@ class Post(db.Model):
             db.session.commit()
             db.session.commit()
 
 
             # Now lets update the last post id
             # Now lets update the last post id
-            topic.last_post_id = self.id
-            topic.forum.last_post_id = self.id
+            # TODO: Invalidate relevant caches.
+            #topic.last_post_id = self.id
+            #topic.forum.last_post_id = self.id
 
 
             # Update the post counts
             # Update the post counts
-            user.post_count += 1
-            topic.post_count += 1
-            topic.forum.post_count += 1
+            # TODO: Invalidate relevant caches.
+            #user.post_count += 1
+            #topic.post_count += 1
+            #topic.forum.post_count += 1
 
 
             # And commit it!
             # And commit it!
             db.session.add(topic)
             db.session.add(topic)
@@ -64,16 +67,18 @@ class Post(db.Model):
             return self
             return self
 
 
         # Delete the last post
         # Delete the last post
-        if self.topic.last_post_id == self.id:
+        if self.topic.last_post.id == self.id:
             # Now the second last post will be the last post
             # Now the second last post will be the last post
-            self.topic.last_post_id = self.topic.second_last_post
-            self.topic.forum.last_post_id = self.topic.second_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()
             db.session.commit()
 
 
         # Update the post counts
         # Update the post counts
-        self.user.post_count -= 1
-        self.topic.post_count -= 1
-        self.topic.forum.post_count -= 1
+        # TODO: Invalidate relevant caches
+        #self.user.post_count -= 1
+        #self.topic.post_count -= 1
+        #self.topic.forum.post_count -= 1
 
 
         # Is there a better way to do this?
         # Is there a better way to do this?
         db.session.delete(self)
         db.session.delete(self)
@@ -93,20 +98,6 @@ class Topic(db.Model):
     locked = db.Column(db.Boolean, default=False)
     locked = db.Column(db.Boolean, default=False)
     important = db.Column(db.Boolean, default=False)
     important = db.Column(db.Boolean, default=False)
     views = db.Column(db.Integer, default=0)
     views = db.Column(db.Integer, default=0)
-    post_count = db.Column(db.Integer, default=0)
-
-    # One-to-one (uselist=False) relationship between first_post and topic
-    first_post_id = db.Column(db.Integer, db.ForeignKey("posts.id",
-                                                        ondelete="CASCADE"))
-    first_post = db.relationship("Post", backref="first_post", uselist=False,
-                                 foreign_keys=[first_post_id])
-
-    # One-to-one
-    last_post_id = db.Column(db.Integer, db.ForeignKey("posts.id",
-                                                       ondelete="CASCADE",
-                                                       onupdate="CASCADE"))
-    last_post = db.relationship("Post", backref="last_post", uselist=False,
-                                foreign_keys=[last_post_id])
 
 
     # One-to-many
     # One-to-many
     posts = db.relationship("Post", backref="topic", lazy="joined",
     posts = db.relationship("Post", backref="topic", lazy="joined",
@@ -117,6 +108,39 @@ class Topic(db.Model):
         if title:
         if title:
             self.title = title
             self.title = title
 
 
+
+    @property
+    def post_count(self):
+        """
+        Returns the amount of posts within the current topic.
+        """
+        # TODO: Cache
+        return Post.query.\
+            filter(Post.topic_id == self.id).\
+            count()
+
+    @property
+    def first_post(self):
+        """
+        Returns the first post within the current topic.
+        """
+        # TODO: Cache this method.
+        return Post.query.\
+            filter(Post.topic_id == self.id).\
+            order_by(Post.date_created.asc()).\
+            first()
+
+    @property
+    def last_post(self):
+        """
+        Returns the latest post within the current topic.
+        """
+        # TODO: Cache this method.
+        return Post.query.\
+            filter(Post.topic_id == self.id).\
+            order_by(Post.date_created.desc()).\
+            first()
+
     @property
     @property
     def second_last_post(self):
     def second_last_post(self):
         """
         """
@@ -147,21 +171,23 @@ class Topic(db.Model):
         post.save(user, self)
         post.save(user, self)
 
 
         # Update the first post id
         # Update the first post id
-        self.first_post_id = post.id
+        # TODO: Invalidate first_post cache
+        #self.first_post_id = post.id
         db.session.commit()
         db.session.commit()
 
 
         return self
         return self
 
 
     def delete(self, users=None):
     def delete(self, users=None):
-        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
+        # 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
         # These things needs to be stored in a variable before they are deleted
         forum = self.forum
         forum = self.forum
@@ -171,18 +197,19 @@ class Topic(db.Model):
         db.session.commit()
         db.session.commit()
 
 
         # Update the post counts
         # Update the post counts
-        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()
+        # 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()
 
 
         db.session.commit()
         db.session.commit()
 
 
@@ -199,19 +226,69 @@ class Forum(db.Model):
     is_category = db.Column(db.Boolean, default=False)
     is_category = db.Column(db.Boolean, default=False)
     parent_id = db.Column(db.Integer, db.ForeignKey("forums.id"))
     parent_id = db.Column(db.Integer, db.ForeignKey("forums.id"))
 
 
-    # TODO:  Remove post_count, topic_count, last_post_id, and last_post from Forum model.  They should be in cache layer, not database.
-    post_count = db.Column(db.Integer, default=0)
-    topic_count = db.Column(db.Integer, default=0)
-    # One-to-one
-    last_post_id = db.Column(db.Integer, db.ForeignKey("posts.id"))
-    last_post = db.relationship("Post", backref="last_post_forum",
-                                uselist=False, foreign_keys=[last_post_id])
-
     # One-to-many
     # One-to-many
     topics = db.relationship("Topic", backref="forum", lazy="joined")
     topics = db.relationship("Topic", backref="forum", lazy="joined")
     children = db.relationship("Forum", backref=db.backref("parent", remote_side=[id]))
     children = db.relationship("Forum", backref=db.backref("parent", remote_side=[id]))
 
 
-    moderators = db.Column(DenormalizedText)
+    moderators = db.Column(DenormalizedText)  # TODO: No forum_moderators column?
+
+    @property
+    def 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.\
+                filter(Post.topic_id == Topic.id). \
+                filter(Topic.forum_id.in_(get_forum_ids(self))). \
+                count()
+        else:
+            return Post.query.\
+                filter(Post.topic_id == Topic.id).\
+                filter(Topic.forum_id == self.id).\
+                count()
+
+    @property
+    def 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.\
+                filter(Topic.forum_id.in_(get_forum_ids(self))). \
+                count()
+        else:
+            return Topic.query.\
+                filter(Topic.forum_id == self.id).\
+                count()
+
+    @property
+    def 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.\
+                filter(Post.topic_id == Topic.id). \
+                filter(Topic.forum_id.in_(get_forum_ids(self))). \
+                order_by(Post.date_created.desc()). \
+                first()
+        else:
+            return Post.query.\
+                filter(Post.topic_id == Topic.id).\
+                filter(Topic.forum_id == self.id).\
+                order_by(Post.date_created.desc()).\
+                first()
+
 
 
     def add_moderator(self, user_id):
     def add_moderator(self, user_id):
         self.moderators.add(user_id)
         self.moderators.add(user_id)

+ 2 - 1
flaskbb/forum/views.py

@@ -51,7 +51,8 @@ def view_forum(forum_id):
 
 
     forum = Forum.query.filter_by(id=forum_id).first()
     forum = Forum.query.filter_by(id=forum_id).first()
     topics = Topic.query.filter_by(forum_id=forum.id).\
     topics = Topic.query.filter_by(forum_id=forum.id).\
-        order_by(Topic.last_post_id.desc()).\
+        filter(Post.topic_id == Topic.id).\
+        order_by(Post.id.desc()).\
         paginate(page, current_app.config['TOPICS_PER_PAGE'], False)
         paginate(page, current_app.config['TOPICS_PER_PAGE'], False)
 
 
     return render_template("forum/forum.html", forum=forum, topics=topics)
     return render_template("forum/forum.html", forum=forum, topics=topics)

+ 2 - 2
flaskbb/templates/forum/category_layout.html

@@ -46,8 +46,8 @@
             <td valign="top" align="center" style="white-space: nowrap">{{ forum.post_count }}</td>
             <td valign="top" align="center" style="white-space: nowrap">{{ forum.post_count }}</td>
 
 
             <td valign="top" align="right" style="white-space: nowrap">
             <td valign="top" align="right" style="white-space: nowrap">
-                {% if forum.last_post_id %}
-                <a href="{{ url_for('forum.view_post', post_id=forum.last_post_id) }}" title="{{ forum.last_post.topic.title }}">
+                {% if forum.last_post %}
+                <a href="{{ url_for('forum.view_post', post_id=forum.last_post.id) }}" title="{{ forum.last_post.topic.title }}">
                     <strong>{{ forum.last_post.topic.title|crop_title }}</strong>
                     <strong>{{ forum.last_post.topic.title|crop_title }}</strong>
                 </a>
                 </a>
                 <br />
                 <br />

+ 2 - 2
flaskbb/templates/forum/forum.html

@@ -71,8 +71,8 @@
             <td valign="top" align="center" style="white-space: nowrap">{{ subforum.post_count }}</td>
             <td valign="top" align="center" style="white-space: nowrap">{{ subforum.post_count }}</td>
 
 
             <td valign="top" align="right" style="white-space: nowrap">
             <td valign="top" align="right" style="white-space: nowrap">
-                {% if subforum.last_post_id %}
-                <a href="{{ url_for('forum.view_post', post_id=subforum.last_post_id) }}" title="{{ subforum.last_post.topic.title }}">
+                {% if subforum.last_post %}
+                <a href="{{ url_for('forum.view_post', post_id=subforum.last_post.id) }}" title="{{ subforum.last_post.topic.title }}">
                     <strong>{{ subforum.last_post.topic.title|crop_title }}</strong>
                     <strong>{{ subforum.last_post.topic.title|crop_title }}</strong>
                 </a>
                 </a>
                 <br />
                 <br />

+ 11 - 3
flaskbb/user/models.py

@@ -75,8 +75,6 @@ class User(db.Model, UserMixin):
     posts = db.relationship("Post", backref="user", lazy="dynamic")
     posts = db.relationship("Post", backref="user", lazy="dynamic")
     topics = db.relationship("Topic", backref="user", lazy="dynamic")
     topics = db.relationship("Topic", backref="user", lazy="dynamic")
 
 
-    post_count = db.Column(db.Integer, default=0)
-
     primary_group_id = db.Column(db.Integer, db.ForeignKey('groups.id'))
     primary_group_id = db.Column(db.Integer, db.ForeignKey('groups.id'))
 
 
     primary_group = db.relationship('Group', lazy="joined",
     primary_group = db.relationship('Group', lazy="joined",
@@ -161,6 +159,15 @@ class User(db.Model, UserMixin):
         return expired, invalid, data
         return expired, invalid, data
 
 
     @property
     @property
+    def post_count(self):
+        """
+        Returns the amount of posts within the current topic.
+        """
+        # TODO: Cache
+        return Post.query.filter(Post.user_id == self.id).\
+            count()
+
+    @property
     def last_post(self):
     def last_post(self):
         """
         """
         Returns the latest post from the user
         Returns the latest post from the user
@@ -173,7 +180,8 @@ class User(db.Model, UserMixin):
         Returns a paginated query result with all topics the user has created.
         Returns a paginated query result with all topics the user has created.
         """
         """
         return Topic.query.filter(Topic.user_id == self.id).\
         return Topic.query.filter(Topic.user_id == self.id).\
-            order_by(Topic.last_post_id.desc()).\
+            filter(Post.topic_id == Topic.id).\
+            order_by(Post.id.desc()).\
             paginate(page, current_app.config['TOPICS_PER_PAGE'], False)
             paginate(page, current_app.config['TOPICS_PER_PAGE'], False)
 
 
     def all_posts(self, page):
     def all_posts(self, page):

+ 11 - 9
manage.py

@@ -209,10 +209,11 @@ def createall():
         db.session.commit()
         db.session.commit()
 
 
         # Update the post and topic count
         # Update the post and topic count
-        topic.forum.topic_count += 1
-        topic.forum.post_count += 1
-        topic.post_count += 1
-        topic.first_post.user.post_count += 1
+        # TODO: Invalidate relevant caches
+        #topic.forum.topic_count += 1
+        #topic.forum.post_count += 1
+        #topic.post_count += 1
+        #topic.first_post.user.post_count += 1
 
 
         # create 2 additional posts for each topic
         # create 2 additional posts for each topic
         for m in range(1, 3):
         for m in range(1, 3):
@@ -222,12 +223,13 @@ def createall():
             db.session.commit()
             db.session.commit()
 
 
             # Update the post count
             # Update the post count
-            post.user.post_count += 1
-            topic.post_count += 1
-            topic.forum.post_count += 1
+            # TODO: Invalidate relevant caches
+            #post.user.post_count += 1
+            #topic.post_count += 1
+            #topic.forum.post_count += 1
 
 
-            topic.last_post_id = post.id
-            topic.forum.last_post_id = post.id
+            #topic.last_post_id = post.id
+            #topic.forum.last_post_id = post.id
 
 
             db.session.commit()
             db.session.commit()