Skip to content

Commit

Permalink
Merge pull request #412 from sh4nks/cascades
Browse files Browse the repository at this point in the history
Fix some data inconsistencies
  • Loading branch information
sh4nks committed Feb 12, 2018
2 parents d8397e2 + f9cddcb commit 0a0a8e6
Show file tree
Hide file tree
Showing 12 changed files with 411 additions and 117 deletions.
10 changes: 9 additions & 1 deletion docs/installation.rst
Expand Up @@ -243,7 +243,15 @@ Both methods are included in the example configs.
Installation
------------

**Sqlite users:** create a DB file in your project source.
**MySQL users:** Make sure that you create the database using the
``utf8`` charset::

CREATE DATABASE flaskbb CHARACTER SET utf8;

Even though the ``utf8mb4`` charset is prefered today
(see `this <https://dba.stackexchange.com/a/152383>`_ SO answer), we have to
create our database using the ``utf8`` charset. A good explanation about this
issue can be found `here <https://stackoverflow.com/a/31474509>`_.

For a guided install, run::

Expand Down
12 changes: 10 additions & 2 deletions flaskbb/extensions.py
Expand Up @@ -27,7 +27,7 @@
WhoosheeQuery)
from flask_wtf.csrf import CSRFProtect
from flaskbb.exceptions import AuthorizationRequired
from sqlalchemy import event
from sqlalchemy import MetaData, event
from sqlalchemy.orm import Query as SQLAQuery


Expand Down Expand Up @@ -73,7 +73,15 @@ def register_whoosheer(self, wh):
allows = Allows(throws=AuthorizationRequired)

# Database
db = SQLAlchemy()
metadata = MetaData(
naming_convention={
"ix": 'ix_%(column_0_label)s',
"uq": "uq_%(table_name)s_%(column_0_name)s",
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
"pk": "pk_%(table_name)s"
}
)
db = SQLAlchemy(metadata=metadata)

# Whooshee (Full Text Search)
whooshee = FlaskBBWhooshee()
Expand Down
89 changes: 41 additions & 48 deletions flaskbb/forum/models.py
Expand Up @@ -27,48 +27,47 @@

