Flaskプロジェクトの構築とログイン、登録(メールボックス検証)の実現

13922 ワード

一、プロジェクト構造:
|--mytest
  |--app/
    |--init.py--   
    |--models.py--    
  	|--auth/
  	   |--init.py--   
  	   |--forms.py--auth    
  	   |--views.py--auth    
  	|--main/
  	   |--init.py--main    
  	   |--views.py--    
  	|--static/--    
  	|--templates/--    
  |--venv--    
  |--config.py--    
  |--emailTest.py--      
  |--etc.py--    
  |--manage.py--    
  |--requirements.txt--     

1、プロファイル:
#-- coding:utf-8 --

class Config(object):
    '''
        1     
        2        
    '''
    SECRET_KEY = "Ay98Cct2oNSlnHDdTl8"
    SQLALCHEMY_COMMIT_ON_TEARDOWN = True
    # SQLALCHEMY_TRACK_MODIFICATIONS = True
    @staticmethod
    def init_app(app):
        pass
    
    
class LastConfig(Config):
    '''
          
             
    '''
    DEBUG = True
    #SQLALCHEMY_ECHO = True
    #     、  username         ,password      
    SQLALCHEMY_DATABASE_URI = "mysql+pymysql://username:password@localhost:3306/test_three"
config = {'default':LastConfig, 'test':TestConfig}

2、起動項目
# -- coding:utf-8 --
from app import create_app, db
from flask_script import Manager, Shell
from etc import default

#  app
app = create_app(default)
#   Manager  
manager = Manager(app)
def make_shell_context():
    return dict(app=app, db=db)

#    
manager.add_command("shell", Shell(make_context=make_shell_context))

#    
if name == "main":
    manager.run()

3、三方ライブラリ集合requirements.txt
alembic==0.8.8
blinker==1.4
click==6.6
dominate==2.2.1
Flask==0.11.1
Flask-Bootstrap==3.3.7.0
Flask-Mail==0.9.1
Flask-Migrate==2.0.0
Flask-Moment==0.5.1
Flask-Script==2.0.5
Flask-SQLAlchemy==2.1
Flask-WTF==0.12
itsdangerous==0.24
Jinja2==2.8
Mako==1.0.4
MarkupSafe==0.23
python-editor==1.0.1
SQLAlchemy==1.0.15
visitor==0.1.3
Werkzeug==0.11.11
WTForms==2.1
Flask-Login==0.3.1 

ワンタッチでプラグインライブラリをインストールするには:
pip install -r requirements.txt

4、メールボックスプロファイルの送信
# -- coding:utf-8 --

from flask import Flask,render_template

from flask_mail import Mail, Message

app = Flask(name)

	'''
        1     
        2     
        3     
        4      
	'''
app.config.update(
	MAIL_SERVER='smtp.qq.com',
	MAIL_PORT='465',
	MAIL_USE_SSL=True,
	MAIL_USERNAME='QQ ',
	MAIL_PASSWORD='   '#(         )
	)
mail = Mail(app)

def send_email(to,subject,template,user,token):
	'''
		1    Message  
		2          
		3     
	'''
	msg = Message(subject, sender='     QQ    ', recipients=[to])
	msg.html = render_template(template + '.txt', user=user,token=token)
	mail.send(msg)

5、仮想環境:前述したように、cmdで仮想環境を作成するルートディレクトリを開き、コマンドvirtualenv venv(仮想環境名、カスタマイズ)を実行して作成します.
仮想環境の作成
virtualenv venv
 
python -m venv venv

