users 表
posts 表
保存到 user 表
class Users(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(50), unique=True, nullable=False)
password = Column(String(50), nullable=False)
created = Column(DateTime, default=datetime.now)
email = Column(String(50))
last_login = Column(DateTime)
def __repr__(self):
return '' .format(self.id, self.name)
# 判断用户是否存在于数据库中
@classmethod
def is_exists(cls, username):
return session.query(exists().where(Users.name == username)).scalar()
# 增加用户到数据库中
@classmethod
def add_user(cls, username, password, email=''):
user = Users(name=username, password=password, email=email, last_login=datetime.now())
session.add(user)
session.commit()
# 查询用户username对应的密码
@classmethod
def get_passwd(cls, username):
user = session.query(Users).filter_by(name=username).first()
# 如果用户存在 返回密码
if user:
return user.password
# 用户不存在 返回空
else:
return ''
def hashed(passwd):
return hashlib.md5(passwd.encode('utf8')).hexdigest()
def authenticate(username, password):
"""
登录认证
:param username:
:param password:
:return:
"""
if username and password:
# 获取数据库中username对应的密码
user_passwd = Users.get_passwd(username)
# 如果用户存在 密码匹配
if user_passwd and user_passwd == hashed(password):
return True
return False
def save_last_login(username):
"""
# 保存用户username最后登录时间
:param username:
:return:
"""
t = datetime.now()
print("user {} login at {}".format(username, t))
session.query(Users).filter_by(name=username).update({Users.last_login: t})
session.commit()
def register(username, password, email):
"""
注册
:param username:
:param password:
:param email:
:return:
"""
# 查看用户是否存在于数据库中
if Users.is_exists(username):
return {'msg': '用户已存在'}
hash_passwd = hashed(password)
# 添加用户到数据库
Users.add_user(username, hash_passwd, email)
return {'msg': 'ok'}
class SignupHandler(AuthBaseHandler):
"""
注册
"""
def get(self, *args, **kwargs):
self.render('signup.html', msg='')
def post(self, *args, **kwargs):
username = self.get_argument('username', 'no')
email = self.get_argument('email', 'no')
password1 = self.get_argument('password1', 'no')
password2 = self.get_argument('password2', 'no')
if username and password1 and password2:
if password1 != password2:
self.render('signup.html', msg='两次输入的密码不一致')
else:
ret = register(username, password2, email) # 注册函数
# 注册成功
if ret['msg'] == 'ok':
self.session.set('tudo_user_info', username) # 注册成功后直接设置session(自动登录)
self.redirect('/') # 注册成功后自动访问首页
else:
self.render('signup.html', msg=ret['msg'])
else:
self.render('signup.html', msg={'sign failed'})
class LoginHandler(AuthBaseHandler):
"""
登录
"""
def get(self, *args, **kwargs):
if self.current_user: # 如果已经登录 访问/login自动跳转到/
self.redirect('/')
next = self.get_argument('next', '/') # 注意是/ 没有next (从logout跳转过来)就跳转到首页
self.render('login.html', nextname=next, error=None)
def post(self, *args, **kwargs):
username = self.get_argument('username')
password = self.get_argument('password')
next = self.get_argument('next', '/')
passed = authenticate(username, password) # 登录认证
if passed:
self.session.set('tudo_user_info', username)
# 保存最后登录时间
save_last_login(username)
self.redirect(next)
else:
self.render('login.html', nextname=next, error='用户名或密码错误')
如果查询到很多结果,抛出异常。
如果只有一个结果,返回它,
如果没有结果,返回None
保存到 post 表
class Posts(Base):
__tablename__ = 'posts'
id = Column(Integer, primary_key=True, autoincrement=True)
image_url = Column(String(50))
thumb_url = Column(String(50))
user_id = Column(Integer, ForeignKey('users.id'))
# Post 有user属性 存放Users对象 , Users有posts属性 存放Posts对象(多对一)
user = relationship('Users', backref='posts', uselist=False, cascade='all')
class Posts(Base):
"""
用户图片信息
"""
__tablename__ = 'posts'
id = Column(Integer, primary_key=True, autoincrement=True)
image_url = Column(String(50)) # 大图路径
thumb_url = Column(String(50)) # 缩略图路径
user_id = Column(Integer, ForeignKey('users.id'))
# Post 有user属性 存放Users对象 , Users有posts属性 存放Posts对象
user = relationship('Users', backref='posts', cascade='all')
# 保存用户上传的图片信息 图片和特定的用户建立关系(这张图片是由这个用户传上来的)
@classmethod
def add_post_for(cls, username, image_url, thumb_url):
user = session.query(Users).filter_by(name=username).first()
post = Posts(image_url=image_url, thumb_url=thumb_url, user=user)
session.add(post)
session.commit()
alembic revision --autogenerate -m 'add table for posts'
alembic upgrade head
def get_post_for(username):
"""
获取用户上传的图片信息
:param username:
:return:
"""
user = session.query(Users).filter_by(name=username).first()
if user:
return user.posts
else:
return []
def get_post(post_id):
"""
获取用户的特定图片
:param post_id:
:return:
"""
post = session.query(Posts).filter_by(id=post_id).first()
return post
class UploadHandler(AuthBaseHandler):
"""
接受图片上传
"""
@tornado.web.authenticated
def get(self, *args, **kwargs):
self.render('upload.html')
def post(self, *args, **kwargs):
# 提取表单中‘name’为‘newimg’的文件元数据 获取上传文件信息
img_files = self.request.files.get('newimg')
if img_files:
for img in img_files:
# img_file['filename']获取文件的名称 有些文件需要以二进制的形式存储
image_url = 'uploads/{}'.format(img['filename'])
# ./ static / uploads / thumbs / 701728.jpg
save_to = './static/{}'.format(image_url)
with open(save_to, 'wb') as f:
f.write(img['body']) # img_file['body']获取文件的内容
# 生成缩略图 ./ static / uploads /thumbs/ 701728_200x200.jpg
save_thumb_to = photo.make_thumbs(save_to)
thumb_url = os.path.relpath(save_thumb_to, 'static')
# 保存图片的地址 把url存到数据库
Posts.add_post_for(self.current_user, image_url, thumb_url)
self.write({'msg': 'got file: {}'.format(img_files[0]['filename'])})
else:
self.write({'msg': 'empty form data'})
class IndexHandler(AuthBaseHandler):
"""
首页
"""
@tornado.web.authenticated
def get(self, *args, **kwargs):
post_list = get_post_for(self.current_user)
self.render('index.html', post_list=post_list)
class ExploreHandler(AuthBaseHandler):
"""
发现页
"""
@tornado.web.authenticated
def get(self, *args, **kwargs):
post_list = get_post_for(self.current_user)
self.render('explore.html', post_list=post_list)
class PostHandler(AuthBaseHandler):
"""
详情页
"""
@tornado.web.authenticated
def get(self, post_id):
post = get_post(post_id)
if not post:
self.write('post id is not exists')
else:
self.render('post.html', post=post)
{% extends 'base.html' %}
{% block title %}
index page
{% end %}
{% block content %}
<p><a href="/logout">登出a>p>
{% for post in post_list %}
<img src="{{ static_url(post.image_url) }}" width="666">
{% end %}
{% end %}
{% extends 'base.html' %}
{% block title %}
explore page
{% end %}
{% block content %}
<p><a href="/logout">登出a>p>
{% for post in post_list %}
<img src="{{ static_url(post.image_url) }}" width="200px">
{% end %}
{% end %}
{% extends 'base.html' %}
{% block title %}
post page
{% end %}
{% block content %}
<img src="{{ static_url(post.image_url)}}" width="560px">
{% end %}
# 生成缩略图
def make_thumb(path):
print(path) # ./static/uploads/1172020.jpg
# basename:截取path中的去目录部分的最后的文件或路径名
# dirname:截取path中的目录路径
dirname = os.path.dirname(path) # static/uploads
file, ext = os.path.splitext(os.path.basename(path)) # file=1172020 ext=.jpg
im = Image.open(path) # 打开./static/uploads/1172020.jpg图片
size = (200, 200)
im.thumbnail(size) # 按长200高200缩放
# *size = 200,200 解包
save_thumb_to = os.path.join(dirname, 'thumbs', '{}_{}x{}.jpg'.format(file, *size))
# 保存./static/uploads/thumbs/1172020_200x200.jpg
im.save(save_thumb_to, "JPEG")
return save_thumb_to
把数据保存用起来。(提交简单截图就可以了)
import tornado.web
import tornado.options
import tornado.ioloop
from tornado.options import define, options
from handlers import main,auth
define(name='port', default='8000', type=int, help='run port')
class Application(tornado.web.Application):
def __init__(self):
handlers = [
(r'/', main.IndexHandler),
(r'/explore', main.ExploreHandler),
(r'/post/(?P[0-9]+)' , main.PostHandler),
(r'/upload', main.UploadHandler),
(r'/login', auth.LoginHandler),
(r'/logout', auth.LogoutHandler),
(r'/signup', auth.SignupHandler),
]
settings = dict(
debug=True,
template_path='templates',
static_path='static',
login_url='/login',
cookie_secret='bZJc2sWbQLKos6GkHn/VB9oXwQt8S0R0kRvJ5/xJ89E=',
pycket={
'engine': 'redis',
'storage': {
'host': 'localhost',
'port': 6379,
# 'password': '',
'db_sessions': 5, # redis db index
'db_notifications': 11,
'max_connections': 2 ** 30,
},
'cookies': {
'expires_days': 30,
},
}
)
super(Application, self).__init__(handlers, **settings)
application = Application()
if __name__ == '__main__':
tornado.options.parse_command_line()
application.listen(options.port)
print("Server start on port {}".format(str(options.port)))
tornado.ioloop.IOLoop.current().start()
import tornado.web
from utils import photo
from models.account import Posts
from pycket.session import SessionMixin
from utils.account import get_post_for,get_post
import os
class AuthBaseHandler(tornado.web.RequestHandler, SessionMixin):
def get_current_user(self):
return self.session.get('tudo_user_info')
class IndexHandler(AuthBaseHandler):
"""
首页
"""
@tornado.web.authenticated
def get(self, *args, **kwargs):
post_list = get_post_for(self.current_user)
self.render('index.html', post_list=post_list)
class ExploreHandler(AuthBaseHandler):
"""
发现页
"""
@tornado.web.authenticated
def get(self, *args, **kwargs):
post_list = get_post_for(self.current_user)
self.render('explore.html', post_list=post_list)
class PostHandler(AuthBaseHandler):
"""
详情页
"""
@tornado.web.authenticated
def get(self, post_id):
post = get_post(post_id)
if not post:
self.write('post id is not exists')
else:
self.render('post.html', post=post)
class UploadHandler(AuthBaseHandler):
"""
接受图片上传
"""
@tornado.web.authenticated
def get(self, *args, **kwargs):
self.render('upload.html')
def post(self, *args, **kwargs):
# 提取表单中‘name’为‘newimg’的文件元数据 获取上传文件信息
img_files = self.request.files.get('newimg')
if img_files:
for img in img_files:
# img_file['filename']获取文件的名称 有些文件需要以二进制的形式存储
image_url = 'uploads/{}'.format(img['filename'])
# ./ static / uploads / thumbs / 701728.jpg
save_to = './static/{}'.format(image_url)
with open(save_to, 'wb') as f:
f.write(img['body']) # img_file['body']获取文件的内容
# 生成缩略图 ./ static / uploads /thumbs/ 701728_200x200.jpg
save_thumb_to = photo.make_thumbs(save_to)
thumb_url = os.path.relpath(save_thumb_to, 'static')
# 保存图片的地址 把url存到数据库
Posts.add_post_for(self.current_user, image_url, thumb_url)
self.write({'msg': 'got file: {}'.format(img_files[0]['filename'])})
else:
self.write({'msg': 'empty form data'})
import os
import glob
from PIL import Image
def make_thumbs(path):
"""
为指定的path文件生成它所在目录的 thumbs 目录下的小图文件
:param path: ./static/uploads/701728.jpg
:return:
"""
# ./ static / uploads / 701728.jpg
im = Image.open(path)
size = (200, 200)
im.thumbnail(size)
dirname = os.path.dirname(path) # ./ static / uploads
basename = os.path.basename(path) # 701728.jpg
file, ext = os.path.splitext(basename)
# ./ static / uploads /thumbs/ 701728_200x200.jpg
save_thumbs_to = os.path.join(dirname, 'thumbs', '{}_{}x{}{}'.format(file, *size, ext))
im.save(save_thumbs_to, "JPEG")
return save_thumbs_to
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}base{% end %}title>
head>
<body>
{% block content %}
base
{% end %}
body>
html>
{% extends 'base.html' %}
{% block title %}
index page
{% end %}
{% block content %}
<p><a href="/logout">登出a>p>
{% for post in post_list %}
<img src="{{ static_url(post.image_url) }}" width="666">
{% end %}
{% end %}
{% extends 'base.html' %}
{% block title %}
explore page
{% end %}
{% block content %}
<p><a href="/logout">登出a>p>
{% for post in post_list %}
<img src="{{ static_url(post.image_url) }}" width="200px">
{% end %}
{% end %}
{% extends 'base.html' %}
{% block title %}
post page
{% end %}
{% block content %}
<img src="{{ static_url(post.image_url)}}" width="560px">
{% end %}
{% extends base.html %}
{% block title %}
upload
{% end %}
{% block content %}
<p><a href="/logout">登出a>p>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="newimg">
<input type="submit">
form>
{% end %}
{% extends 'base.html' %}
{% block title %}
signup page
{% end %}
{% block content %}
<div class="">
{% if msg %}
{{ msg }}
{% end %}
div>
<form action="/signup" enctype="multipart/form-data" method="post">
<div class="form-group">
Username
<input autofocus="" class="form-control" id="id_username" maxlength="150" name="username" type="text" required="">
div>
<div class="form-group">
Email
<input class="form-control" id="id_email" name="email" type="email" required="">
div>
<div class="form-group">
Password
<input class="form-control" id="id_password1" name="password1" type="password" required="">
div>
<div class="form-group">
Password confirmation
<input class="form-control" id="id_password2" name="password2" type="password" required="">
div>
<button class="btn btn-default">注册button>
<div class="text-center help-text">
已有账号请 <a href="/login">登录a>
div>
form>
{% end %}
{% extends 'base.html' %}
{% block title %}
login page
{% end %}
{% block content %}
<div class="">
{% if error %}
{{ error }}
{% end %}
<form action="/login?next={{ nextname }}" method="post" enctype="multipart/form-data">
<div class="form-group">
Username
<input autofocus="" class="form-control" id="id_username" maxlength="254" name="username" type="text"
required="">
div>
<div class="form-group">
Password
<input class="form-control" id="id_password" name="password" type="password" required="">
div>
<button class="">Loginbutton>
<div>
还没有账号 需要<a href="/signup">注册a>一个
div>
form>
div>
{% end %}
from .main import AuthBaseHandler
from utils.account import authenticate, register, save_last_login
from models.account import Users, session
import tornado.web
class LoginHandler(AuthBaseHandler):
"""
登录
"""
def get(self, *args, **kwargs):
if self.current_user: # 如果已经登录 访问/login自动跳转到/
self.redirect('/')
next = self.get_argument('next', '/') # 注意是/ 没有next (从logout跳转过来)就跳转到首页
self.render('login.html', nextname=next, error=None)
def post(self, *args, **kwargs):
username = self.get_argument('username')
password = self.get_argument('password')
next = self.get_argument('next', '/')
passed = authenticate(username, password) # 登录认证
if passed:
self.session.set('tudo_user_info', username)
# 保存最后登录时间
save_last_login(username)
self.redirect(next)
else:
self.render('login.html', nextname=next, error='用户名或密码错误')
class LogoutHandler(AuthBaseHandler):
"""
登出 注销用户
"""
@tornado.web.authenticated
def get(self, *args, **kwargs):
self.session.set('tudo_user_info', '')
self.redirect('/login') # 直接跳转到 login 没有next 默认是/
class SignupHandler(AuthBaseHandler):
"""
注册
"""
def get(self, *args, **kwargs):
self.render('signup.html', msg='')
def post(self, *args, **kwargs):
username = self.get_argument('username', 'no')
email = self.get_argument('email', 'no')
password1 = self.get_argument('password1', 'no')
password2 = self.get_argument('password2', 'no')
if username and password1 and password2:
if password1 != password2:
self.render('signup.html', msg='两次输入的密码不一致')
else:
ret = register(username, password2, email) # 注册函数
# 注册成功
if ret['msg'] == 'ok':
self.session.set('tudo_user_info', username) # 注册成功后直接设置session(自动登录)
self.redirect('/') # 注册成功后自动访问首页
else:
self.render('signup.html', msg=ret['msg'])
else:
self.render('signup.html', msg={'sign failed'})
import hashlib
from models.account import Users, session, Posts
from datetime import datetime
def hashed(passwd):
return hashlib.md5(passwd.encode('utf8')).hexdigest()
def authenticate(username, password):
"""
登录认证
:param username:
:param password:
:return:
"""
if username and password:
# 获取数据库中username对应的密码
user_passwd = Users.get_passwd(username)
# 如果用户存在 密码匹配
if user_passwd and user_passwd == hashed(password):
return True
return False
def register(username, password, email):
"""
注册
:param username:
:param password:
:param email:
:return:
"""
# 查看用户是否存在于数据库中
if Users.is_exists(username):
return {'msg': '用户已存在'}
hash_passwd = hashed(password)
# 添加用户到数据库
Users.add_user(username, hash_passwd, email)
return {'msg': 'ok'}
def save_last_login(username):
"""
# 保存用户username最后登录时间
:param username:
:return:
"""
t = datetime.now()
print("user {} login at {}".format(username, t))
session.query(Users).filter_by(name=username).update({Users.last_login: t})
session.commit()
def get_post_for(username):
"""
获取用户上传的图片信息
:param username:
:return:
"""
user = session.query(Users).filter_by(name=username).first()
if user:
return user.posts
else:
return []
def get_post(post_id):
"""
获取用户的特定图片
:param post_id:
:return:
"""
post = session.query(Posts).filter_by(id=post_id).first()
return post
from sqlalchemy import (Column, Integer, String, DateTime, ForeignKey)
from sqlalchemy import exists
from sqlalchemy.orm import relationship
from sqlalchemy.sql import exists
from .db import Base, DBSession
from datetime import datetime
session = DBSession()
class Users(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String(50), unique=True, nullable=False)
password = Column(String(50), nullable=False)
created = Column(DateTime, default=datetime.now)
email = Column(String(50))
last_login = Column(DateTime)
def __repr__(self):
return '' .format(self.id, self.name)
# 增加用户到数据库中
@classmethod
def add_user(cls, username, password, email=''):
user = Users(name=username, password=password, email=email, last_login=datetime.now())
session.add(user)
session.commit()
# 查询用户username对应的密码
@classmethod
def get_passwd(cls, username):
user = session.query(Users).filter_by(name=username).first()
# 如果用户存在 返回密码
if user:
return user.password
# 用户不存在 返回空
else:
return ''
# 判断用户是否存在于数据库中
@classmethod
def is_exists(cls, username):
ret = session.query(exists().where(Users.name == username)).scalar()
return ret
class Posts(Base):
"""
用户图片信息
"""
__tablename__ = 'posts'
id = Column(Integer, primary_key=True, autoincrement=True)
image_url = Column(String(50)) # 大图路径
thumb_url = Column(String(50)) # 缩略图路径
user_id = Column(Integer, ForeignKey('users.id'))
# Post 有user属性 存放Users对象 , Users有posts属性 存放Posts对象(多对一)
user = relationship('Users', backref='posts', uselist=False, cascade='all')
# 保存用户上传的图片信息 图片和特定的用户建立关系(这张图片是由这个用户传上来的)
@classmethod
def add_post_for(cls, username, image_url, thumb_url):
user = session.query(Users).filter_by(name=username).first()
post = Posts(image_url=image_url, thumb_url=thumb_url, user=user)
session.add(post)
session.commit()
if __name__ == '__main__':
Base.metadata.create_all()