moderators = db.Table(
'moderators',
db.Column('user_id', db.Integer(), db.ForeignKey('users.id'),
db.Column('user_id', db.Integer(),
db.ForeignKey('users.id', ondelete="CASCADE"),
nullable=False),
db.Column('forum_id', db.Integer(),
db.ForeignKey('forums.id', use_alter=True,
name="fk_mods_forum_id"),
db.ForeignKey('forums.id', ondelete="CASCADE"),
nullable=False))


topictracker = db.Table(
'topictracker',
db.Column('user_id', db.Integer(), db.ForeignKey('users.id'),
db.Column('user_id', db.Integer(),
db.ForeignKey('users.id', ondelete="CASCADE"),
nullable=False),
db.Column('topic_id', db.Integer(),
db.ForeignKey('topics.id', use_alter=True,
name="fk_tracker_topic_id"),
db.ForeignKey('topics.id', ondelete="CASCADE"),
nullable=False))


forumgroups = db.Table(
'forumgroups',
db.Column('group_id', db.Integer(), db.ForeignKey('groups.id'),
db.Column('group_id', db.Integer(),
db.ForeignKey('groups.id', ondelete="CASCADE"),
nullable=False),
db.Column('forum_id', db.Integer(),
db.ForeignKey('forums.id', use_alter=True,
name="fk_fg_forum_id"),
db.ForeignKey('forums.id', ondelete="CASCADE"),
nullable=False))


class TopicsRead(db.Model, CRUDMixin):
__tablename__ = "topicsread"

user_id = db.Column(db.Integer, db.ForeignKey("users.id"),
user_id = db.Column(db.Integer,
db.ForeignKey("users.id", ondelete="CASCADE"),
primary_key=True)
user = db.relationship('User', uselist=False, foreign_keys=[user_id])
topic_id = db.Column(db.Integer,
db.ForeignKey("topics.id", use_alter=True,
name="fk_tr_topic_id"),
db.ForeignKey("topics.id", ondelete="CASCADE"),
primary_key=True)
topic = db.relationship('Topic', uselist=False, foreign_keys=[topic_id])
forum_id = db.Column(db.Integer,
db.ForeignKey("forums.id", use_alter=True,
name="fk_tr_forum_id"),
db.ForeignKey("forums.id", ondelete="CASCADE"),
primary_key=True)
forum = db.relationship('Forum', uselist=False, foreign_keys=[forum_id])
last_read = db.Column(UTCDateTime(timezone=True), default=time_utcnow,
Expand All @@ -78,12 +77,12 @@ class TopicsRead(db.Model, CRUDMixin):
class ForumsRead(db.Model, CRUDMixin):
__tablename__ = "forumsread"

user_id = db.Column(db.Integer, db.ForeignKey("users.id"),
user_id = db.Column(db.Integer,
db.ForeignKey("users.id", ondelete="CASCADE"),
primary_key=True)
user = db.relationship('User', uselist=False, foreign_keys=[user_id])
forum_id = db.Column(db.Integer,
db.ForeignKey("forums.id", use_alter=True,
name="fk_fr_forum_id"),
db.ForeignKey("forums.id", ondelete="CASCADE"),
primary_key=True)
forum = db.relationship('Forum', uselist=False, foreign_keys=[forum_id])
last_read = db.Column(UTCDateTime(timezone=True), default=time_utcnow,
Expand Down Expand Up @@ -149,10 +148,8 @@ class Post(HideableCRUDMixin, db.Model):

id = db.Column(db.Integer, primary_key=True)
topic_id = db.Column(db.Integer,
db.ForeignKey("topics.id",
use_alter=True,
name="fk_post_topic_id",
ondelete="CASCADE"),
db.ForeignKey("topics.id", ondelete="CASCADE",
use_alter=True),
nullable=True)
user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=True)
username = db.Column(db.String(200), nullable=False)
Expand Down Expand Up @@ -389,9 +386,7 @@ class Topic(HideableCRUDMixin, db.Model):

id = db.Column(db.Integer, primary_key=True)
forum_id = db.Column(db.Integer,
db.ForeignKey("forums.id",
use_alter=True,
name="fk_topic_forum_id"),
db.ForeignKey("forums.id", ondelete="CASCADE"),
nullable=False)
title = db.Column(db.String(255), nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=True)
Expand Down Expand Up @@ -422,7 +417,8 @@ class Topic(HideableCRUDMixin, db.Model):
# One-to-many
posts = db.relationship("Post", backref="topic", lazy="dynamic",
primaryjoin="Post.topic_id == Topic.id",
cascade="all, delete-orphan", post_update=True)
cascade="all, delete-orphan",
post_update=True)

# Properties
@property
Expand Down Expand Up @@ -667,6 +663,12 @@ def delete(self, users=None):
db.session.delete(self)
self._fix_user_post_counts(users or self.involved_users().all())
self._fix_post_counts(forum)

# forum.last_post_id shouldn't usually be none
if forum.last_post_id is None or \
self.last_post_id == forum.last_post_id:
forum.update_last_post(commit=False)

db.session.commit()
return self

Expand Down Expand Up @@ -723,8 +725,6 @@ def _remove_topic_from_forum(self):
self.forum.last_post_username = None
self.forum.last_post_created = None

TopicsRead.query.filter_by(topic_id=self.id).delete()

def _fix_user_post_counts(self, users=None):
# Update the post counts
if users:
Expand Down Expand Up @@ -789,7 +789,8 @@ class Forum(db.Model, CRUDMixin):
__tablename__ = "forums"

id = db.Column(db.Integer, primary_key=True)
category_id = db.Column(db.Integer, db.ForeignKey("categories.id"),
category_id = db.Column(db.Integer,
db.ForeignKey("categories.id", ondelete="CASCADE"),
nullable=False)
title = db.Column(db.String(255), nullable=False)
description = db.Column(db.Text, nullable=True)
Expand All @@ -803,18 +804,18 @@ class Forum(db.Model, CRUDMixin):

# One-to-one
last_post_id = db.Column(db.Integer, db.ForeignKey("posts.id"),
nullable=True)
nullable=True) # we handle this case ourselfs
last_post = db.relationship("Post", backref="last_post_forum",
uselist=False, foreign_keys=[last_post_id])

last_post_user_id = db.Column(db.Integer, db.ForeignKey("users.id"),
# set to null if the user got deleted
last_post_user_id = db.Column(db.Integer,
db.ForeignKey("users.id",
ondelete="SET NULL"),
nullable=True)

last_post_user = db.relationship(
"User",
uselist=False,
foreign_keys=[last_post_user_id]
)
last_post_user = db.relationship("User", uselist=False,
foreign_keys=[last_post_user_id])

# Not nice, but needed to improve the performance; can be set to NULL
# if the forum has no posts
Expand All @@ -824,12 +825,8 @@ class Forum(db.Model, CRUDMixin):
default=time_utcnow, nullable=True)

# One-to-many
topics = db.relationship(
"Topic",
backref="forum",
lazy="dynamic",
cascade="all, delete-orphan"
)
topics = db.relationship("Topic", backref="forum", lazy="dynamic",
cascade="all, delete-orphan")

# Many-to-many
moderators = db.relationship(
Expand Down Expand Up @@ -872,7 +869,7 @@ def __repr__(self):
"""
return "<{} {}>".format(self.__class__.__name__, self.id)

def update_last_post(self):
def update_last_post(self, commit=True):
"""Updates the last post in the forum."""
last_post = Post.query.\
filter(Post.topic_id == Topic.id,
Expand Down Expand Up @@ -900,7 +897,8 @@ def update_last_post(self):
self.last_post_username = None
self.last_post_created = None

db.session.commit()
if commit:
db.session.commit()

def update_read(self, user, forumsread, topicsread):
"""Updates the ForumsRead status for the user. In order to work
Expand Down Expand Up @@ -1021,11 +1019,6 @@ def delete(self, users=None):
db.session.delete(self)
db.session.commit()

# Delete the entries for the forum in the ForumsRead and TopicsRead
# relation
ForumsRead.query.filter_by(forum_id=self.id).delete()
TopicsRead.query.filter_by(forum_id=self.id).delete()

# Update the users post count
if users:
users_list = []
Expand Down
8 changes: 2 additions & 6 deletions flaskbb/management/models.py
Expand Up @@ -10,13 +10,10 @@
"""
import logging

from flask_wtf import FlaskForm
from flaskbb._compat import iteritems, text_type
from flaskbb._compat import iteritems
from flaskbb.extensions import cache, db
from flaskbb.utils.database import CRUDMixin
from flaskbb.utils.forms import SettingValueType, generate_settings_form
from wtforms import (BooleanField, FloatField, IntegerField, SelectField,
SelectMultipleField, TextField, validators)

logger = logging.getLogger(__name__)

Expand All @@ -41,8 +38,7 @@ class Setting(db.Model, CRUDMixin):
value = db.Column(db.PickleType, nullable=False)
settingsgroup = db.Column(db.String(255),
db.ForeignKey('settingsgroup.key',
use_alter=True,
name="fk_settingsgroup"),
ondelete="CASCADE"),
nullable=False)

# The name (displayed in the form)
Expand Down
21 changes: 14 additions & 7 deletions flaskbb/message/models.py
Expand Up @@ -23,10 +23,14 @@ class Conversation(db.Model, CRUDMixin):
__tablename__ = "conversations"

id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)
from_user_id = db.Column(db.Integer, db.ForeignKey("users.id"),
user_id = db.Column(db.Integer,
db.ForeignKey("users.id", ondelete="CASCADE"),
nullable=False)
from_user_id = db.Column(db.Integer, db.ForeignKey("users.id",
ondelete="SET NULL"),
nullable=True)
to_user_id = db.Column(db.Integer, db.ForeignKey("users.id"),
to_user_id = db.Column(db.Integer, db.ForeignKey("users.id",
ondelete="SET NULL"),
nullable=True)
shared_id = db.Column(UUIDType, nullable=False)
subject = db.Column(db.String(255), nullable=True)
Expand All @@ -41,8 +45,7 @@ class Conversation(db.Model, CRUDMixin):
messages = db.relationship(
"Message", lazy="joined", backref="conversation",
primaryjoin="Message.conversation_id == Conversation.id",
order_by="asc(Message.id)",
cascade="all, delete-orphan"
order_by="asc(Message.id)", cascade="all, delete-orphan"
)

# this is actually the users message box
Expand Down Expand Up @@ -89,11 +92,15 @@ class Message(db.Model, CRUDMixin):
__tablename__ = "messages"

id = db.Column(db.Integer, primary_key=True)
conversation_id = db.Column(db.Integer, db.ForeignKey("conversations.id"),
conversation_id = db.Column(db.Integer,
db.ForeignKey("conversations.id",
ondelete="CASCADE"),
nullable=False)

# the user who wrote the message
user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False)
user_id = db.Column(db.Integer,
db.ForeignKey("users.id", ondelete="SET NULL"),
nullable=True)
message = db.Column(db.Text, nullable=False)
date_created = db.Column(UTCDateTime(timezone=True), default=time_utcnow,
nullable=False)
Expand Down
8 changes: 5 additions & 3 deletions flaskbb/plugins/models.py
Expand Up @@ -28,7 +28,9 @@ class PluginStore(CRUDMixin, db.Model):
# Extra attributes like, validation things (min, max length...)
# For Select*Fields required: choices
extra = db.Column(db.PickleType, nullable=True)
plugin_id = db.Column(db.Integer, db.ForeignKey('plugin_registry.id'))
plugin_id = db.Column(db.Integer,
db.ForeignKey("plugin_registry.id",
ondelete="CASCADE"))

# Display stuff
name = db.Column(db.Unicode(255), nullable=False)
Expand Down Expand Up @@ -62,8 +64,8 @@ class PluginRegistry(CRUDMixin, db.Model):
values = db.relationship(
'PluginStore',
collection_class=attribute_mapped_collection('key'),
cascade='all, delete-orphan',
backref='plugin'
backref='plugin',
cascade="all, delete-orphan",
)

@property
Expand Down
9 changes: 7 additions & 2 deletions flaskbb/templates/layout.html
Expand Up @@ -97,13 +97,18 @@
<li>
<a href="{{ url_for('message.view_conversation', conversation_id=message.id) }}">
<div>
<span class="author-name">{{ message.from_user.username }}</span> <span class="pull-right text-muted">{{ message.last_message.date_created|time_since }}</span>
{% if message.from_user %}
<span class="author-name">{{ message.from_user.username }}</span>
{% else %}
{% trans %}Deleted User{% endtrans %}
{% endif %}
<span class="pull-right text-muted">{{ message.last_message.date_created|time_since }}</span>
<div class="message-subject">{{ message.subject }}</div>
</div>
</a>
</li>
{% else %}
<li><a href="#">No unread messages.</a></li>
<li><a href="#">{% trans %}No unread messages.{% endtrans %}</a></li>
{% endfor %}
<li class="divider"></li>
<li><a href="{{ url_for('message.inbox') }}"><span class="fa fa-envelope fa-fw"></span> {% trans %}Inbox{% endtrans %}</a></li>
Expand Down
4 changes: 2 additions & 2 deletions flaskbb/templates/message/conversation.html
Expand Up @@ -98,7 +98,7 @@
{% endif %}

{% else %}
<div class="author-name"><h4>{% trans %}Deleted{% endtrans %}</h4></div>
<div class="author-name"><h4>{% trans %}Deleted User{% endtrans %}</h4></div>
<div class="author-title"><h5>{% trans %}Guest{% endtrans %}</h5></div>
{% endif %}
</div>
Expand All @@ -110,7 +110,7 @@
</div>
</div>

{% if not conversation.draft %}
{% if not conversation.draft and conversation.from_user != None and conversation.to_user != None %}
{% from "macros.html" import render_quickreply, render_submit_field %}
<form class="form" action="#" method="post">
{{ form.hidden_tag() }}
Expand Down

0 comments on commit 0a0a8e6

Please sign in to comment.