models.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. # -*- coding: utf-8 -*-
  2. """
  3. flaskbb.forum.models
  4. ~~~~~~~~~~~~~~~~~~~~
  5. It provides the models for the forum
  6. :copyright: (c) 2013 by the FlaskBB Team.
  7. :license: BSD, see LICENSE for more details.
  8. """
  9. from datetime import datetime
  10. from flaskbb.extensions import db
  11. from flaskbb.helpers import DenormalizedText
  12. from helpers import get_forum_ids
  13. class Post(db.Model):
  14. __tablename__ = "posts"
  15. id = db.Column(db.Integer, primary_key=True)
  16. topic_id = db.Column(db.Integer, db.ForeignKey("topics.id", use_alter=True,
  17. name="fk_topic_id",
  18. ondelete="CASCADE"))
  19. user_id = db.Column(db.Integer, db.ForeignKey("users.id"))
  20. content = db.Column(db.Text)
  21. date_created = db.Column(db.DateTime, default=datetime.utcnow())
  22. date_modified = db.Column(db.DateTime)
  23. def save(self, user=None, topic=None):
  24. # update/edit the post
  25. if self.id:
  26. db.session.add(self)
  27. db.session.commit()
  28. return self
  29. # Adding a new post
  30. if user and topic:
  31. self.user_id = user.id
  32. self.topic_id = topic.id
  33. self.date_created = datetime.utcnow()
  34. # This needs to be done before I update the last_post_id.
  35. db.session.add(self)
  36. db.session.commit()
  37. # Now lets update the last post id
  38. # TODO: Invalidate relevant caches.
  39. #topic.last_post_id = self.id
  40. #topic.forum.last_post_id = self.id
  41. # Update the post counts
  42. # TODO: Invalidate relevant caches.
  43. #user.post_count += 1
  44. #topic.post_count += 1
  45. #topic.forum.post_count += 1
  46. # And commit it!
  47. db.session.add(topic)
  48. db.session.commit()
  49. return self
  50. def delete(self):
  51. # This will delete the whole topic
  52. if self.topic.first_post_id == self.id:
  53. self.topic.delete()
  54. return self
  55. # Delete the last post
  56. if self.topic.last_post.id == self.id:
  57. # Now the second last post will be the last post
  58. # TODO: Invalidate relevant caches
  59. #self.topic.last_post_id = self.topic.second_last_post
  60. #self.topic.forum.last_post_id = self.topic.second_last_post
  61. db.session.commit()
  62. # Update the post counts
  63. # TODO: Invalidate relevant caches
  64. #self.user.post_count -= 1
  65. #self.topic.post_count -= 1
  66. #self.topic.forum.post_count -= 1
  67. # Is there a better way to do this?
  68. db.session.delete(self)
  69. db.session.commit()
  70. return self
  71. class Topic(db.Model):
  72. __tablename__ = "topics"
  73. id = db.Column(db.Integer, primary_key=True)
  74. forum_id = db.Column(db.Integer, db.ForeignKey("forums.id", use_alter=True,
  75. name="fk_forum_id"))
  76. title = db.Column(db.String)
  77. user_id = db.Column(db.Integer, db.ForeignKey("users.id"))
  78. date_created = db.Column(db.DateTime, default=datetime.utcnow())
  79. locked = db.Column(db.Boolean, default=False)
  80. important = db.Column(db.Boolean, default=False)
  81. views = db.Column(db.Integer, default=0)
  82. # One-to-many
  83. posts = db.relationship("Post", backref="topic", lazy="joined",
  84. primaryjoin="Post.topic_id == Topic.id",
  85. cascade="all, delete-orphan", post_update=True)
  86. def __init__(self, title=None):
  87. if title:
  88. self.title = title
  89. @property
  90. def post_count(self):
  91. """
  92. Returns the amount of posts within the current topic.
  93. """
  94. # TODO: Cache
  95. return Post.query.\
  96. filter(Post.topic_id == self.id).\
  97. count()
  98. @property
  99. def first_post(self):
  100. """
  101. Returns the first post within the current topic.
  102. """
  103. # TODO: Cache this method.
  104. return Post.query.\
  105. filter(Post.topic_id == self.id).\
  106. order_by(Post.date_created.asc()).\
  107. first()
  108. @property
  109. def last_post(self):
  110. """
  111. Returns the latest post within the current topic.
  112. """
  113. # TODO: Cache this method.
  114. return Post.query.\
  115. filter(Post.topic_id == self.id).\
  116. order_by(Post.date_created.desc()).\
  117. first()
  118. @property
  119. def second_last_post(self):
  120. """
  121. Returns the second last post.
  122. """
  123. return self.posts[-2].id
  124. def save(self, user=None, forum=None, post=None):
  125. # Updates the topic - Because the thread title (by intention)
  126. # isn't change able, so we are just going to update the post content
  127. if self.id:
  128. db.session.add(self)
  129. db.session.commit()
  130. return self
  131. # Set the forum and user id
  132. self.forum_id = forum.id
  133. self.user_id = user.id
  134. # Update the topic count
  135. forum.topic_count += 1
  136. # Insert and commit the topic
  137. db.session.add(self)
  138. db.session.commit()
  139. # Create the topic post
  140. post.save(user, self)
  141. # Update the first post id
  142. # TODO: Invalidate first_post cache
  143. #self.first_post_id = post.id
  144. db.session.commit()
  145. return self
  146. def delete(self, users=None):
  147. # TODO: Invalidate forum last post caches
  148. #topic = Topic.query.filter_by(forum_id=self.forum_id).\
  149. # order_by(Topic.last_post_id.desc())
  150. #
  151. #if topic and topic[0].id == self.id:
  152. # try:
  153. # self.forum.last_post_id = topic[1].last_post_id
  154. #Catch an IndexError when you delete the last topic in the forum
  155. #except IndexError:
  156. # self.forum.last_post_id = 0
  157. # These things needs to be stored in a variable before they are deleted
  158. forum = self.forum
  159. # Delete the topic
  160. db.session.delete(self)
  161. db.session.commit()
  162. # Update the post counts
  163. # TODO: Invalidate relevant caches
  164. #if users:
  165. # If someone knows a better method for this,
  166. # feel free to improve it :)
  167. #for user in users:
  168. # user.post_count = Post.query.filter_by(user_id=user.id).count()
  169. # db.session.commit()
  170. #forum.topic_count = Topic.query.filter_by(
  171. # forum_id=self.forum_id).count()
  172. #
  173. #forum.post_count = Post.query.filter(
  174. # Post.topic_id == Topic.id,
  175. # Topic.forum_id == self.forum_id).count()
  176. db.session.commit()
  177. return self
  178. class Forum(db.Model):
  179. __tablename__ = "forums"
  180. id = db.Column(db.Integer, primary_key=True)
  181. title = db.Column(db.String)
  182. description = db.Column(db.String)
  183. position = db.Column(db.Integer, default=0)
  184. is_category = db.Column(db.Boolean, default=False)
  185. parent_id = db.Column(db.Integer, db.ForeignKey("forums.id"))
  186. # One-to-many
  187. topics = db.relationship("Topic", backref="forum", lazy="joined")
  188. children = db.relationship("Forum", backref=db.backref("parent", remote_side=[id]))
  189. moderators = db.Column(DenormalizedText) # TODO: No forum_moderators column?
  190. @property
  191. def post_count(self, include_children=True):
  192. """
  193. Returns the amount of posts within the current forum or it's children.
  194. Children can be excluded by setting the second parameter to 'false'.
  195. """
  196. # TODO: Cache
  197. if include_children:
  198. return Post.query.\
  199. filter(Post.topic_id == Topic.id). \
  200. filter(Topic.forum_id.in_(get_forum_ids(self))). \
  201. count()
  202. else:
  203. return Post.query.\
  204. filter(Post.topic_id == Topic.id).\
  205. filter(Topic.forum_id == self.id).\
  206. count()
  207. @property
  208. def topic_count(self, include_children=True):
  209. """
  210. Returns the amount of topics within the current forum or it's children.
  211. Children can be excluded by setting the second parameter to 'false'.
  212. """
  213. # TODO: Cache
  214. if include_children:
  215. return Topic.query.\
  216. filter(Topic.forum_id.in_(get_forum_ids(self))). \
  217. count()
  218. else:
  219. return Topic.query.\
  220. filter(Topic.forum_id == self.id).\
  221. count()
  222. @property
  223. def last_post(self, include_children=True):
  224. """
  225. Returns the latest post within the current forum or it's children.
  226. Children can be excluded by setting the second parameter to 'false'.
  227. """
  228. # TODO: Cache this method.
  229. if include_children:
  230. return Post.query.\
  231. filter(Post.topic_id == Topic.id). \
  232. filter(Topic.forum_id.in_(get_forum_ids(self))). \
  233. order_by(Post.date_created.desc()). \
  234. first()
  235. else:
  236. return Post.query.\
  237. filter(Post.topic_id == Topic.id).\
  238. filter(Topic.forum_id == self.id).\
  239. order_by(Post.date_created.desc()).\
  240. first()
  241. def add_moderator(self, user_id):
  242. self.moderators.add(user_id)
  243. def remove_moderator(self, user_id):
  244. self.moderators.remove(user_id)
  245. def save(self):
  246. db.session.add(self)
  247. db.session.commit()
  248. return self
  249. def delete(self):
  250. db.session.delete(self)
  251. db.session.commit()
  252. return self
  253. @classmethod
  254. def get_categories(cls):
  255. return cls.query.filter(cls.is_category)
  256. def get_breadcrumbs(self):
  257. breadcrumbs = []
  258. parent = self.parent
  259. while parent is not None:
  260. breadcrumbs.append(parent)
  261. parent = parent.parent
  262. breadcrumbs.reverse()
  263. return breadcrumbs
  264. """
  265. A topic can be tracked by many users
  266. and a user can track many topics.. so it's a many-to-many relationship
  267. """
  268. topictracker = db.Table('topictracker',
  269. db.Column('user_id', db.Integer(), db.ForeignKey('users.id')),
  270. db.Column('topic_id', db.Integer(), db.ForeignKey('topics.id')))
  271. class Tracking(db.Model):
  272. """
  273. This model tracks the unread/read posts
  274. Note: This functionality isn't implemented yet, but this will be the next
  275. feature after the TopicTracker
  276. """
  277. __tablename__ = "tracking"
  278. id = db.Column(db.Integer, primary_key=True)
  279. user_id = db.Column(db.Integer, db.ForeignKey("users.id"))
  280. topic_id = db.Column(db.Integer, db.ForeignKey("topics.id"))
  281. last_read = db.Column(db.DateTime, default=datetime.utcnow())
  282. def save(self):
  283. db.session.add(self)
  284. db.session.commit()
  285. return self
  286. def delete(self):
  287. db.session.delete(self)
  288. db.session.commit()
  289. return self