Browse Source

Remove Category model, replacing with parent/child relationships on the Forum class.

TODO: Top-level forums should be categories, but for now we have an explicit is_category boolean - this is because I'm not sure how to get the 'parent' form to contain a "Null" option.  Once we can get a "Null" option in the parents list, we can replace the is_category boolean code with "parent is None" checks.
RJackson 11 years ago
parent
commit
cb847f75e0

+ 21 - 22
flaskbb/admin/forms.py

@@ -21,7 +21,7 @@ from wtforms.ext.sqlalchemy.fields import (QuerySelectField,
 
 from flaskbb.helpers import SelectDateWidget
 from flaskbb.extensions import db
-from flaskbb.forum.models import Category, Forum
+from flaskbb.forum.models import Forum
 from flaskbb.user.models import User, Group
 
 USERNAME_RE = r'^[\w.+-]+$'
@@ -29,8 +29,8 @@ is_username = regexp(USERNAME_RE,
                      message=("You can only use letters, numbers or dashes"))
 
 
-def selectable_categories():
-    return Category.query.order_by(Category.id)
+def selectable_forums():
+    return Forum.query.order_by(Forum.id)
 
 
 def select_primary_group():
@@ -217,6 +217,8 @@ class AddGroupForm(GroupForm):
 
 
 class ForumForm(Form):
+    _id = None
+
     title = TextField("Forum Title", validators=[
         Required(message="Forum title required")])
 
@@ -226,28 +228,25 @@ class ForumForm(Form):
     position = IntegerField("Position", validators=[
         Required(message="Forum position required")])
 
-    category = QuerySelectField("Category",
-                                query_factory=selectable_categories,
-                                get_label="title")
+    parent = QuerySelectField("Parent",
+                              query_factory=selectable_forums,
+                              get_label="title",
+                              description="This field is not saved if this forum is a category (see \"Is a category?\" field below).")
+
+    is_category = BooleanField("Is a category?", description="Categories are root-level parents for forums.  They can not contain topics.")
+
+    def validate_parent(self, field):
+        if field.data.id == self._id:
+            raise ValidationError("A forum cannot be it's own parent!")
 
     def save(self):
         forum = Forum(title=self.title.data,
                       description=self.description.data,
-                      position=self.position.data,
-                      category_id=self.category.data.id)
-        return forum.save()
-
-
-class CategoryForm(Form):
-    title = TextField("Category Title", validators=[
-        Required(message="Category title required")])
-
-    description = TextAreaField("Description", validators=[
-        Optional()])
+                      position=self.position.data)
 
-    position = IntegerField("Position", validators=[
-        Required(message="Forum position required")])
+        if self.is_category.data:
+            forum.is_category = True
+        else:
+            forum.parent_id = self.parent.data.id
 
-    def save(self):
-        category = Category(**self.data)
-        return category.save()
+        return forum.save()

+ 8 - 54
flaskbb/admin/views.py

@@ -8,9 +8,9 @@ from flaskbb import __version__ as flaskbb_version
 from flaskbb.decorators import admin_required
 from flaskbb.extensions import db
 from flaskbb.user.models import User, Group
-from flaskbb.forum.models import Post, Topic, Forum, Category
+from flaskbb.forum.models import Post, Topic, Forum
 from flaskbb.admin.forms import (AddUserForm, EditUserForm, AddGroupForm,
-                                 EditGroupForm, ForumForm, CategoryForm)
+                                 EditGroupForm, ForumForm)
 
 
 admin = Blueprint("admin", __name__)
@@ -54,15 +54,6 @@ def groups():
     return render_template("admin/groups.html", groups=groups)
 
 
-@admin.route("/categories")
-@admin_required
-def categories():
-    page = request.args.get("page", 1, type=int)
-    categories = Category.query.\
-        paginate(page, current_app.config['USERS_PER_PAGE'], False)
-    return render_template("admin/categories.html", categories=categories)
-
-
 @admin.route("/forums")
 @admin_required
 def forums():
@@ -197,11 +188,14 @@ def edit_forum(forum_id):
     forum = Forum.query.filter_by(id=forum_id).first()
 
     form = ForumForm()
+    form._id = forum.id  # Used for validation only.
+
     if form.validate_on_submit():
         forum.title = form.title.data
         forum.description = form.description.data
         forum.position = form.position.data
-        forum.category_id = form.category.data.id
+        forum.parent_id = form.parent.data.id
+        forum.is_category = form.is_category.data
         forum.save()
 
         flash("Forum successfully edited.", "success")
@@ -210,7 +204,8 @@ def edit_forum(forum_id):
         form.title.data = forum.title
         form.description.data = forum.description
         form.position.data = forum.position
