4 数据库和用户信息

数据库和用户中心

数据库

users 表

posts 表

用户注册 和登陆

保存到 user 表

增加 models

models/account.py
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 '<User(#{}:{})>'.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 ''
        
        

增加 utils 辅助函数

utils/account.py

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'}

完成注册 /signup

auth.py


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'})

            

更新 /login


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='用户名或密码错误')


.scalar()

如果查询到很多结果,抛出异常。
如果只有一个结果,返回它,

如果没有结果,返回None

图片

保存到 post 表

增加 models

models/account.py
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

增加 utils 辅助函数

utils/account.py


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

更新 /upload 和辅助函数

main.py

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'})

更新 /index 和 /explore

main.py

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)


index.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 %}



explore.html
{% 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 %}

post.html
{% extends 'base.html' %}

{% block title %}
    post page
{% end %}

{% block content %}
    <img src="{{ static_url(post.image_url)}}" width="560px">
{% end %}
photo.py
# 生成缩略图
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

作业

把数据保存用起来。(提交简单截图就可以了)

完整代码

app.py

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<post_id>[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()

main.py

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'})

photo.py

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

base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}base{% end %}</title>
</head>
<body>
    {% block content %}
        base
    {% end %}
</body>
</html>


index.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 %}



explore.html

{% 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 %}

post.html

{% extends 'base.html' %}

{% block title %}
    post page
{% end %}

{% block content %}
    <img src="{{ static_url(post.image_url)}}" width="560px">
{% end %}


upload.html

{% 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 %}

signup.html

{% 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 %}

login.html

{% 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="">Login</button>

        <div>
            还没有账号 需要<a href="/signup">注册</a>一个
        </div>
    </form>
</div>
{% end %}

auth.py

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'})

utils/account.py

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

models/account.py

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 '<User(#{}:{})>'.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()

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值