6、app:つまりアプリケーション全体の核心で、私は手動で作成したフォルダで、appと命名しました.
6.1、初期ファイル:工場関数(create_app(config)を含む.その中のパラメータは配置ファイルの中の配置名、データベースのインスタンス化データベース、メールボックス、時間フォーマット、bootstrapなどである.
from flask import Flask
from flask_bootstrap import Bootstrap
from flask_mail import Mail
from flask_moment import Moment
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from config import config
#       
bootstrap = Bootstrap()
mail = Mail()
moment = Moment()
db = SQLAlchemy()

login_manager = LoginManager()
login_manager.session_protection = 'strong'
login_manager.login_view = 'auth.login'


def create_app(config_name):
    app = Flask(__name__)
    app.config.from_object(config[config_name])
    config[config_name].init_app(app)

    bootstrap.init_app(app)
    mail.init_app(app)
    moment.init_app(app)
    db.init_app(app)
    login_manager.init_app(app)
    #      ,   
    from .main import main as main_blueprint
    app.register_blueprint(main_blueprint)
    
    from .auth import auth as auth_blueprint
    app.register_blueprint(auth_blueprint, url_prefix='/auth')
    
    return app

6.2、データベースモデル(models):
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
### flask_login  UserMixin 
###USerMixin               。
from . import db, login_manager
###          login_manager  
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
##        
from flask import current_app


class Role(db.Model):
    '''

    1      、  id、   
    
    '''
    __tablename__ = 'roles'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(64), unique=True)
    users = db.relationship('User',backref='role',lazy='dynamic')
    
    def __repr__(self):
        return '' % self.name


class User(UserMixin, db.Model):
    '''

        1 User  UserMixin db.Model      
        2   users、id、email、username、role_id、password_hash(      ),confirmed(    )
        3     、    
        4             Unicode             
        5        ,            ,    None。
    
    '''
    __tablename__ = 'users'
    
    id = db.Column(db.Integer, primary_key=True)
    
    email = db.Column(db.String(64), unique=True, index=True)
       
    username = db.Column(db.String(64),unique=True, index=True)
    
    role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
    
    password_hash = db.Column(db.String(128))
    
    confirmed = db.Column(db.Boolean,default=False)


    @property
    def password(self):
        raise AttributeError('password is not a readable attribute')
    
    @password.setter
    def password(self,password):
        self.password_hash = generate_password_hash(password)
    
    def verify_password(self, password):
        return check_password_hash(self.password_hash, password)
    
    def __repr__(self):
        return '' % self.username
    
    #expiration       ,        
    
    def generate_confirmation_token(self,expiration=3600):
        '''
                 
        '''
        s = Serializer(current_app.config['SECRET_KEY'],expiration)
        return s.dumps({'confirm':self.id})
    
    def confirm(self,token):
        '''
                 
        '''
        s = Serializer(current_app.config['SECRET_KEY'])
        try:
            data=s.loads(token)
        except:
            return False
        if data.get('confirm') != self.id:
            return False
        self.confirmed = True
        db.session.add(self)
        return True 


@login_manager.user_loader

def load_user(user_id):
    return User.query.get(int(user_id))

######6.3、ユーザーファイル(auth):(初期ファイル(青写真作成)、formsファイル(フォーム作成)、viewsファイル(ビュー関数)を含む).
6.3.1、ユーザー初期化(青写真作成):
from flask import Blueprint
auth = Blueprint('auth',__name__)
from . import views

6.3.2、formsファイル(フォーム作成):
from flask_wtf import Form
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import Required, Length, Email,Regexp,EqualTo
from wtforms import ValidationError
from ..models import User

フォームlogin
class LoginForm(Form):
	'''
		1     。StringField          validators              ,                。
		2          WTForms   Length() Email()    。
		3 PasswordField      type="password" < input>  。
		4 BooleanField      。
	'''
	email = StringField('Email', validators=[Required(), Length(1, 64), Email()])
	password = PasswordField('  ', validators=[Required()]) 
	remember_me = BooleanField('    ')
	submit = SubmitField('  ')

#    Registration
class RegistrationForm(Form):
	'''
		1     
		2           (    、     、    、    、    )
		3           
		4            
	'''

	email = StringField('Email',validators=[Required(),Length(1,64),Email()])
	username = StringField('   ',validators=[Required(),Length(1,64),Regexp('^[A-Za-z][A-Za-z0-9_.]*$',\
		0,'Usernames must have only letter,numbers,dots or underscores')])
	password = PasswordField('  ',validators=[Required(),EqualTo('password2',message='        .')])
	password2 = PasswordField('    ',validators=[Required()])
	submit = SubmitField('  ')
	
	def validate_email(self,field):
		if User.query.filter_by(email=field.data).first():
			raise ValidationError('Email     .')


	
	def validata_username(self,field):
		if User.query.filter_by(username=field.data).first():
			raise ValidationError('      .')