-        form.category.data = forum.category
+        form.parent.data = forum.parent
+        form.is_category.data = forum.is_category
         #form.moderators.data = forum.moderators
 
     return render_template("admin/edit_forum.html", form=form)
@@ -236,44 +231,3 @@ def add_forum():
         return redirect(url_for("admin.forums"))
 
     return render_template("admin/edit_forum.html", form=form)
-
-
-@admin.route("/categories/<int:category_id>/edit", methods=["GET", "POST"])
-@admin_required
-def edit_category(category_id):
-    category = Category.query.filter_by(id=category_id).first()
-
-    form = CategoryForm()
-    if form.validate_on_submit():
-        form.populate_obj(category)
-        category.save()
-        flash("Category successfully edited.", "success")
-        return redirect(url_for("admin.edit_category", category_id=category.id))
-    else:
-        form.title.data = category.title
-        form.description.data = category.description
-        form.position.data = category.position
-
-    return render_template("admin/edit_category.html", form=form)
-
-
-@admin.route("/categories/<int:category_id>/delete")
-@admin_required
-def delete_category(category_id):
-    category = Category.query.filter_by(id=category_id).first()
-    category.delete()
-    flash("Category successfully deleted.", "success")
-    return redirect(url_for("admin.categories"))
-
-
-@admin.route("/categories/add", methods=["GET", "POST"])
-@admin_required
-def add_category():
-    form = CategoryForm()
-
-    if form.validate_on_submit():
-        form.save()
-        flash("Category successfully added.", "success")
-        return redirect(url_for("admin.categories"))
-
-    return render_template("admin/edit_category.html", form=form)

+ 8 - 26
flaskbb/forum/models.py

@@ -196,12 +196,12 @@ class Forum(db.Model):
     title = db.Column(db.String)
     description = db.Column(db.String)
     position = db.Column(db.Integer, default=0)
-    category_id = db.Column(db.Integer, db.ForeignKey("categories.id",
-                                                      use_alter=True,
-                                                      name="fk_category_id"))
+    is_category = db.Column(db.Boolean, default=False)
+    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",
@@ -209,6 +209,7 @@ class Forum(db.Model):
 
     # One-to-many
     topics = db.relationship("Topic", backref="forum", lazy="joined")
+    children = db.relationship("Forum", backref=db.backref("parent", remote_side=[id]))
 
     moderators = db.Column(DenormalizedText)
 
@@ -228,28 +229,9 @@ class Forum(db.Model):
         db.session.commit()
         return self
 
