版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
講師:李粵平Flask課程——第六章如何使程序易于維護(hù)掌握合理地安排項(xiàng)目結(jié)構(gòu)的方法掌握使用配置文件完成應(yīng)用實(shí)例配置的方法掌握藍(lán)圖的基本使用方法掌握Flask-Login的基本使用方法學(xué)習(xí)目標(biāo)教學(xué)重點(diǎn)教學(xué)難點(diǎn)第1章的引言提到,F(xiàn)lask微框架的“微”并不是指把整個(gè)Web應(yīng)用放入一個(gè)Python文件,而是指Flask旨在保持代碼簡潔且易于擴(kuò)展。但在第3、4、5章的實(shí)例中,為方便讀者調(diào)試、理解代碼,大部分代碼都是以單文件的方式編寫的。本章將會以實(shí)用項(xiàng)目的結(jié)構(gòu)進(jìn)行介紹,并“保持代碼簡潔且易于擴(kuò)展”。教學(xué)重難點(diǎn)模塊化開發(fā)配置文件項(xiàng)目結(jié)構(gòu)Flask-Login小結(jié)管理員注冊配置文件01配置文件在前幾章的程序中,應(yīng)用初始化時(shí)都會有諸如以下的代碼。app.config['SECRET_KEY']='Chapter5'app.config['SQLALCHEMY_DATABASE_URI']='sqlite:///'+os.path.join(basedir,'data.sqlite')app.config['SQLALCHEMY_TRACK_MODIFICATIONS']=False這些代碼都是用于定義應(yīng)用實(shí)例的一些參數(shù)、屬性的,可以通過建立配置類,將這些零碎的配置項(xiàng)整合在一起,并可以通過類繼承的方式建立多套配置方案,來實(shí)現(xiàn)不同環(huán)境下所需的設(shè)定。配置文件以下是配置文件(config.py)的代碼實(shí)現(xiàn)。importosbasedir=os.path.abspath(os.path.dirname(__file__))#配置類基類,用于定義一些固定的參數(shù)classConfig:SECRET_KEY='Chapter6'SQLALCHEMY_TRACK_MODIFICATIONS=False#可在初始化時(shí)用于執(zhí)行自定義操作@staticmethoddefinit_app(app):pass@staticmethod:這是Python中的裝飾器,用于將下面的init_app方法定義為一個(gè)靜態(tài)方法,它不依賴于類的實(shí)例而可以直接調(diào)用。definit_app(app):這是一個(gè)靜態(tài)方法,接受一個(gè)參數(shù)app,代表Flask應(yīng)用程序。它通常用于在應(yīng)用程序初始化時(shí)執(zhí)行一些自定義操作。該方法被設(shè)置為pass,即不執(zhí)行任何操作。配置文件#開發(fā)環(huán)境下所使用的配置類classDevelopmentConfig(Config):DEBUG=TrueSQLALCHEMY_DATABASE_URI='sqlite:///'+os.path.join(basedir,'data-dev.sqlite')#測試環(huán)境下所使用的配置類classTestingConfig(Config):TESTING=TrueSQLALCHEMY_DATABASE_URI='sqlite:///'+os.path.join(basedir,'data-test.sqlite')#生產(chǎn)環(huán)境下所使用的配置類classProductionConfig(Config):SQLALCHEMY_DATABASE_URI='sqlite:///'+os.path.join(basedir,'data.sqlite')#configs用于映射不同環(huán)境下所使用的不同配置類configs=dict(development=DevelopmentConfig,testing=TestingConfig,production=ProductionConfig,)這里寫了三個(gè)不同環(huán)境的配置類,根據(jù)應(yīng)用程序在不同環(huán)境中的運(yùn)行情況,配置不同的參數(shù)和選項(xiàng),以確保應(yīng)用程序在各種環(huán)境下表現(xiàn)良好。配置文件setFLASK_ENV=development配置文件編寫完成之后,便是要使應(yīng)用加載相應(yīng)的配置。通常情況下,可以根據(jù)環(huán)境變量FLASK_ENV獲取到當(dāng)前使用的環(huán)境(development、testing、production),也可以使用Flask提供的助手函數(shù)get_env()獲取。環(huán)境變量FLASK_ENV可在命令提示符窗口中通過set指令進(jìn)行設(shè)置,設(shè)置方法如下所示。接著就是應(yīng)用加載配置的代碼,代碼如下。fromflaskimportFlaskfromflask.helpersimportget_envfromconfigimportconfigsapp=Flask(__name__)#加載應(yīng)用配置env=get_env()config=configs.get(env)app.config.from_object(config)config.init_app(app)配置文件在默認(rèn)情況下,如果直接處于命令提示符窗口中,app.env的值為“production”,即生產(chǎn)環(huán)境;如果通過PyCharm啟動(dòng),則app.env的值默認(rèn)為“development”,即開發(fā)環(huán)境。如果需要對應(yīng)用進(jìn)行測試,可以在PyCharm中建立一套新的運(yùn)行配置,指定FLASK_ENV的值(即app.env)為“testing”,步驟如下圖所示。配置文件配置文件配置建立完成后,選中新配置運(yùn)行,可以發(fā)現(xiàn)環(huán)境變量變成了“testing”,如下圖所示。項(xiàng)目結(jié)構(gòu)02項(xiàng)目結(jié)構(gòu)相信各位同學(xué)在之前章節(jié)的例子中,也隱隱約約發(fā)現(xiàn)了所有內(nèi)容都整合在一起的結(jié)構(gòu)是不利于維護(hù)的。Flask沒有固定的結(jié)構(gòu),程序要如何設(shè)計(jì)都取決于開發(fā)者。這節(jié)主要以基于模塊劃分的結(jié)構(gòu)進(jìn)行演示。內(nèi)容較多,如下圖所示。項(xiàng)目結(jié)構(gòu)在接下來的內(nèi)容中,將基于以上結(jié)構(gòu)來進(jìn)行開發(fā)。由于文件數(shù)量較多,過于零碎化,不便于將所有文件內(nèi)容直接在文中展示,詳情請參考書本所提供的樣例代碼。項(xiàng)目結(jié)構(gòu)2.1應(yīng)用管理入口書本上5.2.2小節(jié)簡單介紹了使用Manager對應(yīng)用進(jìn)行托管的方法,即作為應(yīng)用管理入口。使用Manager托管可以很方便地將應(yīng)用功能(如Migrate、Shell等)聯(lián)系在一起。本小節(jié)將會把Manager部分的代碼單獨(dú)拆分到一個(gè)文件,以便于維護(hù)。以下是應(yīng)用管理入口文件(manage.py)的部分代碼實(shí)現(xiàn)。fromflask.helpersimportget_envfromflask_migrateimportMigrateCommandfromflask_scriptimportManager,Shellfromappimportcreate_app,db,dh#根據(jù)相應(yīng)的環(huán)境創(chuàng)建應(yīng)用實(shí)例,并初始化Managerenv=get_env()app=create_app(env)manager=Manager(app)項(xiàng)目結(jié)構(gòu)接著是注冊指令的代碼,代碼如下所示。defmake_shell_context():context=dict(db=db,dh=dh,)#將所有模型注冊到交互Shellcontext.update((cls.__name__,cls)forclsindh.get_all_model_classes())returncontext#注冊指令到Managermanager.add_command("shell",Shell(make_context=make_shell_context))manager.add_command('db',MigrateCommand)if__name__=='__main__':manager.run()由上述代碼可見,應(yīng)用管理入口文件僅包含了創(chuàng)建Manager所需的基本代碼,相當(dāng)簡潔。應(yīng)用實(shí)例就由下一小節(jié)來實(shí)現(xiàn)。項(xiàng)目結(jié)構(gòu)2.2應(yīng)用“工廠”函數(shù)一個(gè)完整的Flask應(yīng)用不只有應(yīng)用實(shí)例本身,應(yīng)用根據(jù)業(yè)務(wù)需求,往往依賴于多個(gè)模塊輔助。即初始化應(yīng)用時(shí),還需要初始化其相關(guān)的依賴模塊。單獨(dú)分析每一個(gè)模塊的初始化代碼相當(dāng)簡單,但將所有模塊的初始化代碼整合在一起,就比較復(fù)雜了。而且,在不同的環(huán)境下創(chuàng)建應(yīng)用,需要加載不同的配置類。根據(jù)以上需求,常見的解決方案便是建立應(yīng)用“工廠”函數(shù),將所有關(guān)于初始化應(yīng)用、相關(guān)模塊的內(nèi)容整合在一起,以便于維護(hù)。項(xiàng)目結(jié)構(gòu)首先將之前所創(chuàng)建的app.py文件刪除,然后建立一個(gè)名為“app”的Python包(PythonPackage),使用Python包可以更方便地?cái)U(kuò)展應(yīng)用功能,如下圖所示。注意:建立Python包后,需要將“templates”與“static”目錄移動(dòng)至“app”目錄內(nèi)。項(xiàng)目結(jié)構(gòu)以下是應(yīng)用“工廠”函數(shù)(app/__init__.py)的代碼實(shí)現(xiàn)。fromflaskimportFlaskfromflask_bootstrapimportBootstrapfromflask_migrateimportMigratefromflask_sqlalchemyimportSQLAlchemyfromconfigimportconfigsfrom.databaseimportDatabaseHelper#對象在函數(shù)外創(chuàng)建,以便在其他文件中引用bootstrap=Bootstrap()db=SQLAlchemy()migrate:Migrate#創(chuàng)建數(shù)據(jù)庫助手類實(shí)例dh=DatabaseHelper(db)首先是引入了所需要的擴(kuò)展,都是之前的內(nèi)容,大家如果不記得了可以回顧一下之前學(xué)習(xí)的知識。項(xiàng)目結(jié)構(gòu)接著就是將創(chuàng)建應(yīng)用的流程,封裝為一個(gè)函數(shù),方便我們?nèi)ス芾?。代碼如下。defcreate_app(env):app=Flask(__name__)#加載應(yīng)用配置config=configs.get(env)app.config.from_object(config)config.init_app(app)#初始化數(shù)據(jù)庫操作實(shí)例db.init_app(app)#初始化Flask-Migrateglobalmigratemigrate=Migrate(app,db)migrate.init_app(app,render_as_batch=True)#初始化Flask-Bootstrapbootstrap.init_app(app)bootstrap_cdns=app.extensions['bootstrap']['cdns']bootstrap_cdns['bootstrap']=bootstrap_cdns['local']bootstrap_cdns['jquery']=bootstrap_cdns['local']項(xiàng)目結(jié)構(gòu)剩下的代碼如下所示。#將各模塊的藍(lán)圖(Blueprint)注冊到應(yīng)用實(shí)例from.userimportuser#url_prefix參數(shù)可以為用戶模塊添加上級位置app.register_blueprint(user,url_prefix='/user')from.adminimportadmin,admin_userapp.register_blueprint(admin,url_prefix='/admin')app.register_blueprint(admin_user,url_prefix='/admin/user')#將所有模型類注冊到模板全局變量,以便于后續(xù)調(diào)用#注冊模型須放置在所有模型加載之后,否則將獲取不到相應(yīng)模型forclsindh.get_all_model_classes():app.add_template_global(cls)returnapp這里是將不同模塊的藍(lán)圖注冊到應(yīng)用程序中,使其可以處理不同URL路由,同時(shí)也注冊了模型類到模板的全局變量,以便在模板中訪問這些模型。藍(lán)圖的內(nèi)容會在后續(xù)詳細(xì)講到,這里就先略過。項(xiàng)目結(jié)構(gòu)在完成了應(yīng)用的,就到了數(shù)據(jù)庫的內(nèi)容,數(shù)據(jù)庫相關(guān)代碼,如“BaseModel”類等內(nèi)容,都整合到了“app/database.py”文件中。以下是數(shù)據(jù)庫相關(guān)功能(app/database.py)的代碼實(shí)現(xiàn)fromflask_sqlalchemyimportBaseQuery,Model,SQLAlchemy#定義所有模型的基類classBaseModel(Model):#聲明query屬性的類型,以獲得代碼提示補(bǔ)全query:BaseQuery#定義模型初始化方法def__init__(self,**kwargs):#將參數(shù)傳入父類初始化方法super(BaseModel,self).__init__(**kwargs)這里是用到了5.3.3小節(jié)里面的模型基類代碼,這里不再詳細(xì)講解具體代碼。項(xiàng)目結(jié)構(gòu)在完成了應(yīng)用的,就到了數(shù)據(jù)庫的內(nèi)容,數(shù)據(jù)庫相關(guān)代碼,如“BaseModel”類等內(nèi)容,都整合到了“app/database.py”文件中。以下是數(shù)據(jù)庫相關(guān)功能(app/database.py)的代碼實(shí)現(xiàn)fromflask_sqlalchemyimportBaseQuery,Model,SQLAlchemy#定義所有模型的基類classBaseModel(Model):#聲明query屬性的類型,以獲得代碼提示補(bǔ)全query:BaseQuery#定義模型初始化方法def__init__(self,**kwargs):#將參數(shù)傳入父類初始化方法super(BaseModel,self).__init__(**kwargs)這里是用到了5.3.3小節(jié)里面的模型基類代碼,這里不過多講解。項(xiàng)目結(jié)構(gòu)緊接上文的數(shù)據(jù)庫代碼,代碼如下。#定義對象輸出格式,方便輸出預(yù)覽def__repr__(self):fields=[]#獲取對象原始數(shù)據(jù)fork,vinself.__dict__.items():#只輸出相關(guān)屬性ifk[0]!='_':#防止模型關(guān)聯(lián)后無限遞歸ifisinstance(v,BaseModel):fields.append('%s=<%s...>'%(k,v.__class__.__name__))elifisinstance(v,str):fields.append("%s='%s'"%(k,v))else:fields.append('%s=%a'%(k,v))#拼接顯示結(jié)果result='<%s%s>'%(self.__class__.__name__,''.join(fields))returnresult這段是之前講過的定義對象輸出的格式代碼。大家也可以試著去修改這段代碼。項(xiàng)目結(jié)構(gòu)緊接上文的數(shù)據(jù)庫代碼,代碼如下。#定義數(shù)據(jù)庫助手類,以便進(jìn)行一些常用操作classDatabaseHelper:def__init__(self,db:SQLAlchemy):self._db=db#獲取所有基于BaseModel類的模型,以便注冊到交互Shelldefget_all_model_classes(self):classes=[]forclsinself._db.Model._decl_class_registry.values():ifhasattr(cls,'__tablename__')andissubclass(cls,BaseModel):classes.append(cls)returnclasses#定義獲取db.session的屬性,以獲得完整的代碼補(bǔ)全@propertydefsession(self)->Session:returnself._db.session這段是之前講過的把內(nèi)容注冊到Shell的代碼。在應(yīng)用“工廠”函數(shù)(create_app())中,大部分的初始化內(nèi)容都在以往的章節(jié)中有所介紹。除了“藍(lán)圖”,該內(nèi)容將會在下一節(jié)中進(jìn)行講解。模塊化開發(fā)03模塊化開發(fā)在前面章節(jié)的講解中,均采用了單一文件的方式進(jìn)行開發(fā)。在簡單的demo或演示中,采用這種方式固然簡單,可以快速達(dá)成目標(biāo)。但這種方式不適合用于完成大型項(xiàng)目,也不利于后續(xù)的維護(hù)。本節(jié)將主要介紹使用藍(lán)圖(Blueprint)來實(shí)現(xiàn)模塊化開發(fā),從而解決以上問題的方法。模塊化開發(fā)3.1使用藍(lán)圖在前面的例子中,注冊視圖函數(shù)都是直接使用應(yīng)用路由來進(jìn)行的。當(dāng)使用應(yīng)用“工廠”函數(shù)時(shí),便出現(xiàn)了一個(gè)問題:如何注冊視圖?將所有視圖函數(shù)都建立在應(yīng)用“工廠”函數(shù)中顯然是不合理的。而使用“藍(lán)圖”,則可以將數(shù)個(gè)視圖函數(shù)組合在一起,將其作為一個(gè)模塊注冊到應(yīng)用中。假設(shè)現(xiàn)在需要實(shí)現(xiàn)一個(gè)用戶模塊,可以參考6.2節(jié)中的項(xiàng)目結(jié)構(gòu)展示,在“app”包中再建立一個(gè)“user”包,同時(shí)在包內(nèi)建立“models.py”“views.py”“form.py”“errors.py”4個(gè)文件,以便于后續(xù)代碼的編寫。模塊化開發(fā)以下是模塊(藍(lán)圖)初始化代碼(__init__.py)的實(shí)現(xiàn)。fromflaskimportBlueprintuser=Blueprint('user',__name__)#加載相關(guān)的模型、視圖、錯(cuò)誤視圖(如果有)from.importmodels,views,errors初始化代碼相當(dāng)簡潔,僅創(chuàng)建了一個(gè)“Blueprint”類對象,引入了該包下各部分的功能。此處引入“models”“views”“errors”,可能有的同學(xué)會有疑問:創(chuàng)建完Blueprint類后,僅是引入這些內(nèi)容,后面并沒有使用這些內(nèi)容,這部分的代碼有何意義?此時(shí),可以回想一下,模型所繼承的“db.Model”對象及注冊函數(shù)時(shí)使用的“app.route”裝飾器。在“models.py”中定義的模型將會因?yàn)楸灰?,而被“db”對象捕捉到,實(shí)現(xiàn)表結(jié)構(gòu)的初始化;而“views.py”中定義的視圖函數(shù),將會因?yàn)槭褂谩皉oute”裝飾器而被初始化代碼中的“user”藍(lán)圖對象捕捉到,從而實(shí)現(xiàn)路由的注冊;“errors.py”中的錯(cuò)誤視圖函數(shù)同理。最后便完成了模型、視圖、錯(cuò)誤視圖的加載(或注冊)。模塊化開發(fā)以下是數(shù)據(jù)模型(models.py)的代碼實(shí)現(xiàn)。fromappimportdbfromapp.databaseimportBaseModelclassUserModel(db.Model,BaseModel):__tablename__='user'id=db.Column(db.INTEGER,primary_key=True,autoincrement=True)username=db.Column(db.VARCHAR,unique=True)password=db.Column(db.VARCHAR)is_admin=db.Column(db.BOOLEAN,default=False,nullable=False)info=db.relationship('UserInfoModel',backref='user',uselist=False,cascade='all')classUserInfoModel(db.Model,BaseModel):__tablename__='user_info'user:UserModeluser_id=db.Column(db.INTEGER,db.ForeignKey(UserModel.id),primary_key=True)phone=db.Column(db.VARCHAR)email=db.Column(db.VARCHAR)introduce=db.Column(db.TEXT)用戶模塊模型較為簡單,僅是賬號模型與信息模型進(jìn)行了一對一關(guān)聯(lián),以描述一個(gè)完整的用戶。模塊化開發(fā)以下是表單類(forms.py)的代碼實(shí)現(xiàn)。fromflask_wtfimportFlaskFormfromwtformsimportStringField,PasswordField,SubmitField,TextAreaField,BooleanFieldfromwtforms.validatorsimportDataRequired,Email,Optional,RegexpclassLoginForm(FlaskForm):username=StringField(label='用戶名',validators=[DataRequired()])password=PasswordField(label='密碼',validators=[DataRequired()])remember=BooleanField(label='記住該用戶')submit=SubmitField(label='登錄')classRegisterForm(FlaskForm):username=StringField(label='用戶名',validators=[DataRequired()])password=PasswordField(label='密碼',validators=[DataRequired()])#此處使用11位數(shù)字的正則表達(dá)式檢測手機(jī)號phone=StringField(label='手機(jī)號',validators=[Optional(),Regexp(r'\d{11}')])email=StringField(label='郵箱',validators=[Optional(),Email()])introduce=TextAreaField(label='自我介紹')submit=SubmitField(label='注冊')上述代碼僅包含了登錄、注冊功能,結(jié)構(gòu)也是相當(dāng)簡單。模塊化開發(fā)以下是視圖函數(shù)(views.py)的代碼實(shí)現(xiàn)。由于內(nèi)容過多,所以分成注冊用戶、登錄用戶、查看用戶信息3個(gè)基本功能的視圖函數(shù)來講。首先還是要引入相關(guān)的內(nèi)容依賴,大家要記得不要引用錯(cuò)誤,導(dǎo)致代碼報(bào)錯(cuò)。引入的代碼如下所示。fromflaskimportrender_template,request,redirect,flash,abortfromappimportdhfrom.importuserfrom.formsimport*from.modelsimport*模塊化開發(fā)以下是用戶注冊的代碼。#用戶注冊@user.route('/register',methods=['GET','POST'])defregister():form=RegisterForm()ifrequest.method=='POST'andform.validate_on_submit():item=UserModel(username=form.username.data,password=form.password.data,is_admin=False,info=UserInfoModel(phone=form.phone.data,email=form.email.data,introduce=roduce.data,),)try:dh.session.add(item)mit()flash('注冊成功','success')returnredirect('login')exceptExceptionase:flash('注冊失敗-%s'%e,'danger')returnabort(500)else:returnrender_template('user/register.html',form=form)模塊化開發(fā)以下是用戶登錄的代碼。#用戶登錄@user.route('/login',methods=['GET','POST'])deflogin():form=LoginForm()ifrequest.method=='POST'andform.validate_on_submit():try:item=UserModel.query.filter_by(username=form.username.data,password=form.password.data).first()ifitemisnotNone:flash('登錄成功','success')returnredirect(request.path)else:raiseException('用戶名或密碼錯(cuò)誤')exceptExceptionase:flash('登錄失敗-%s'%e,'danger')returnabort(500)else:returnrender_template('user/login.html',form=form)用戶訪問/user/login頁面時(shí),可以輸入用戶名和密碼,然后將其提交給服務(wù)器進(jìn)行處理。這個(gè)登錄過程包括表單驗(yàn)證和數(shù)據(jù)庫查詢操作。如果登錄成功,用戶會得到成功的提示并被重定向到當(dāng)前頁面,否則會收到登錄失敗的消息。模塊化開發(fā)以下是查看用戶信息的代碼。@user.route('/<int:id>')defview(id:int):try:item=UserModel.query.get(id)#type:UserModelifitemisnotNone:returnrender_template('user/view.html',item=item)else:raiseException('用戶不存在')exceptExceptionase:flash('查看用戶-%s'%e,'danger')returnabort(404)用戶可以訪問/user/<id>頁面,其中<id>是用戶的標(biāo)識符,以查看特定用戶的詳細(xì)信息。如果用戶存在,將顯示用戶詳情頁面,否則將顯示404錯(cuò)誤頁面。上述代碼包含了用戶模塊的功能實(shí)現(xiàn),包含了登錄、注冊、查看用戶信息3個(gè)基本功能的視圖函數(shù)(相關(guān)模板頁面可以自行設(shè)計(jì)或參考本書所提供的樣例代碼實(shí)現(xiàn))。模塊化開發(fā)在完成了模型、視圖、錯(cuò)誤視圖、表單代碼的編寫以后,在應(yīng)用“工廠”函數(shù)中,將“user”藍(lán)圖實(shí)例注冊到應(yīng)用實(shí)例即可。以下是應(yīng)用“工廠”函數(shù)(app/__init__.py)需要修改的部分。#……引入依賴代碼……#……其他初始化代碼……defcreate_app(env):#……應(yīng)用初始化代碼……#在此處將用戶模塊的藍(lán)圖(Blueprint)注冊到應(yīng)用實(shí)例from.userimportuser#url_prefix參數(shù)可以為用戶模塊添加上級位置app.register_blueprint(user,url_prefix='/user')#……其他初始化代碼……此時(shí),即可通過訪問“/user/register”頁面來進(jìn)行用戶注冊,用戶注冊完成后可通過訪問“/user/login”頁面來進(jìn)行用戶登錄,還可根據(jù)id訪問“/user/<id>”頁面查看用戶相關(guān)信息。注意:在運(yùn)行程序之前需要初始化數(shù)據(jù)庫,具體操作可參考書本5.4.3小節(jié)。模塊化開發(fā)用戶模塊完成后的演示效果如下圖所示。用戶注冊頁面模塊化開發(fā)用戶模塊完成后的演示效果如下圖所示。用戶登錄頁面模塊化開發(fā)用戶模塊完成后的演示效果如下圖所示。查看用戶信息頁面模塊化開發(fā)3.2子模塊在大型網(wǎng)站中,一個(gè)模塊可以由多個(gè)子模塊組成。例如,在博客系統(tǒng)中,后臺管理模塊可以用于管理用戶、文章,那么便可以將后臺管理模塊拆分成用戶管理模塊、文章管理模塊兩個(gè)子模塊。在3.1小節(jié)的例子中,應(yīng)用實(shí)例可以通過注冊藍(lán)圖實(shí)現(xiàn)模塊化開發(fā);而在當(dāng)前版本的Flask中,可以從項(xiàng)目結(jié)構(gòu)的方面入手來實(shí)現(xiàn)子模塊的效。模塊化開發(fā)具體實(shí)現(xiàn)可參考接下來的“用戶管理模塊”代碼。以下是管理模塊初始化(admin/__init__.py)的代碼實(shí)現(xiàn)。fromflaskimportBlueprintadmin=Blueprint('admin',__name__)from.userimportuserasadmin_userfrom.importviews以下是用戶管理模塊初始化(admin/user/__init__.py)的代碼實(shí)現(xiàn)。fromflaskimportBlueprintuser=Blueprint('admin.user',__name__)from.importviews,models,errors模塊化開發(fā)以下是應(yīng)用“工廠”函數(shù)(app/__init__.py)的代碼實(shí)現(xiàn)。defcreate_app(env): #……應(yīng)用初始化代碼…… #將各模塊的藍(lán)圖(Blueprint)注冊到應(yīng)用實(shí)例 from.userimportuser #url_prefix參數(shù)可以為用戶模塊添加上級位置 app.register_blueprint(user,url_prefix='/user') from.adminimportadmin,admin_user app.register_blueprint(admin,url_prefix='/admin') app.register_blueprint(admin_user,url_prefix='/admin/user') from.portalimportportal app.register_blueprint(portal) #……其他初始化代碼……模塊化開發(fā)后臺首頁與用戶管理模塊的簡單演示效果如下圖所示后臺首頁模塊化開發(fā)當(dāng)前例子的后臺主體內(nèi)容僅包含了首頁。通常情況下,后臺首頁作為各個(gè)模塊管理功能的入口。如下圖所示。用戶管理功能Flask-Login04概念介紹Flask-Login是什么?Flask-Login是一個(gè)Flask擴(kuò)展,用于處理用戶身份驗(yàn)證和用戶會話管理。它簡化了用戶認(rèn)證和會話管理的實(shí)現(xiàn),特別適用于構(gòu)建需要用戶登錄功能的Web應(yīng)用程序?!?.1小節(jié)實(shí)現(xiàn)了一個(gè)簡單的用戶模塊,其中登錄視圖函數(shù)代碼的功能是從表單(用戶輸入)中獲取賬號密碼,然后在數(shù)據(jù)庫中查找與信息匹配的用戶,如果存在便提示登錄成功。但仍存在一個(gè)問題:如果登錄后訪問其他頁面,會發(fā)現(xiàn)用戶并沒有登錄。實(shí)際上其中還缺了一部分核心功能,那便是用戶認(rèn)證(會話)的實(shí)現(xiàn)。Flask-Login顧名思義就是與登錄功能相關(guān)的模塊。該模塊主要用于實(shí)現(xiàn)“用戶認(rèn)證”,以及“要求認(rèn)證訪問”,它提供了一套用戶基本模型,可以很好地解決以上問題。為什么要用Flask-Login?”概念介紹安裝依賴與安裝Flask的操作一致,打開命令提示符窗口,輸入以下命令。pipinstallflask-login==0.4.1執(zhí)行上述命令之后,能看到“Successfullyinstalled…”提示信息,沒有提示紅色的報(bào)錯(cuò)信息,即安裝成功,此時(shí),F(xiàn)lask-Login的依賴包便安裝完成了,如下圖所示。用戶認(rèn)證實(shí)現(xiàn)用戶認(rèn)證從原理上看其實(shí)很簡單,僅需要使用會話(Session)對客戶瀏覽器進(jìn)行記錄即可。而Flask-Login實(shí)現(xiàn)了一個(gè)易于維護(hù)的基本用戶模型,規(guī)范了用戶認(rèn)證的操作,易于擴(kuò)展。要使用Flask-Login,首先需要在應(yīng)用初始化文件(app/__init__.py)中添加相關(guān)代碼,要添加的代碼如下。#……引入依賴代碼……fromflask_loginimportLoginManager
#……其他初始化代碼……login_manager=LoginManager()
defcreate_app(env):#……應(yīng)用初始化代碼……
#初始化Flask-Loginlogin_manager.login_view='user.login'login_manager.login_message_category='danger'login_manager.login_message='請登錄后再進(jìn)行操作'login_manager.init_app(app)
#……將模塊的藍(lán)圖(Blueprint)注冊到應(yīng)用實(shí)例……
#其他初始化代碼用戶認(rèn)證
#初始化Flask-Loginlogin_manager.login_view='user.login'login_manager.login_message_category='danger'login_manager.login_message='請登錄后再進(jìn)行操作'login_manager.init_app(app)login_manager.login_view='user.login':這一行設(shè)置了登錄頁面的視圖函數(shù),它告訴Flask-Login在需要用戶登錄時(shí)將用戶重定向到user.login視圖。也就是說,如果用戶未登錄嘗試訪問需要登錄的頁面,F(xiàn)lask-Login將自動(dòng)重定向到user.login視圖,以便用戶登錄。login_manager.login_message_category='danger':這一行設(shè)置了登錄消息的Bootstrap樣式類別。在默認(rèn)情況下,F(xiàn)lask-Login使用info類別來顯示登錄消息,但在這里,將它設(shè)置為danger,以便登錄消息顯示為紅色,以突出顯示警告。login_manager.login_message='請登錄后再進(jìn)行操作':這一行設(shè)置了顯示給用戶的登錄提示消息。如果用戶嘗試訪問需要登錄的頁面但未登錄,將顯示這條消息。login_manager.init_app(app):最后,這一行初始化Flask-Login擴(kuò)展,將其與Flask應(yīng)用程序?qū)嵗齛pp關(guān)聯(lián)起來,以便在整個(gè)應(yīng)用程序中使用Flask-Login功能。拿新添的代碼給大家講解一下,新添的代碼如下。Flask-Login然后使用戶模型(user/models.py)繼承Flask-Login所提供的基本用戶實(shí)現(xiàn),代碼如下。fromflask_loginimportUserMixin#使用戶模型繼承Flask-Login的基本用戶實(shí)現(xiàn)(UserMixin)classUserModel(UserMixin,db.Model,BaseModel):#……用戶模型代碼……Flask-Login再添加模塊公共方法(user/common.py),實(shí)現(xiàn)當(dāng)前用戶的獲取及Flask-Login所需的用戶加載器,代碼如下。fromflask_loginimportcurrent_userfromappimportlogin_manager#用于Flask-Login獲取用戶from.modelsimportUserModel@login_manager.user_loaderdef_load_user(id:int):returnUserModel.query.get(id)#獲取當(dāng)前用戶,指定類型以便獲取代碼補(bǔ)全defget_current_user()->UserModel:returncurrent_userFlask-Login最后在視圖函數(shù)中(user/views.py)實(shí)現(xiàn)登錄功能即可,代碼如下。fromflask_loginimportlogin_user#用戶登錄@user.route('/login',methods=['GET','POST'])deflogin():form=LoginForm()ifrequest.method=='POST'andform.validate_on_submit():try:item=UserModel.query.filter_by(username=form.username.data,password=form.password.data).first()ifitemisnotNone:#使用Flask-Login完成用戶登錄認(rèn)證login_user(item)flash('登錄成功','success')returnredirect(request.path)else:raiseException('用戶名或密碼錯(cuò)誤')exceptExceptionase:flash('登錄失敗-%s'%e,'danger')returnabort(500)else:returnrender_template('user/login.html',form=form)到此,用戶認(rèn)證便完成了,可以在其他模塊(視圖函數(shù))中,調(diào)用用戶模塊公共方法(common.py)中的get_current_user()函數(shù)獲取當(dāng)前登錄的用戶。Flask-Login4.1要求認(rèn)證訪問在某些頁面中,例如注銷頁面、查看當(dāng)前登錄的用戶信息頁面等,需要用戶登錄后才可訪問。這時(shí)可能讀者會有一個(gè)疑問:難道每一個(gè)需要使用當(dāng)前用戶的視圖函數(shù)都需要添加一段“ifcurrent_userisnotNone”的代碼嗎?答案是不需要,使用Flask-Login,只要添加“l(fā)ogin_required”裝飾器即可。Flask-Login以下是視圖函數(shù)(user/views.py)的代碼fromflask_loginimportlogout_user,login_requiredfrom.commonimportget_current_user@user.route('/logout')@login_requireddeflogout():iflogout_user():flash('注銷成功','success')returnredirect('/user/login')#查看用戶信息(當(dāng)前用戶)@user.route('/')@login_requireddefview_current():item=get_current_user()returnrender_template('user/view.html',item=item)@login_required:這個(gè)裝飾器確保只有已登錄的用戶才能訪問這個(gè)路由。如果未登錄的用戶嘗試訪問/user/logout,F(xiàn)lask-Login會自動(dòng)將他們重定向到登錄頁面。Flask-Login以上例子(查看當(dāng)前用戶信息)僅用于演示要求驗(yàn)證訪問。如果需要在模板頁面中使用當(dāng)前登錄的用戶,僅需要使用Flask-Login所注冊的全局對象“current_user”即可。參考“common/nav.html”編寫模板頁面的代碼,因篇幅過長,此處僅截取部分代碼進(jìn)行展示。代碼在下一頁展示。Flask-Login<ulclass="navnavbar-navnavbar-right"><liclass="dropdown">{%ifcurrent_user.is_anonymous%}{#如果用戶沒有登錄,即顯示游客用的菜單#}<ahref="#"class="dropdown-toggle"data-toggle="dropdown"role="button"aria-haspopup="true"aria-expanded="true">游客<spanclass="caret"></span></a><ulclass="dropdown-menu"><li><ahref="{{url_for('user.login')}}">登錄</a></li><li><ahref="{{url_for('user.register')}}">注冊</a></li></ul>{%else%}{#登錄以后顯示用戶名#}<ahref="#"class="dropdown-toggle"data-toggle="dropdown"role="button"aria-haspopup="true"aria-expanded="true">{{current_.nicknameorcurrent_user.username}}<spanclass="caret"></span></a><ulclass="dropdown-menu"><li><ahref="{{url_for('user.edit')}}">用戶信息</a></li><liclass="divider"></li><li><ahref="{{url_for('user.logout')}}">注銷</a></li></ul>{%endif%}</li></ul>Flask-Login查看當(dāng)前登錄用戶信息的效果如下圖所示。查看當(dāng)前登錄用戶信息Flask-Login注銷登錄用戶后查看當(dāng)前登錄用戶信息的效果如下圖所示。注銷登錄用戶后查看當(dāng)前登錄用戶信息Flask-Login4.2管理員認(rèn)證訪問4.1小節(jié)并沒有提到后臺管理模塊的修改。顯然,后臺管理模塊是需要用戶登錄認(rèn)證后才可以進(jìn)行訪問的,但同時(shí),也不是所有用戶都可以訪問,只有管理員才可以對后臺進(jìn)行操作。在之前所義的用戶模型中有一個(gè)“is_admin”的布爾值屬性,可以基于“l(fā)ogin_required”裝飾器,使用這個(gè)屬性對當(dāng)前登錄的用戶進(jìn)行檢測,以實(shí)現(xiàn)管理員認(rèn)證訪問。Flask-Login以下是“admin_required”裝飾器(admin/common.py)的代碼實(shí)現(xiàn)。fromfunctoolsimportwrapsfromflaskimportabort,flashfromflask_loginimportlogin_requiredfrommonimportget_current_userdefadmin_required(func):@wraps(func)defdecorated_view(*args,**kwargs):ifnotget_current_user().is_admin:flash('拒絕訪問','danger')returnabort(403)returnfunc(*args,**kwargs)returnlogin_required(decorated_view)以上代碼對“l(fā)ogin_required”裝飾器進(jìn)行了包裝,然后ifnotget_current_user().is_admin:在decorated_view視圖函數(shù)中,首先調(diào)用get_current_user()函數(shù)獲取當(dāng)前登錄的用戶信息,然后檢查該用戶是否具有管理員權(quán)限。如果用戶不是管理員,表示沒有權(quán)限訪問受保護(hù)的視圖。Flask-Login那么接下來,所有與管
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 四川省成都市簡陽市2021年中考語文一診試題及參考答案
- 小學(xué)數(shù)學(xué)五年級上冊小數(shù)乘除法解決問題
- 小學(xué)生一年級20以內(nèi)的加減法練習(xí)題
- 《簡短戒煙干預(yù)技術(shù)》課件
- 《奧斯維辛沒有新聞》課件
- 煤炭銷售代理委托合同三篇
- 自主學(xué)習(xí)環(huán)境的構(gòu)建工作計(jì)劃
- 汽車設(shè)計(jì)師工作總結(jié)
- 人力資源行業(yè)業(yè)務(wù)員工作總結(jié)
- 線上線下整合營銷策略計(jì)劃
- 專項(xiàng)債券培訓(xùn)課件
- 中央企業(yè)人工智能應(yīng)用場景案例白皮書(2024年版)-中央企業(yè)人工智能協(xié)同創(chuàng)新平臺
- 江蘇省蘇州市2024-2025學(xué)年第一學(xué)期八年級歷史期末模擬卷(二)(含答案)
- 甘肅蘭州生物制品研究所筆試題庫
- 醫(yī)院改擴(kuò)建工程可行性研究報(bào)告(論證后)
- 雙方共同招工協(xié)議書(2篇)
- 2021-2022學(xué)年第二學(xué)期《大學(xué)生職業(yè)發(fā)展與就業(yè)指導(dǎo)2》學(xué)習(xí)通超星期末考試答案章節(jié)答案2024年
- 期末檢測試卷(試題)-2024-2025學(xué)年四年級上冊數(shù)學(xué)青島版
- 國家開放大學(xué)電大本科《工程經(jīng)濟(jì)與管理》2023-2024期末試題及答案(試卷代號:1141)
- 客車交通安全培訓(xùn)課件
- 醫(yī)院勞務(wù)外包服務(wù)方案(技術(shù)方案)
評論
0/150
提交評論