6.3.3、viewsビュー関数:
from flask import render_template, redirect, request, url_for, flash
from flask_login import login_user, logout_user, login_required 
from . import auth 
from ..models import User 
from .forms import LoginForm
from .forms import RegistrationForm
from .. import db
from emailTest import send_email
from flask.ext.login import current_user

@auth.route('/login', methods=['GET', 'POST'])
###    GET ,      ,    POST   ,      ,        。
def login():
    flash('         .')
    '''
        1      
        2         ,      
        3             email    
        4   user    ,            ,       ,  Flask_Login  login_user()  ,              
        5       flash          。
        6 login_user            ,     ‘   ’   。
        7         url        ,Flask-Login              next   ,      request.args     。          next  ,       。
    '''
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(email=form.email.data).first() 
        if user is not None and user.verify_password(form.password.data):
            login_user(user, form.remember_me.data)
            return redirect(request.args.get('next') or url_for('main.index'))
        flash('         .')
    return render_template('auth/login.html', form=form)

@auth.route('/logout') 
###    
@login_required 
###        
def logout():
    '''
       1     ,        logout_user()  ,         。 

    '''
    logout_user()
    flash('       !')
    return redirect(url_for('main.index'))

###    
@auth.route('/register',methods=['GET','POST'])
def register():
    '''
        1        
        2         ,         ,                 ,       
    '''
    form = RegistrationForm()
    if form.validate_on_submit():
        user = User(email=form.email.data,username=form.username.data,password=form.password.data)
        db.session.add(user)
        db.session.commit()
        token = user.generate_confirmation_token()
        send_email(user.email,'      ','auth/email/confirm',user,token)
        flash('         !')
        return redirect(url_for('main.index'))
        # return redirect(url_for('auth.login'))
        ##        
    return render_template('auth/register.html',form=form)

#        
@auth.route('/confirm/')
@login_required
def confirm(token):
    '''
        1       confirmed     True,  flash           ,       
    '''
    if current_user.confirmed:
        return redirect(url_for('main.index'))
    if current_user.confirm(token):
        flash('          ,  ')
    else:
        flash('         ,    ')
    return redirect(url_for('main.index'))

@auth.before_app_request
def before_request():
    '''
                  ,         ,          
    '''
    if current_user.is_authenticated \
            and not current_user.confirmed \
            and request.endpoint[:5] != 'auth.'\
            and request.endpoint != 'static':
        return redirect(url_for('auth.unconfirmed'))

#      
@auth.route('/unconfirmed')
def unconfirmed():
    '''
              confirmed     True,   False(0),            
    '''
    if current_user.is_anonymous or current_user.confirmed:
        return redirect(url_for('main.index'))
    return render_template('auth/unconfirmed.html')

#          
@auth.route('/confirm')
@login_required
def resend_confirmation():
    token = current_user.generate_confirmation_token()
    send_email(current_user.email, '      ',
               'auth/email/confirm', current_user, token)
    flash('             ,    .')
    return redirect(url_for('main.index'))

6.4、mainファイル:(初期ファイルも青写真作成)、viewsビュー関数.
6.4.1、初期関数:
from flask import Blueprint
main = Blueprint('main', __name__)
from .views import *

6.4.2、ビュー関数:
from . import main
from .. import auth
from flask import render_template, request
from app import db
from sqlalchemy import or_
from flask import redirect, url_for

@main.route('/')
def index():
    # return render_template('index.html')
     return redirect(url_for('auth.login'))

@main.route('/index')
def index1():
    return render_template('index.html')

7.静的スタイルファイル:(主にスタイル(css、jsなど)を含む)
8.静的テンプレートファイル:(主にHTMLテンプレート)
最後に、青写真、ビュー関数に基づいて、各モジュールとテンプレートを並べて、クリック登録を実現することができます.フォームに追加された資料をデータベースに書き込み、同時にメールボックス検証を送信します(初期検証フィールドはデータベースのデフォルト値が0(Flase)、ユーザーが自分のメールボックスに送信した検証リンクをクリックし、データのフィールドを1(True)に変更します.あ、秘密暗号化もあります.flaskには暗号化データに便利なモジュールがあります.itsdangerousパッケージのT i m e d J S S S o n W e b SignatureSerializerはフィールドを暗号解読することができます.アカウント、ユーザー名が使用されたかどうかは、フォームに入力された内容をデータライブラリのフィールドと最も一致させ、存在するかどうかを確認します.