-
-class Category(db.Model):
-    __tablename__ = "categories"
-
-    id = db.Column(db.Integer, primary_key=True)
-    title = db.Column(db.String)
-    description = db.Column(db.String)
-    position = db.Column(db.Integer, default=0)
-
-    # One-to-many
-    forums = db.relationship("Forum", backref="category", lazy="joined",
-                             primaryjoin="Forum.category_id == Category.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
+    @classmethod
+    def get_categories(cls):
+        return cls.query.filter(cls.is_category)
 
 
 """

+ 3 - 3
flaskbb/forum/views.py

@@ -19,7 +19,7 @@ from flask.ext.login import login_required, current_user
 from flaskbb.helpers import (time_diff, perm_post_reply, perm_post_topic,
                              perm_edit_post, perm_delete_topic,
                              perm_delete_post, get_online_users)
-from flaskbb.forum.models import Category, Forum, Topic, Post
+from flaskbb.forum.models import Forum, Topic, Post
 from flaskbb.forum.forms import QuickreplyForm, ReplyForm, NewTopicForm
 from flaskbb.user.models import User
 
@@ -29,7 +29,7 @@ forum = Blueprint("forum", __name__)
 
 @forum.route("/")
 def index():
-    categories = Category.query.all()
+    categories = Forum.get_categories().all()
 
     # Fetch a few stats about the forum
     user_count = User.query.count()
@@ -48,7 +48,7 @@ def index():
 
 @forum.route("/category/<int:category_id>")
 def view_category(category_id):
-    category = Category.query.filter_by(id=category_id).first()
+    category = Forum.get_categories().filter_by(id=category_id).first()
 
     return render_template("forum/category.html", category=category)
 

+ 0 - 1
flaskbb/templates/admin/admin_layout.html

@@ -10,7 +10,6 @@
                 {{ navlink('admin.overview', 'Overview') }}
                 {{ navlink('admin.users', 'Users') }}
                 {{ navlink('admin.groups', 'Groups') }}
-                {{ navlink('admin.categories', 'Categories') }}
                 {{ navlink('admin.forums', 'Forums') }}
             </ul>
         </div><!--/.sidebar -->

+ 0 - 27
flaskbb/templates/admin/categories.html

@@ -1,27 +0,0 @@
-{% extends "admin/admin_layout.html" %}
-{% block admin_content %}
-<legend>Manage Categories | <a href="{{ url_for('admin.add_category') }}">Add Category</a></legend>
-<table class="table table-bordered">
-    <thead>
-        <tr>
-            <th>#</th>
-            <th>Category Name</th>
-            <th>Description</th>
-            <th>Manage</th>
-        </tr>
-    </thead>
-    <tbody>
-        {% for category in categories.items %}
-        <tr>
-            <td>{{ category.id }}</td>
-            <td><a href="#">{{ category.title }}</a></td>
-            <td>{{ category.description }}</td>
-            <td>
-                <a href="{{ url_for('admin.edit_category', category_id = category.id) }}">Edit</a> |
-                <a href="{{ url_for('admin.delete_category', category_id = category.id) }}">Delete</a>
-            </td>
-        </tr>
-        {% endfor %}
-    </tbody>
-</table>
-{% endblock %}

+ 0 - 22
flaskbb/templates/admin/edit_category.html

@@ -1,22 +0,0 @@
-{% set page_title = "Edit Category" %}
-{% set active_forum_nav=True %}
-
-{% extends "admin/admin_layout.html" %}
-{% block admin_content %}
-{% from "macros.html" import horizontal_field %}
-
-<form class="form-horizontal" role="form" method="post">
-    {{ form.hidden_tag() }}
-    <legend class="">Edit Forum</legend>
-        {{ horizontal_field(form.title) }}
-        {{ horizontal_field(form.position) }}
-        {{ horizontal_field(form.description, rows=5, div_class="col-lg-9") }}
-
-        <div class="form-group">
-            <div class="col-lg-offset-0 col-lg-9">
-                <button type="submit" class="btn btn-success">Save</button>
-            </div>
-        </div>
-</form>
-
-{% endblock %}

+ 6 - 3
flaskbb/templates/admin/edit_forum.html

@@ -3,16 +3,19 @@
 
 {% extends "admin/admin_layout.html" %}
 {% block admin_content %}
-{% from "macros.html" import horizontal_field %}
+{% from "macros.html" import horizontal_field, render_boolean_field %}
 
 <form class="form-horizontal" role="form" method="post">
     {{ form.hidden_tag() }}
     <legend class="">Edit Forum</legend>
         {{ horizontal_field(form.title) }}
-        {{ horizontal_field(form.position) }}
-        {{ horizontal_field(form.category) }}
         {{ horizontal_field(form.description, rows=5, div_class="col-lg-9") }}
 
+        {{ horizontal_field(form.parent) }}
+        {{ horizontal_field(form.position) }}
+
+        {{ render_boolean_field(form.is_category) }}
+
         <div class="form-group">
             <div class="col-lg-offset-0 col-lg-9">
                 <button type="submit" class="btn btn-success">Save</button>

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

@@ -14,7 +14,7 @@
             <td width="200" align="center" style="white-space: nowrap"><strong>Last Post</strong></td>
         </tr>
 
-        {% for forum in category.forums %}
+        {% for forum in category.children %}
         <tr>
             <td align="center" valign="center" width="1">
                 New </br> Posts

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

@@ -7,7 +7,7 @@
 
 <ol class="breadcrumb">
     <li><a href="{{ url_for('forum.index') }}">Forum</a></li>
-    <li><a href="{{ url_for('forum.view_category', category_id=forum.category_id) }}">{{ forum.category.title }}</a></li>
+    <li><a href="{{ url_for('forum.view_category', category_id=forum.parent_id) }}">{{ forum.parent.title }}</a></li>
     <li class="active">{{ forum.title }}</li>
 </ol>
 

+ 3 - 3
manage.py

@@ -20,7 +20,7 @@ from flaskbb import create_app
 from flaskbb.extensions import db
 
 from flaskbb.user.models import User, Group
-from flaskbb.forum.models import Post, Topic, Forum, Category
+from flaskbb.forum.models import Post, Topic, Forum
 
 # Use the development configuration if available
 try:
@@ -179,7 +179,7 @@ def createall():
     # create 2 categories
     for i in range(1, 3):
         category_title = "Test Category %s" % i
-        category = Category(title=category_title,
+        category = Forum(is_category=True, title=category_title,
                             description="Test Description")
         db.session.add(category)
 
@@ -190,7 +190,7 @@ def createall():
 
             forum_title = "Test Forum %s %s" % (j, i)
             forum = Forum(title=forum_title, description="Test Description",
-                          category_id=i)
+                          parent_id=i)
             db.session.add(forum)
 
     # create 1 topic in each forum