-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmodels.py
More file actions
145 lines (116 loc) · 4.65 KB
/
models.py
File metadata and controls
145 lines (116 loc) · 4.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
from datetime import datetime
from hashlib import md5
from time import time
import jwt
from flask import current_app
from flask_login import UserMixin
from werkzeug.security import check_password_hash, generate_password_hash
from app import db, login
from app.search import add_to_index, query_index, remove_from_index
class SearchableMixin:
@classmethod
def search(cls, expression, page, per_page):
ids, total = query_index(cls.__tablename__, expression, page, per_page)
if total == 0:
return cls.query.filter_by(id=0), 0
when = [(ids[i], i) for i in range(len(ids))]
return (
cls.query.filter(cls.id.in_(ids)).order_by(db.case(*when, value=cls.id)),
total,
)
@classmethod
def before_commit(cls, session):
session._changes = {
"add": list(session.new),
"update": list(session.dirty),
"delete": list(session.deleted),
}
@classmethod
def after_commit(cls, session):
for obj in session._changes["add"]:
if isinstance(obj, SearchableMixin):
add_to_index(obj.__tablename__, obj)
for obj in session._changes["update"]:
if isinstance(obj, SearchableMixin):
add_to_index(obj.__tablename__, obj)
for obj in session._changes["delete"]:
if isinstance(obj, SearchableMixin):
remove_from_index(obj.__tablename__, obj)
session._changes = None
@classmethod
def reindex(cls):
for obj in cls.query:
add_to_index(cls.__tablename__, obj)
followers = db.Table(
"followers",
db.Column("follower_id", db.Integer, db.ForeignKey("user.id")),
db.Column("followed_id", db.Integer, db.ForeignKey("user.id")),
)
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
posts = db.relationship("Post", backref="author", lazy="dynamic")
about_me = db.Column(db.String(140))
last_seen = db.Column(db.DateTime, default=datetime.utcnow)
followed = db.relationship(
"User",
secondary=followers,
primaryjoin=(followers.c.follower_id == id),
secondaryjoin=(followers.c.followed_id == id),
backref=db.backref("followers", lazy="dynamic"),
lazy="dynamic",
)
def __repr__(self):
return f"<User {self.username}>"
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
def avatar(self, size):
digest = md5(self.email.lower().encode("utf-8")).hexdigest()
return f"https://www.gravatar.com/avatar/{digest}?d=identicon&s={size}"
def follow(self, user):
if not self.is_following(user):
self.followed.append(user)
def unfollow(self, user):
if self.is_following(user):
self.followed.remove(user)
def is_following(self, user):
return self.followed.filter(followers.c.followed_id == user.id).count() > 0
def followed_posts(self):
followed = Post.query.join(
followers, (followers.c.followed_id == Post.user_id)
).filter(followers.c.follower_id == self.id)
own = Post.query.filter_by(user_id=self.id)
return followed.union(own).order_by(Post.timestamp.desc())
def get_reset_password_token(self, expires_in=600):
return jwt.encode(
{"reset_password": self.id, "exp": time() + expires_in},
current_app.config["SECRET_KEY"],
algorithm="HS256",
)
@staticmethod
def verify_reset_password_token(token):
try:
id = jwt.decode(
token, current_app.config["SECRET_KEY"], algorithms=["HS256"]
)["reset_password"]
except: # noqa: E722
return
return User.query.get(id)
@login.user_loader
def load_user(id):
return User.query.get(int(id))
class Post(SearchableMixin, db.Model):
__searchable__ = ["body"]
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
user_id = db.Column(db.Integer, db.ForeignKey("user.id"))
language = db.Column(db.String(5))
def __repr__(self):
return f"<Post {self.body}>"
db.event.listen(db.session, "before_commit", SearchableMixin.before_commit)
db.event.listen(db.session, "after_commit", SearchableMixin.after_commit)