




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
讲师:李粤平Flask课程——第七章实例:简易博客系统理解如何根据项目需求设计相应模块根据数据模型设计图完成模型类的构建完成简易博客系统的开发学习目标教学重点教学难点博客系统是一个典型的开发入门案例,其中包含了用户验证、内容管理、数据关联等基本功能实现的综合应用。本章将会从项目设计开始,逐步完成各个模块的开发,最终实现一个完整的简易博客系统。教学重难点用户模块项目设计入口模块文章模块小结管理模块项目设计01项目设计在项目开发之前,需要了解项目需求;然后分析需求中所包含的功能,将各个功能划分到不同的模块,确立程序整体结构;接着分析各个模块所包含的功能,为需使用的数据模型设计具体结构。当项目设计完成之后,再将设计翻译成相应的代码实现。项目设计1.1项目需求一个简单的博客系统通常包含文章发布、文章分类、文章搜索、文章阅读量统计、文章留言、博客信息展示、友情链接展示等功能模块。接下来将逐步完成以上功能的开发。项目设计1.2模块设计根据需求,可以分析出该系统的整体结构、功能模块架构,如下图所示。该系统可以分成4个模块进行开发,各个模块负责各部分的内容。要注意的是,管理模块建立于其他模块之上(例如,先有用户模块,后有用户管理模块),接下来只要理解各个模块之间的关系,设计好相应的数据模型,最后为这些模块开发相应功能即可。项目设计1.3数据模型设计了解程序模块结构后,便要对模块所包含的功能中所使用的“对象”进行设计,建立数据模型,完成数据库的创建。为右图图所示为各个数据模型类的简要设计图,包含模型属性及各个模型之间的关系。根据设计图所描述的结构完成各个数据模型类的定义后(可参考书本5.3.2、5.3.5小节),便可以使用Flask-Migrate生成数据库。数据库建立完成之后,便可以开始开发相应的模块、功能。项目设计1.4功能实现在接下来的功能代码实现中,模板页面文件将放置于“templates”(模板)目录中,而Python代码文件放置于“模块”目录中;这是约定俗成的规则。详细的项目结构可参考书本6.2节,模块初始化代码可参考书本6.3节。入口模块02入口模块入口模块是所有模块的基础之一,包含网站的首页,以及一些公共功能(如友情链接、网站设置)的实现。入口模块2.1数据模型定义友情链接模型的实现相当简单,它仅包含名称及其对应的链接(HTML中的<a>标签)。设定模型与友情链接模型在结构上基本一致,但“key”字段唯一,用于快速获取对应的设定值,“value”字段用于存储JSON数据对象(字符串格式,可与Python的基本数据类型相互转换)。数据模型图如下所示。入口模块根据数据模型设计图,可定义以下模型,代码如下(portal/models.py)。fromappimportdbfromapp.databaseimportBaseModelclassFriendLinkModel(db.Model,BaseModel):__tablename__='friend_link'id=db.Column(db.INTEGER,primary_key=True,autoincrement=True)name=db.Column(db.VARCHAR)url=db.Column(db.VARCHAR)classSettingModel(db.Model,BaseModel):__tablename__='setting'id=db.Column(db.INTEGER,primary_key=True,autoincrement=True)key=db.Column(db.VARCHAR,unique=True)value=db.Column(db.VARCHAR)入口模块2.2设定存取每一个页面中都会显示博客的名称及个性签名之类的内容。对这种用途很少、对象唯一的功能,不需要为其单独建立模型,只需将其作为一个设置项保存到数据库即可。所以首先需要建立设定项存取的方法。入口模块以下是公共方法的代码实现(portal/common.py)。分为读取设定数据和保存设定数据。读取设定数据代码如下。importjsonfromappimportdhfromapp.portal.modelsimportSettingModel#读取设定数据defload_setting(key:str):item=SettingModel.query.filter_by(key=key).first()#type:SettingModelifitemisnotNone:returnjson.loads(item.value)returnNoneitem=SettingModel.query.filter_by(key=key).first():这一行代码执行了一个数据库查询操作。它使用SQLAlchemy查询语言,从名为"SettingModel"的数据库模型中筛选出符合给定"key"的第一条记录,并将结果赋值给变量"item"。ifitemisnotNone:这是一个条件语句,检查变量"item"是否不为空。如果存在符合条件的设置项记录,就执行以下操作。入口模块保存设定数据代码如下。#保存设定数据defsave_setting(key:str,value):item=SettingModel.query.filter_by(key=key).first()#type:SettingModelifitemisNone:item=SettingModel(key=key)item.value=json.dumps(value,ensure_ascii=False)dh.session.add(item)mit()returnFalseitem.value=json.dumps(value,ensure_ascii=False):将参数"value"转换为JSON格式的字符串,并将其赋值给"item"的"value"属性。这将把设置项的数据保存为JSON字符串。接着就是把item对象添加到数据库会话中,再提交。入口模块由于JSON可以很方便地将Python对象类型转换为str类型数据进行存取,所以使用JSON格式存储相关数据到数据库是一个很好的选择。读取、保存设定数据的两个方法接下来需要在各个模板中使用,所以需要在应用中将其注册为全局对象,在应用“工厂”函数中添加以下粗体代码即可。defcreate_app(env):#……其他应用初始化代码……
#从Python包中获取所有可以调用的对象并注册到模板全局变量,以便模板调用defregister_all_callable_object_from_package(pkg,is_filter=False):forkindir(pkg):f=getattr(pkg,k)ifcallable(f):ifis_filter:app.add_template_filter(f)else:app.add_template_global(f)
fromapp.portalimportcommonascommon_portalregister_all_callable_object_from_package(common_portal)#……其他应用初始化代码……入口模块2.3公共模板页面在博客系统中,很多页面都包含公共的部分,例如导航栏、友情链接、页尾等内容会在大部分页面中展示。显然,这些内容不会被重复编写,而是通过“模板包含、继承”的方式重复使用(参考书本3.2.6小节)。入口模块以下是前台基准页的代码实现(common/base.html)。分为Flask-Bootstrap提供的基准页,同时加载系统设置、信息设置、导航栏、页尾这几块来说。首先是继承基准页的信息,代码如下。{#前台基准模板,加载了站点设置与博客信息设置#}
{%extends'bootstrap/base.html'%}
{%import'bootstrap/utils.html'asutils%}
{%setsite_setting=load_setting('site_setting')%}{%setinfo_setting=load_setting('info_setting')%}前面两行代码是继承之前的bootstrap的base模板,以及导入utils模板,并且重命名后两行代码分别用于从数据库中加载站点设置和博客信息设置,并将它们存储在变量"site_setting"和"info_setting"中。这些设置数据将在模板中用于动态渲染页面内容。入口模块以下是前台基准页的代码实现(common/base.html)。以下是导航栏,尾页等内容(大部分页面会用到)。{%blocknavbar%}{#加载导航栏#}{%include'common/nav.html'%}{%endblock%}{%blockstyles%}{{super()}}<linkhref="{{url_for('static',filename='blog.css')}}"rel="stylesheet">{%endblock%}{%blockcontent%}<divclass="container"style="margin-bottom:16px;padding-top:72px;">{{utils.flashed_messages()}}{%blockcontent_inner%}{%endblock%}</div>{#网页尾部内容,根据站点设置生成#}<footerclass="blog-footer"><p>{{site_setting.site_footer}}</p><p><ahref="#">回到顶端</a></p></footer>{%endblock%}入口模块界面样式基于Bootstrap官方样例修改而成,由于本章主要内容为Flask应用的功能实现,故样式等静态资源请同学们自行参阅本书提供的样例代码。入口模块导航栏的结构较为复杂,所以会分成好几块给大家讲解一下。以下是导航栏模板页的代码实现(common/nav.html)。<navclass="navbarnavbar-inversenavbar-static-top"style="position:fixed;width:100%;">...中间代码省略...</nav>这是<nav>元素的开始标签,表示一个导航栏。导航栏的样式由多个类定义,这些类通常来自于前端框架(比如Bootstrap)。这里的类包括:navbar:表示这是一个导航栏元素。navbar-inverse:指定了导航栏的颜色方案,这里是一个深色背景的导航栏。navbar-static-top:将导航栏固定在页面的顶部。另外,style属性设置了一些内联样式,其中position:fixed;让导航栏固定在页面上,而width:100%;让导航栏占满整个页面宽度。入口模块以下是导航栏模板页的代码实现(common/nav.html)。
<divclass="navbar-header">{#该按钮用于伸缩菜单,在小型设备(如手机)中使用#}<buttontype="button"class="navbar-togglecollapsed"data-toggle="collapse"data-target="#navbar"aria-expanded="false"aria-controls="navbar"><spanclass="sr-only">Togglenavigation</span><spanclass="icon-bar"></span><spanclass="icon-bar"></span><spanclass="icon-bar"></span></button>{#导航栏左侧网站名称#}<aclass="navbar-brand"href="/">{{site_setting.site_name}}</a></div>这是一个标准的小型设备上显示一个可伸缩菜单按钮和网站名称的部分代码。点击按钮时,它会触发导航栏菜单的展开或折叠,以提供更好的移动设备用户体验。具体的参数可以去/components/#navbar
这里去查看导航栏的写法。入口模块以下是导航栏模板页的代码实现(common/nav.html)。<divid="navbar"class="collapsenavbar-collapse">...中间代码省略...</div>这是一个导航栏的折叠容器,为了放后面的菜单内容。入口模块以下是导航栏模板页的代码实现(common/nav.html)。<ulclass="navnavbar-nav"><liclass="dropdown"><ahref="{{url_for('article.list_all')}}">所有文章</a></li></ul>{#加载文章分类菜单#}{%include'common/nav_menu/category.html'%}{#加载用户菜单#}{%include'common/nav_menu/user.html'%}ul这一块代码是写了一个下拉子菜单。接着后面两个就是引入两个模板文件,以生成文章分类的子菜单和用户相关的菜单项。入口模块以下是导航栏模板页的代码实现(common/nav.html)。{#如果用户是管理员,则显示后台管理菜单#}{%ifcurrent_user.is_admin%}<ulclass="navnavbar-navnavbar-right"><liclass="dropdown"><ahref="{{url_for('admin.index')}}">后台管理</a></li></ul>{%endif%}紧接着是把管理员的后台管理菜单也写了进来,用了个if来判断是否是管理员的身份。入口模块以下是导航栏模板页的代码实现(common/nav.html)。{#搜索栏代码#}<formclass="navbar-formnavbar-right"action="{{url_for('article.search')}}"><label><inputname="keyword"type="text"class="form-control"placeholder="Search..."value="{%ifrequest.endpoint=='article.search'%}{{request.args.keyword}}{%endif%}"></label></form>value="{%ifrequest.endpoint=='article.search'%}{{request.args.keyword}}{%endif%}":根据当前请求的端点(endpoint)来动态设置输入框的值。如果请求的端点是'article.search',则输入框会显示搜索关键字,否则为空。入口模块导航栏的结构较为复杂,为便于展示,上面采用模板包含方式引入结构较为复杂的菜单项。引入的是文章分类菜单(category.html)和用户菜单(user.html),下面是文章分类菜单的代码实现(common/nav_menu/category.html)。{#文章分类菜单代码#}<ulclass="navnavbar-nav"><liclass="dropdown"><ahref="#"class="dropdown-toggle"data-toggle="dropdown"role="button"aria-haspopup="true"aria-expanded="true">文章分类<spanclass="caret"></span></a><ulclass="dropdown-menu">{%forcategoryinCategoryModel.query.all()%}<li><ahref="{{url_for('article.category',name=)}}">{{}}</a></li>{%endfor%}</ul></li></ul>入口模块下面是用户菜单的代码实现(common/nav_menu/user.html)。{#用户菜单代码#}<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>入口模块前台基准页看似简单,实际上是由上述各个部分的内容一点点组合而成的,在基准页完成以后,每一个前台页面都基于基准页进行开发。下面是前台表单页面的代码实现(common/form.html)。{%extendscommon/base.html'%}{%import'bootstrap/wtf.html'aswtf%}{%blocktitle%}{{title}}{%endblock%}{%blockcontent_inner%}{{wtf.quick_form(form)}}{%endblock%}此页面将在接下来的用户模块等其他页面中多次使用。入口模块2.4文章分类、友情链接展示文章分类、友情链接将于各个页面的右侧进行展示,所以也是作为公共模板页面存在。下面会分开两个页面来讲解。入口模块以下是右侧栏模板页的代码实现(common/right.py)。接下来将会在需要展示右侧栏的页面中包含这个文件。{#博客右侧栏,包含“关于”部分内容、文章分类、友情链接#}<divclass="col-sm-3col-sm-offset-1blog-sidebar"><divclass="sidebar-modulesidebar-module-inset"><h4>关于</h4><p>{{info_setting.about}}</p></div><divclass="sidebar-modulesidebar-module-inset"><h4>文章分类</h4><olclass="list-unstyled">{%foriteminCategoryModel.query.all()%}<li><ahref="{{url_for('article.category',name=)}}">{{}}</a></li>{%endfor%}</ol></div><divclass="sidebar-modulesidebar-module-inset"><h4>友情链接</h4><olclass="list-unstyled">{%foriteminFriendLinkModel.query.all()%}<!--target="_blank"可以实现单击链接打开新页面的功能--><li><ahref="{{item.url}}"target="_blank">{{}}</a></li>{%endfor%}</ol></div></div>入口模块2.5博客信息展示(首页)博客信息展示这个功能仅在首页中可见。首页需要根据“系统设置”中提供的参数,展示一定数量的文章,此时便需要用到公共方法中的load_setting(),然后根据数量从文章模型获取相应的数据。入口模块以下是视图函数的代码实现(portal/views.py)。fromflaskimportrender_templatefromapp.article.modelsimportArticleModelfrommonimportload_settingfrom.importportal@portal.route('/')defindex():site_setting=load_setting('site_setting')home_per_page=site_setting.get('home_per_page',10)articles=ArticleModel.query.order_by(ArticleModel.id.desc()).limit(home_per_page).all()returnrender_template('portal/index.html',articles=articles)入口模块由于首页仅展示最新的几篇文章,所以只需要根据id倒序排列获取文章并注入模板页中即可。以下是模板页面的代码实现(portal/index.html)。{%extends'common/base.html'%}{%import'article/macros.html'asmacros%}{%blocktitle%}首页{%endblock%}{%blockcontent_inner%}<divclass="container"><divclass="row"><divclass="col-sm-8blog-main">{#首页的博客头部信息#}<divclass="blog-header"><h1class="blog-title">{{info_setting.title}}</h1><pclass="leadblog-description">{{info_setting.description}}</p></div>{#根据首页文章列表生成相应的HTML结构#}{{macros.generate_articles(articles)}}</div>{%include'common/right.html'%}</div></div>{%endblock%}入口模块博客首页完成后的效果如下图所示。首页继承了公共模板前台基准页,引入了文章模块中的宏指令(后续介绍),以及公共模板中的右侧栏部分(用于展示“关于”内容、文章分类、友情链接),接下来将逐步进行分析。入口模块2.6实现CKEditor上传功能CKEditor是一个富文本编辑器(可参考书本4.4节),在本项目的前台、后台中都会用到(后续介绍)。为便于开发,将其代码于入口模块实现。入口模块以下是配置文件的代码实现(config.py)。importosbasedir=os.path.abspath(os.path.dirname(__file__))#配置类基类,用于定义一些固定的参数classConfig:SECRET_KEY='Chapter7'SQLALCHEMY_TRACK_MODIFICATIONS=False#CKEditor配置项CKEDITOR_SERVE_LOCAL=TrueCKEDITOR_FILE_UPLOADER='portal.upload'UPLOADED_PATH=os.path.join(basedir,'uploads')#可在初始化时执行自定义操作@staticmethoddefinit_app(app):pass此处仅展示配置类基类,其他内容可参考书本6.1节。入口模块以下是视图函数的代码实现(portal/views.py)。#省略引入代码。#CKEditor,用于获取上传的文件@portal.route('/files/<filename>')defuploaded_files(filename):path=current_app.config['UPLOADED_PATH']returnsend_from_directory(path,filename)#CKEditor,用于上传文件@portal.route('/upload',methods=['POST'])defupload():f=request.files.get('upload')extension=f.filename.split('.')[1].lower()ifextensionnotin['jpg','gif','png','jpeg']:returnupload_fail(message='Imageonly!')f.save(os.path.join(current_app.config['UPLOADED_PATH'],f.filename))url=url_for('portal.uploaded_files',filename=f.filename)returnupload_success(url=url)入口模块最后只要在配置文件(config.py)中添加CKEditor的相关配置即可,相关代码如下。importosbasedir=os.path.abspath(os.path.dirname(__file__))#配置类基类,用于定义一些固定的参数classConfig:SECRET_KEY='Chapter7'SQLALCHEMY_TRACK_MODIFICATIONS=False#CKEditor配置项CKEDITOR_SERVE_LOCAL=TrueCKEDITOR_FILE_UPLOADER='portal.upload'UPLOADED_PATH=os.path.join(basedir,'uploads')#可在初始化时执行自定义操作@staticmethoddefinit_app(app):pass用户模块03用户模块在一个网站中,用户模块往往是最重要的基础模块之一,主要用于对用户进行认证,认证通过的用户才可以使用相关功能。用户模块需要实现注册、登录、注销,以及用户信息查看、编辑功能。由于文章留言需要用户登录后才能使用,所以用户模块也被文章模块所依赖。数据模型图如下所示。用户模块3.1数据模型定义根据数据模型设计图,可定义以下模型,代码如下(user/models.py)。代码较多,分用户模型和用户信息模型,用户模型代码如下。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')articles=db.relationship('ArticleModel',backref='author',uselist=True)comments=db.relationship('CommentModel',backref='author',uselist=True)用户模块根据数据模型设计图,可定义以下模型,代码如下(user/models.py)。代码较多,分用户模型和用户信息模型,用户信息模型代码如下。classUserInfoModel(db.Model,BaseModel):__tablename__='user_info'user:UserModeluser_id=db.Column(db.INTEGER,db.ForeignKey(UserModel.id),primary_key=True)nickname=db.Column(db.VARCHAR)phone=db.Column(db.VARCHAR)email=db.Column(db.VARCHAR)introduce=db.Column(db.TEXT)用户模块3.2注册功能注册功能相当简单,在之前的例子中也曾出现过,实际上就是“添加数据”的操作,只需要定义一个合适的表单类,然后将表单数据传递给用户模型进行添加即可。用户模块以下是表单类的代码实现(user/forms.py)。fromflask_wtfimportFlaskFormfromwtformsimportStringField,PasswordField,SubmitField,TextAreaField,BooleanFieldfromwtforms.validatorsimportDataRequired,Optional,Regexp,EmailclassRegisterForm(FlaskForm):username=StringField(label='用户名',validators=[DataRequired()])password=PasswordField(label='密码',validators=[DataRequired()])#此处使用11位数字的正则表达式检测手机号phone=StringField(label='手机号',validators=[Optional(),Regexp(r'\d{11}')])email=StringField(label='邮箱',validators=[Optional(),Email()])introduce=TextAreaField(label='自我介绍')submit=SubmitField(label='注册')用户模块以下是视图函数的代码实现(user/views.py)。#省略顶部引入代码..@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,info=UserInfoModel(phone=form.phone.data,email=form.email.data,introduce=roduce.data,),)try:dh.session.add(item)mit()flash('注册成功','success')returnredirect('/user/login')exceptExceptionase:flash('注册失败-%s'%e,'danger')returnabort(500)else:returnrender_template('common/form.html',form=form,title='用户注册')用户模块注册功能完成后的效果如下图所示。用户模块3.3登录、注销功能登录功能只需要根据表单中所提供的账号密码在数据库中查询,找到相应用户然后使用Flask-Login提供的login_user()建立会话即可,而注销则使用logout_user()清除会话数据即可(可参考书本6.4节)。用户模块以下是表单类的代码实现(user/forms.py)。classLoginForm(FlaskForm):username=StringField(label='用户名',validators=[DataRequired()])password=PasswordField(label='密码',validators=[DataRequired()])remember=BooleanField(label='记住登录状态')submit=SubmitField(label='登录')用户模块以下是视图函数的代码实现(user/views.py)。代码有点多,分两部分,一个是登录的视图函数,一个是登出的视图函数,登出的视图函数代码如下。@user.route('/logout')@login_requireddeflogout():iflogout_user():flash('注销成功','success')returnredirect('/user/login')@login_required装饰器:这是Flask-Login提供的装饰器,用于限制只有已登录的用户才能执行该路由。如果用户未登录,将被重定向到登录页面。iflogout_user():
logout_user()是Flask-Login提供的函数,用于注销当前用户。如果用户成功注销,它会返回True,否则返回False。用户模块登录的视图函数代码如下。@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:login_user(item,form.remember.data)flash('登录成功','success')next_path=request.args.get('next',request.path)returnredirect(next_path)else:raiseException('用户名或密码错误')exceptExceptionase:flash('登录失败-%s'%e,'danger')returnabort(500)else:returnrender_template('common/form.html',form=form,title='用户登录')让用户输入用户名和密码,并在数据库中查找匹配的用户记录,并根据查找结果进行相应的操作。用户模块登录功能完成后的效果如下图所示。用户模块3.4用户信息功能用户信息功能的实现也同样简单,实际上是“查询数据”与“修改数据”操作。查看用户信息只需要根据链接中所包含的“id”,查询相应用户信息,“注入变量”到模板页面中显示即可;而编辑用户信息则通过get_current_user()获取当前用户对象,然后修改数据即可。用户模块以下是表单类的代码实现(user/forms.py)。classUserInfoEditForm(FlaskForm):password=PasswordField(label='密码',description='留空则不改变密码')nickname=StringField(label='昵称')phone=StringField(label='手机号',validators=[Optional(),Regexp(r'\d{11}')])email=StringField(label='邮箱',validators=[Optional(),Email()])introduce=TextAreaField(label='自我介绍')submit=SubmitField(label='编辑')用户模块以下是视图函数的代码实现(user/views.py)。分查看和编辑两个视图函数来讲解。查看的视图函数代码如下。@user.route('/<int:id>')defview(id:int):try:item=UserModel.query.get(id)#type:UserModelifitemisnotNone:returnrender_template('user/view.html',item=item)else:returnabort(404)exceptExceptionase:flash('查看用户-%s'%e,'danger')returnabort(404)<int:id>是一个动态参数,表示用户的唯一标识符(通常是用户的ID)。这意味着我们可以通过访问/user/1、/user/2等路径来查看不同用户的详细信息。然后使用UserModel模型类的query属性,通过用户的ID从数据库中获取用户对象。再进行一个用户的匹配。根据匹配结果展示不同的内容。用户模块编辑的视图函数代码如下。由于内容有五项可编辑,这里就展示一项,其他大家根据书上代码补全即可。@user.route('/edit',methods=['GET','POST'])@login_requireddefedit():form=UserInfoEditForm()try:item=get_current_user()ifrequest.method=='POST'andform.validate_on_submit():ifform.password.data!='':item.password=form.password.data...省略代码dh.session.add(item)mit()flash('编辑成功','success')returnredirect('/user/edit')else:form.password.data=item.password...省略代码returnrender_template('common/form.html',form=form,title='用户信息编辑')exceptExceptionase:flash('编辑失败-%s'%e,'danger')returnabort(500)用户模块以下是用户信息查看模板页面的代码(user/view.html)。{#用户信息展示页#}{%extends'common/base.html'%}{%blocktitle%}查看用户{%endblock%}{%blockcontent_inner%}<tableclass="table"><tr><th>名称</th><td>{{.nicknameoritem.username}}</td></tr><tr><th>手机号</th><td>{{.phone}}</td></tr><tr><th>邮箱</th><td>{{.email}}</td></tr><tr><th>介绍</th><td>{{roduce}}</td></tr></table>{%endblock%}用户模块用户信息编辑页面的效果如下图所示。文章模块04文章模块文章模块是整个博客系统中最重要的模块。文章模块需要实现分页显示文章列表、按分类显示文章、搜索文章、文章留言几个核心功能。其列表显示功能被首页所依赖。数据模型图如下所示。文章模块4.1数据模型定义根据数据模型设计图,可定义以下模型,代码如下(article/models.py)。内容较多,分成ArticleModel、CommentModel、CategoryModel来演示。文章模块CategoryModel的代码如下所示。classCategoryModel(db.Model,BaseModel):__tablename__='category'
id=db.Column(db.INTEGER,primary_key=True,autoincrement=True)name=db.Column(db.VARCHAR,unique=True)
articles=db.relationship('ArticleModel',backref='category',uselist=True)#type:List[ArticleModel]文章模块ArticleModel的代码如下所示。classArticleModel(db.Model,BaseModel):__tablename__='article'author:UserModelcategory:CategoryModelid=db.Column(db.INTEGER,primary_key=True,autoincrement=True)author_id=db.Column(db.INTEGER,db.ForeignKey(UserModel.id),index=True)category_id=db.Column(db.INTEGER,db.ForeignKey(CategoryModel.id),index=True)title=db.Column(db.VARCHAR,index=True)content=db.Column(db.VARCHAR)create_time=db.Column(db.INTEGER,index=True)update_time=db.Column(db.INTEGER,index=True)view_count=db.Column(db.INTEGER,default=0)comments=db.relationship('CommentModel',backref='article',uselist=True,cascade='all')#type:List[CommentModel]文章模块CommentModel的代码如下所示。classCommentModel(db.Model,BaseModel):__tablename__='comment'author:UserModelarticle:ArticleModelid=db.Column(db.INTEGER,primary_key=True,autoincrement=True)author_id=db.Column(db.INTEGER,db.ForeignKey(UserModel.id),index=True)article_id=db.Column(db.INTEGER,db.ForeignKey(ArticleModel.id),index=True)content=db.Column(db.VARCHAR)create_time=db.Column(db.INTEGER,index=True)文章模块4.2文章生成宏指令在博客系统中,文章会在各个页面中显示,如首页、文章列表页、文章查看页等。此时便需要建立宏指令,对单个文章对象生成相应的HTML结构,或者根据多个文章对象组成的列表,生成文章列表的HTML结构,以便在各个页面中重复使用。文章模块以下是宏指令模板的代码实现(article/macros.html)。分为生成单个文章的HTML结构和生成多个HTML结构来讲解。生成单个文章的HTML结构的代码如下所示。{#生成单个文章的HTML结构#}
{%macrogenerate_article(article,is_full=True)%}...省略代码{%endmacro%}这是定义Jinja2宏的语法。generate_article是宏的名称,它接受两个参数:article是文章对象,is_full是一个可选参数,用于确定是否要显示完整的文章内容。文章模块生成单个文章的HTML结构的代码如下所示。<divclass="blog-post"><h2class="blog-post-title">{%ifis_full%}
{{article.title}}{%else%}<ahref="{{url_for('article.view',id=article.id)}}">
{{article.title}}
</a>{%endif%}</h2><pclass="blog-post-meta">于{{article.create_time|convert_time}}由
{%ifarticle.author%}<ahref="{{url_for('user.view',id=article.author_id)}}">{{article.author.username}}</a>{%else%}未知作者{%endif%}发布,最后更新于{{article.update_time|convert_time}}。阅读量:{{article.view_count}}</p><p>{%ifis_full%}{#默认情况下为确保内容安全,Jinja2会对数据进行HTML实体转义#}{{article.content|safe}}{%else%}{#使用truncate过滤器对内容进行截断#}{{article.content|safe|striptags|truncate}}{%endif%}</p></div>文章模块生成多个文章的HTML结构的代码如下所示。{#生成多个文章的HTML结构#}{%macrogenerate_articles(articles)%}{%forarticleinarticles%}{{generate_article(article,False)}}{%endfor%}{#如果内容为空,则添加提示信息#}{%ifnotarticles%}<p>emmmm,作者偷懒啦,并没有添加文章。</p>{%endif%}{%endmacro%}在前面定义好了这个宏,所以在后面写生成多个文章的HTML结构代码就可以直接拿来用,以简洁的方式生成多篇文章的HTML结构,包括文章标题、作者、发布时间等信息。只需要把generate_article(article,True)改成generate_article(article,False)就好。文章模块4.3最新文章列表、搜索最新文章列表与搜索功能实际上就是从数据库中获取相应数据并排序,最终展示出来的页面为分页文章列表(可参考书本5.3.6小节)。文章模块以下是视图函数的代码实现(article/views.py)。分为搜索视图和最新文章列表来展示。最新文章列表代码如下。fromflaskimportrender_template,request,redirectfromsqlalchemyimportor_frommonimportload_settingfrom.importarticlefrom.modelsimportArticleModel@article.route('/article/list_all')deflist_all():site_setting=load_setting('site_setting')#type:dictper_page=site_setting.get('list_all_per_page',5)pagination=ArticleModel.query.order_by(ArticleModel.id.desc()).paginate(None,per_page)returnrender_template('article/list.html',pagination=pagination,title='所有文章')pagination这行代码使用SQLAlchemy查询所有文章,并按照文章ID降序排列。然后,使用paginate函数分页获取文章,其中None表示获取所有文章,per_page表示每页显示的文章数量。分页后的结果存储在pagination变量中。list_all路由处理函数是一个文章列表页面,它加载了站点设置以确定每页显示的文章数量,然后查询文章并进行分页,最后渲染HTML模板以显示文章列表。文章模块搜索视图函数代码如下。@article.route('/article/search')defsearch():keyword=request.args.get('keyword','')ifkeyword=='':returnredirect('/')site_setting=load_setting('site_setting')#type:dictper_page=site_setting.get('search_per_page',5)pagination=ArticleModel.query.order_by(ArticleModel.id.desc()).filter(or_(ArticleModel.title.contains(keyword),ArticleModel.content.contains(keyword))).paginate(None,per_page)returnrender_template('article/list.html',pagination=pagination,title='"%s"的搜索结果'%keyword)pagination这行代码使用SQLAlchemy构建查询,查询文章标题或内容中包含搜索关键字的文章。然后,按照文章ID降序排列,并使用paginate函数分页获取搜索结果,其中None表示获取所有结果,per_page表示
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 《黄鹤楼记课件佳品》课件
- 《Python程序设计基础》课件 第3、4章 程序控制结构;Python 容器
- 《GB 32459-2015消防应急救援装备 手动破拆工具通 用技术条件》(2025版)深度解析
- 铁路工程安全技术石家庄铁路35课件
- 二手住房交易合同模板
- 电商代运营合作协议范本
- 内蒙古呼和浩特市实验教育集团2025届初三下学期5月月考英语试题试卷含答案
- 沈阳大学《CI原理与实务》2023-2024学年第二学期期末试卷
- 山东第一医科大学《诊断学2(医技)》2023-2024学年第一学期期末试卷
- 台州科技职业学院《国际金融B》2023-2024学年第二学期期末试卷
- 跟着电影去旅游知到智慧树章节测试课后答案2024年秋山东大学(威海)
- 《有机硅乳液防水剂》文本及编制说明
- 2024上海市招聘社区工作者考试题及参考答案
- 2021年高考物理试卷(江苏)(解析卷)
- 调度室副主任安全生产职责模版(3篇)
- 2025年中电科太力通信科技限公司招聘高频重点提升(共500题)附带答案详解
- 《设备房管理标准》课件
- 2025年内蒙古呼伦贝尔农垦拉布大林上库力三河苏沁农牧场有限公司招聘笔试参考题库附带答案详解
- 呼吸机故障应急演练
- 轻钢结构大棚施工组织设计方案
- 垃圾分类垃圾箱绿色公益宣传
评论
0/150
提交评论