models.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # **************************************************************************
  4. # Copyright © 2016 jianglin
  5. # File Name: models.py
  6. # Author: jianglin
  7. # Email: xiyang0807@gmail.com
  8. # Created: 2016-12-15 21:09:08 (CST)
  9. # Last Update:星期六 2017-4-1 20:28:26 (CST)
  10. # By:
  11. # Description:
  12. # **************************************************************************
  13. from datetime import datetime
  14. from threading import Thread
  15. from flask import current_app
  16. from flask_login import UserMixin, current_user
  17. from flask_mail import Message
  18. from itsdangerous import BadSignature, SignatureExpired, URLSafeTimedSerializer
  19. from pytz import all_timezones
  20. from sqlalchemy import event
  21. from sqlalchemy.orm import object_session
  22. from werkzeug.security import check_password_hash, generate_password_hash
  23. from flask_maple.models import ModelMixin
  24. from forums.count import Count
  25. from forums.extension import db, mail
  26. from forums.common.records import load_online_sign_users
  27. user_follower = db.Table(
  28. 'user_follower',
  29. db.Column('user_id', db.Integer, db.ForeignKey('users.id')),
  30. db.Column('follower_id', db.Integer, db.ForeignKey('users.id')))
  31. class User(db.Model, UserMixin, ModelMixin):
  32. __tablename__ = 'users'
  33. id = db.Column(db.Integer, primary_key=True)
  34. username = db.Column(db.String(49), unique=True, nullable=False)
  35. email = db.Column(db.String(81), unique=True, nullable=False)
  36. password = db.Column(db.String(81), nullable=False)
  37. is_superuser = db.Column(db.Boolean, default=False)
  38. is_confirmed = db.Column(db.Boolean, default=False)
  39. register_time = db.Column(db.DateTime, default=datetime.now())
  40. last_login = db.Column(db.DateTime, default=datetime.now())
  41. followers = db.relationship(
  42. 'User',
  43. secondary=user_follower,
  44. primaryjoin=(id == user_follower.c.user_id),
  45. secondaryjoin=(id == user_follower.c.follower_id),
  46. backref=db.backref(
  47. 'following_users', lazy='dynamic'),
  48. lazy='dynamic')
  49. def is_followed(self, user=None):
  50. if user is None:
  51. user = current_user
  52. return db.session.query(user_follower).filter(
  53. user_follower.c.user_id == self.id,
  54. user_follower.c.follower_id == user.id).exists()
  55. @property
  56. def is_online(self):
  57. setting = self.setting
  58. if setting.online_status == UserSetting.STATUS_ALLOW_ALL:
  59. return self.username in load_online_sign_users()
  60. elif setting.online_status == UserSetting.STATUS_ALLOW_AUTHENTICATED:
  61. return self.username in load_online_sign_users(
  62. ) and current_user.is_authenticated
  63. elif setting.online_status == UserSetting.STATUS_ALLOW_OWN:
  64. return current_user.id == self.id
  65. return False
  66. @property
  67. def topic_count(self):
  68. return self.topics.count()
  69. @topic_count.setter
  70. def topic_count(self, value):
  71. return Count.user_topic_count(self.id, value)
  72. @property
  73. def reply_count(self):
  74. return self.replies.count()
  75. @reply_count.setter
  76. def reply_count(self, value):
  77. return Count.user_reply_count(self.id, value)
  78. @property
  79. def message_count(self):
  80. # return self.receive_messages.filter_by(status='0').count()
  81. return Count.user_message_count(self.id)
  82. @message_count.setter
  83. def message_count(self, value):
  84. return Count.user_message_count(self.id, value)
  85. def __str__(self):
  86. return self.username
  87. def __repr__(self):
  88. return '<User %r>' % self.username
  89. def set_password(self, raw_password):
  90. self.password = generate_password_hash(raw_password)
  91. def check_password(self, raw_password):
  92. return check_password_hash(self.password, raw_password)
  93. def send_async_email(self, msg):
  94. app = current_app._get_current_object()
  95. with app.app_context():
  96. mail.send(msg)
  97. def send_email(self,
  98. subject='',
  99. recipients=None,
  100. body=None,
  101. html=None,
  102. **kwargs):
  103. if recipients is None:
  104. recipients = self.email
  105. if not isinstance(recipients, list):
  106. recipients = [recipients]
  107. msg = Message(subject=subject, recipients=recipients, html=html)
  108. thr = Thread(target=self.send_async_email, args=[msg])
  109. thr.start()
  110. @property
  111. def email_token(self):
  112. config = current_app.config
  113. secret_key = config.setdefault('SECRET_KEY')
  114. salt = config.setdefault('SECURITY_PASSWORD_SALT')
  115. serializer = URLSafeTimedSerializer(secret_key)
  116. token = serializer.dumps(self.email, salt=salt)
  117. return token
  118. @staticmethod
  119. def check_email_token(token, max_age=1800):
  120. config = current_app.config
  121. secret_key = config.setdefault('SECRET_KEY')
  122. salt = config.setdefault('SECURITY_PASSWORD_SALT')
  123. serializer = URLSafeTimedSerializer(secret_key)
  124. try:
  125. email = serializer.loads(token, salt=salt, max_age=max_age)
  126. except BadSignature:
  127. return False
  128. except SignatureExpired:
  129. return False
  130. user = User.query.filter_by(email=email).first()
  131. if user is None:
  132. return False
  133. return user
  134. @property
  135. def token(self):
  136. config = current_app.config
  137. secret_key = config.setdefault('SECRET_KEY')
  138. salt = config.setdefault('SECURITY_PASSWORD_SALT')
  139. serializer = URLSafeTimedSerializer(secret_key)
  140. token = serializer.dumps(self.username, salt=salt)
  141. return token
  142. @staticmethod
  143. def check_token(token, max_age=86400):
  144. config = current_app.config
  145. secret_key = config.setdefault('SECRET_KEY')
  146. salt = config.setdefault('SECURITY_PASSWORD_SALT')
  147. serializer = URLSafeTimedSerializer(secret_key)
  148. try:
  149. username = serializer.loads(token, salt=salt, max_age=max_age)
  150. except BadSignature:
  151. return False
  152. except SignatureExpired:
  153. return False
  154. user = User.query.filter_by(username=username).first()
  155. if user is None:
  156. return False
  157. return user
  158. class UserInfo(db.Model, ModelMixin):
  159. __tablename__ = 'userinfo'
  160. id = db.Column(db.Integer, primary_key=True)
  161. avatar = db.Column(db.String(128))
  162. school = db.Column(db.String(128), nullable=True)
  163. word = db.Column(db.Text, nullable=True)
  164. introduce = db.Column(db.Text, nullable=True)
  165. user_id = db.Column(
  166. db.Integer, db.ForeignKey(
  167. 'users.id', ondelete="CASCADE"))
  168. user = db.relationship(
  169. User,
  170. backref=db.backref(
  171. "info", uselist=False, cascade='all,delete', lazy='joined'),
  172. uselist=False,
  173. lazy='joined')
  174. def __repr__(self):
  175. return "<UserInfo %r>" % str(self.id)
  176. def __str__(self):
  177. return "%s's info" % self.user_id
  178. class UserSetting(db.Model, ModelMixin):
  179. STATUS_ALLOW_ALL = '0'
  180. STATUS_ALLOW_AUTHENTICATED = '1'
  181. STATUS_ALLOW_OWN = '2'
  182. STATUS = (('0', 'ALLOW ALL USER'), ('1', 'ALLOW AUTHENTICATED USER'),
  183. ('2', 'ALLOW OWN'))
  184. LOCALE_CHINESE = 'zh'
  185. LOCALE_ENGLISH = 'en'
  186. LOCALE = (('zh', 'Chinese'), ('en', 'English'))
  187. TIMEZONE_UTC = 'UTC'
  188. TIMEZONE = [(i, i) for i in all_timezones]
  189. __tablename__ = 'usersetting'
  190. id = db.Column(db.Integer, primary_key=True)
  191. online_status = db.Column(
  192. db.String(10), nullable=False, default=STATUS_ALLOW_ALL)
  193. topic_list = db.Column(
  194. db.String(10), nullable=False, default=STATUS_ALLOW_ALL)
  195. rep_list = db.Column(
  196. db.String(10), nullable=False, default=STATUS_ALLOW_ALL)
  197. ntb_list = db.Column(
  198. db.String(10), nullable=False, default=STATUS_ALLOW_OWN)
  199. collect_list = db.Column(
  200. db.String(10), nullable=False, default=STATUS_ALLOW_AUTHENTICATED)
  201. locale = db.Column(db.String(32), nullable=False, default=LOCALE_CHINESE)
  202. timezone = db.Column(db.String(32), nullable=False, default=TIMEZONE_UTC)
  203. user_id = db.Column(
  204. db.Integer, db.ForeignKey(
  205. 'users.id', ondelete="CASCADE"))
  206. user = db.relationship(
  207. User,
  208. backref=db.backref(
  209. "setting", uselist=False, cascade='all,delete', lazy='joined'),
  210. uselist=False,
  211. lazy='joined')
  212. def __repr__(self):
  213. return "<UserSetting %r>" % str(self.id)
  214. def __str__(self):
  215. return "%s's setting" % self.user_id
  216. @event.listens_for(User, 'before_insert')
  217. def add_info(mapper, connection, target):
  218. info = UserInfo()
  219. setting = UserSetting()
  220. object_session(target).add(info)
  221. object_session(target).add(setting)
  222. target.info = info
  223. target.setting = setting