利用Flask-AppBuilder 快速构建Web后台管理应用

快速构建 Web 后台管理应用

一、实验介绍

1.1 实验内容

本次实验将学习如何利用Flask-AppBuilder快速生成项目结构,以及基本配置。

1.2 实验知识点

  • 认识Flask-AppBuilder
  • 使用命令行接口快速生成项目结构
  • Flask-AppBuilder的基本配置
  • Flask-AppBuilder BaseView的基本用法
  • Flask-AppBuilder ModelView的基本用法

1.3 实验环境

  • python 3.5.2
  • Xfce终端

1.4 适合人群

本课程难度为初级,适合具有Python、Flask、SQLAlchemy基础的同学。

二、实验基础

2.1 Flask-AppBuilder介绍

Flask-AppBuilder是基于Flask实现的一个用于快速构建Web后台管理系统的简单的框架。主要用于解决构建Web后台管理系统时避免一些重复而繁琐的工作,提高项目完成时间,它可以和 Flask/Jinja2自定义的页面进行无缝集成,并且可以进行高级的配置。这个框架还集成了一些CSS和JS库,包括以下内容:

  • Google charts CSS and JS
  • BootStrap CSS and JS
  • BootsWatch Themes
  • Font-Awesome CSS and Fonts

三、开发准备

3.1 安装Flask-AppBuilder

在正式开发之前,首先我们要安装Flask-AppBuilder。打开XFce终端,输入如下命令:

shiyanlou:~/ $ sudo pip3 install flask-appbuilder

安装完成之后会自动为我们安装所需依赖,效果如下图所示:

此处输入图片的描述

3.2 创建项目目录

现在我们创建一个目录,之后的操作都在这个目录中进行:

shiyanlou:~/ $ mkdir project
shiyanlou:~/ $ cd project

此处输入图片的描述

四、项目文件结构

我们的项目结构如下:

此处输入图片的描述

其中app目录下存放我们Web应用的相关脚本文件,babel存放国际化相关配置和导出的翻译字符串。根目录下则存放着Web应用的配置脚本和主脚本文件。

五、实验步骤

5.1 生成项目文件

利用Flask-AppBuilder的命令行管理工具(fabmanager)可以很方便的生成项目的基本目录结构以及一些相关的文件和代码:

shiyanlou:project/ $ fabmanager create-app
Your new app name: ums
Your engine type, SQLAlchemy or MongoEngine [SQLAlchemy]:
Downloaded the skeleton app, good coding!

命令执行后会提示我们输入app名称,随便起一个名字就可以。接下来要求选择orm引擎类型,默认是SQLAlchemy。也可以选择MongoEngine,但必须安装'flask-mongoengine',这里我们使用'SQLAlchemy',直接回车就可以了。效果如下图所示:

项目结构文件放在实验楼的服务器中,如果有的同学无法连接外网,则可以直接从以下链接下载:

wget http://labfile.oss.aliyuncs.com/courses/870/Flask-AppBuilder-Skeleton-master.zip

之后在当前目录下解压zip文件,并将文件夹名字改为项目名称:

unzip Flask-AppBuilder-Skeleton-master.zip
mv Flask-AppBuilder-Skeleton-master ums

此处输入图片的描述

现在我们的项目结构就自动生成了。同学们可以看一下自己的目录结构,对比一下是不是和上面的结构一样。

5.2 创建管理用户

fabmanager还可以快速的创建管理用户:

shiyanlou:project/ $ cd ums
shiyanlou:ums/ $ fabmanager create-admin
Username [admin]:
User first name [admin]:
User last name [user]:
Email [admin@fab.org]:
Password:
Repeat for confirmation:

此处输入图片的描述

至此,fabmanager已经替我们完成了一个基本的Web后台管理系统,稍后我们将运行生成的代码。

5.3 运行脚本

我们可以通过fabmanager来运行生成的脚本:

shiyanlou:ums/ $ fabmanager run

也可以通过Python解释器来运行项目根目录下的run.py文件:

shiyanlou:ums/ $ python3 run.py

此处输入图片的描述

之后开发服务器将在http://localhost:8080 运行。打开火狐浏览器,在地址栏输入http://localhost:8080,就会看到Flask-AppBuilder自动为我们生成的页面。用之前我们生成的管理员账户登陆就可以进入到Web的后台管理界面:

此处输入图片的描述

在导航栏中Flask-AppBuilder为我们自动生成了Security菜单,子菜单中包括了一些用户、角色、视图、菜单、权限等相关操作。

导航栏右侧还包含了语言菜单,可以选择7个国家的语言,不过当我们自己增加代码后,就需要使用Flask-Babel进行国际化翻译,在课程的后面我会详细讲解。

5.4 基本配置

Flask-AppBuilder具有高度的灵活性,可以通过配置项目根目录下config.py文件来实现我们想要的需求,下面讲解一些常用的配置,更详细的内容请参考官方文档(Flask-AppBuilder基本配置):

5.4.1 数据库配置

如果使用SQLAlchemy可以通过配置SQLALCHEMY_DATABASE_URI的值来指定数据库连接。如果使用Mongdb可以配置MONGODB_SETTINGS的值。默认使用Sqlite数据库,SQLALCHEMY_DATABASE_URI的值为'sqlite:///' + os.path.join(basedir, 'app.db')

5.4.1 认证方式配置

Flask-Appbuilder可以通过配置AUTH_TYPE来指定应用使用的认证方式。

AUTH_TYPE = 0 | 1 | 2 | 3 | 4AUTH_TYPE = AUTH_OID, AUTH_DB,AUTH_LDAP, AUTH_REMOTE AUTH_OAUTH。默认使用AUTH_DB的认证方式。

5.4.2 主题配置

Flask-AppBuilder集成了bootwatch,只需要配置APP_THEME的值就可以改变应用的主题风格。下面是config.py文件中可供选择的主题:

#APP_THEME = "bootstrap-theme.css"  # default bootstrap
#APP_THEME = "cerulean.css"
#APP_THEME = "amelia.css"
#APP_THEME = "cosmo.css"
#APP_THEME = "cyborg.css"  
#APP_THEME = "flatly.css"
#APP_THEME = "journal.css"
#APP_THEME = "readable.css"
#APP_THEME = "simplex.css"
#APP_THEME = "slate.css"   
APP_THEME = "spacelab.css"
#APP_THEME = "united.css"
#APP_THEME = "yeti.css"

把不使用的主题注释掉,这里我们使用APP_THEME = "spacelab.css"这项,重新运行开发服务器看一下效果:

此处输入图片的描述

可以看到页面的主题风格变了,是不是很方便呢?至此,我们这节实验的内容就讲完了。

5.5 基础视图

5.5.1 BaseView

BaseView是视图中的基类,所有的视图都继承自它。当我们定义一个继承自BaseView的子类时,BaseView自动将我们用@exposed修饰的urls注册为Flask中的蓝图。

我们可以通过BaseView来实现自定义页面,并添加到菜单上或者通过一个连接来访问它。这里需要注意,作为路由的方法一定要用@exposed修饰,如果需要保护的路由则需要额外使用@has_access修饰。

现在我们来看一个小例子,通常我们使用F.A.B.框架都使用自动生成的项目结构,这样我们只需要在app目录下的views.py 文件中修改代码即可。下面我们来看一个简单例子:

from flask_appbuilder import AppBuilder, expose, BaseView
from app import appbuilder

class MyView(BaseView):
    route_base = "/myview"

    @expose('/hello/')
    def hello(self):
        return 'Hello World!'

    @expose('/message/<string:msg>')
    def message(self, msg):
        msg = 'Hello %s' % (msg)
        return msg

appbuilder.add_view_no_menu(MyView())

在这个代码中我们在ums/app/views.py中定义了一个BaseView的子类,同时注册了两个路由,在最后一行添加了视图,但是没有创建菜单。其中route_base用于自定义相对路径的url。我们可以通过如下urls来测试我们实现的两个路由:

这里需要注意一点就是在访问urls的时候,一定要将视图的名字作为路由的前缀!

此处输入图片的描述

此处输入图片的描述

你会发现,这些方法都是公开的,为了只让登陆用户可以看到,我们为上面的两个方法用@has_access修饰,修改ums/app/views.py

from flask_appbuilder import AppBuilder, expose, BaseView
from app import appbuilder
from flask_appbuilder import has_access

class MyView(BaseView):
    default_view = 'hello'

    @expose('/hello/')
    @has_access
    def hello(self):
        return 'Hello World!'

    @expose('/message/<string:msg>')
    @has_access
    def message(self, msg):
        msg = 'Hello %s' % (msg)
        return msg

appbuilder.add_view_no_menu(MyView())

再次尝试访问上面的urls,发现会无法访问,并重定向到了登陆页面,登陆之后就可以正常访问了。

下面我们再来看一下将视图添加到菜单,并渲染一个Jinja2模板的例子:

在项目根目录下的/app/templates/目录中,创建一个index.html,并添加如下代码:

{% extends "appbuilder/base.html" %}
{% block content %}
    <h1>{{ msg }}</h1>
{% endblock %}

编辑项目根目录中的/app/views.py,给出 views.py 修改后的完整代码:

from flask_appbuilder import AppBuilder, expose, BaseView
from app import appbuilder
from flask_appbuilder import has_access

class MyView(BaseView):
    default_view = 'hello'

    @expose('/hello/')
    @has_access
    def hello(self):
        return 'Hello World!'

    @expose('/message/<string:msg>')
    @has_access
    def message(self, msg):
        msg = 'Hello %s' % (msg)
        return msg

    @expose('/welcome/<string:msg>')
    @has_access
    def welcome(self, msg):
        msg = 'Hello %s' % (msg)
        return self.render_template('index.html',msg = msg)

appbuilder.add_view(MyView, "Hello", category='My View')
appbuilder.add_link("Message", href='/myview/message/john', category='My View')
appbuilder.add_link("Welcome", href='/myview/welcome/student', category='My View')

在这个例子中我们添加了一个方法,并通过render_template渲染一个自定义模板,可以很方便的定制页面。这里需要注意一点,在模板中我们继承了appbuilder/base.html这个基模板,它由F.A.B.提供,目的是为了保持风格的一致性

通过add_viewadd_link这两个方法,将在菜单中生成链接访问我们定义的路由,效果如下图所示:

此处输入图片的描述

此处输入图片的描述

5.6 模型视图

5.6.1 定义数据库模型

这个例子需要在ums/app/models.py中定义两个表,分别是存储联系人详细信息的Contacts表和用来存储分组信息的ContactGroup表。定义表的时候需要继承Model类,这个类和Flask-SQLALchemydb.Model是一样的。

  • 定义ContactGroup表

ContactGroup表中需要定义两个字段,分别是作为主键的id字段和非空唯一的分组名称name字段。编辑ums/app/models.py文件:

from sqlalchemy import Column, Integer, String, ForeignKey, Date
from sqlalchemy.orm import relationship
from flask_appbuilder import Model

class ContactGroup(Model):
    id = Column(Integer, primary_key=True)
    name = Column(String(50), unique = True, nullable=False)

    def __repr__(self):
        return self.name
  • 定义Contact表

Contact表中除了需要定义联系人的姓名、地址、生日、电话号、手机号之外,还需要关联ContactGroup表的id值作为外键,用来表示多对一的关系。ums/app/models.py文件,添加代码:

class Contact(Model):
    id = Column(Integer, primary_key=True)
    name =  Column(String(150), unique = True, nullable=False)
    address =  Column(String(564))
    birthday = Column(Date)
    personal_phone = Column(String(20))
    personal_cellphone = Column(String(20))
    contact_group_id = Column(Integer, ForeignKey('contact_group.id'))
    contact_group = relationship("ContactGroup")

    def __repr__(self):
        return self.name

这儿给出 ums/app/models.py 的完整文件:

from sqlalchemy import Column, Integer, String, ForeignKey, Date
from sqlalchemy.orm import relationship
from flask_appbuilder import Model

class ContactGroup(Model):
    id = Column(Integer, primary_key=True)
    name = Column(String(50), unique = True, nullable=False)

    def __repr__(self):
        return self.name

class Contact(Model):
    id = Column(Integer, primary_key=True)
    name =  Column(String(150), unique = True, nullable=False)
    address =  Column(String(564))
    birthday = Column(Date)
    personal_phone = Column(String(20))
    personal_cellphone = Column(String(20))
    contact_group_id = Column(Integer, ForeignKey('contact_group.id'))
    contact_group = relationship("ContactGroup")

    def __repr__(self):
        return self.name

5.6.2 定义视图

通过继承ModelView类可以实现我们自定义的视图,F.A.B.可以针对我们定义好的数据库模型生成创建、删除、更新和显示的功能。

视图类中必须使用定义好的数据库模型初始化datamodel属性。

  • 定义Contact模型的视图

由于Contacts模型中包含很多字段,所以在联系人视图中我们使用label_columnslist_columnsshow_fieldsets属性来定制联系人视图。下面来看一下这三个属性的用法:

  • label_columns:用于定义列的显示名称。
  • list_columns:用于定义视图中要显示的字段。
  • show_fieldsets:用于视图中显示页面中显示的内容,还可以单独定义 add_fieldsetsedit_fieldsets页面中的内容。编辑 ums/app/views.py 文件:
from flask_appbuilder import ModelView
from flask_appbuilder.models.sqla.interface import SQLAInterface
from .models import ContactGroup, Contact
from app import appbuilder, db

class ContactModelView(ModelView):
    datamodel = SQLAInterface(Contact)

    label_columns = {'contact_group':'Contacts Group'}
    list_columns = ['name','personal_cellphone','birthday','contact_group']

    show_fieldsets = [
                        (
                            'Summary',
                            {'fields':['name','address','contact_group']}
                        ),
                        (
                            'Personal Info',
                            {'fields':['birthday','personal_phone','personal_cellphone'],'expanded':False}
                        ),
                     ]
# 在联系人组视图中,我们使用related_views来关联联系人视图,F.A.B.将自动处理他们之间的关系。
class GroupModelView(ModelView):
    datamodel = SQLAInterface(ContactGroup)
    related_views = [ContactModelView]

# 现在我们就差最后一步工作就要完成本次实验了。
# 首先使用db.create_all()根据数据库模型创建表,然后再将视图添加到菜单。

db.create_all()
appbuilder.add_view(GroupModelView,
                    "List Groups",
                    icon = "fa-address-book-o",
                    category = "Contacts",
                    category_icon = "fa-envelope")
appbuilder.add_view(ContactModelView,
                    "List Contacts",
                    icon = "fa-address-card-o",
                    category = "Contacts")

在添加菜单的时候,我们指定了icon的值,它来源于Font-Awesome,我们使用图标的时候可以在Font-Awesome官网中查找喜欢的图标。

现在我们在项目根目录中执行如下命令:

shiyanlou:ums/ $ fabmanager run

然后打开Firefox浏览器,在地址栏中输入http://127.0.0.1:8080/,之后登陆就可以看到我们实现的联系人Web应用了,效果如图所示:

此处输入图片的描述

此处输入图片的描述

此处输入图片的描述

此处输入图片的描述

5.7 Flask-AppBuilder实现学校后台管理

这部分内容我们将通过前面的学习来实现一个简单的学校后台管理系统

5.7.1 定义数据库模型

在定义数据库模型之前我们先导入需要的包或模块,编辑app/models.py

from flask import Markup, url_for, g
from flask.ext.appbuilder import Model
from flask.ext.appbuilder.models.mixins import AuditMixin, FileColumn, ImageColumn
from sqlalchemy import Table, Column, Integer, String, ForeignKey, Date, Float, Text
from sqlalchemy.orm import relationship
from flask_appbuilder.models.decorators import renders
from flask_appbuilder.models.sqla.filters import FilterStartsWith, FilterEqualFunction
  • 定义学院管理模型

在学院模型中,我们将定义一个代表id的主键和代表学院名称的name字段。

#学院
class College(Model):
    id = Column(Integer, primary_key=True)
    name = Column(String(50), unique=True, nullable=False)

    def __repr__(self):
        return self.name
  • 定义部门管理模型

在部门模型中,我们将定义一个代表id的主键和代表部门名称的name字段。

#部门
class Department(Model):
    id = Column(Integer, primary_key=True)
    name = Column(String(50), unique=True, nullable=False)

    def __repr__(self):
        return self.name
  • 定义专业管理模型

在专业模型中,我们将定义一个代表id的主键和代表专业名称的name字段。

#专业
class Major(Model):
    id = Column(Integer, primary_key=True)
    name = Column(String(50), unique=True, nullable=False)

    def __repr__(self):
        return self.name
  • 定义班级管理模型

在班级模型中,我们将定义一个代表id的主键和代表班级名称的name字段。

#班级
class MClass(Model):
    __tablename__ = 'mclass'
    id = Column(Integer, primary_key=True)
    name = Column(String(50), unique=True, nullable=False)

    def __repr__(self):
        return self.name
  • 定义教师与学生管理模型

在定义教师数据模型和学生数据模型时除了定义一些教师和学生的个人信息之外,由于教师和学生都和上面定义好的模型之间存在着多对一的关系,所以在教师和学生中都关联了上面的相关模型。

这里还定义了一个联接表用来处理学生和教师之间的多对多关系,它将多对多关系划分为两个一对多关系。

#教师
class Teacher(Model):
    id = Column(Integer, primary_key=True)
    work_num = Column(String(30), unique=True, nullable=False)  #工号
    name = Column(String(50), nullable=False)
    college_id = Column(Integer, ForeignKey('college.id'), nullable=False)  #学院
    college = relationship("College")
    department_id = Column(Integer, ForeignKey('department.id'), nullable=False)    #部门
    department = relationship("Department")
    tel_num = Column(String(30), unique=True, nullable=False)   #电话号
    birthday = Column(Date)

    def __repr__(self):
        return self.name

assoc_teacher_student = Table('teacher_student', Model.metadata,
                                      Column('id', Integer, primary_key=True),
                                      Column('teacher_id', Integer, ForeignKey('teacher.id')),
                                      Column('student_id', Integer, ForeignKey('student.id'))
)

class Student(Model):
    id = Column(Integer, primary_key=True)
    stu_num = Column(String(30), unique=True, nullable=False)  #学号
    name = Column(String(50), nullable=False)  
    college_id = Column(Integer, ForeignKey('college.id'), nullable=False)
    college = relationship("College")
    major_id = Column(Integer, ForeignKey('major.id'), nullable=False)
    major = relationship("Major")
    mclass_id = Column(Integer, ForeignKey('mclass.id'), nullable=False)
    mclass = relationship("MClass")
    teachers = relationship('Teacher', secondary=assoc_teacher_student, backref='student')
    tel_num = Column(String(30), unique=True, nullable=False)   #电话号
    birthday = Column(Date)

    def __repr__(self):
        return self.name
  • 这里给出 ums/app/models.py 的完整文件
from flask import Markup, url_for, g
from flask.ext.appbuilder import Model
from flask.ext.appbuilder.models.mixins import AuditMixin, FileColumn, ImageColumn
from sqlalchemy import Table, Column, Integer, String, ForeignKey, Date, Float, Text
from sqlalchemy.orm import relationship
from flask_appbuilder.models.decorators import renders
from flask_appbuilder.models.sqla.filters import FilterStartsWith, FilterEqualFunction

#学院
class College(Model):
    id = Column(Integer, primary_key=True)
    name = Column(String(50), unique=True, nullable=False)

    def __repr__(self):
        return self.name

#部门
class Department(Model):
    id = Column(Integer, primary_key=True)
    name = Column(String(50), unique=True, nullable=False)

    def __repr__(self):
        return self.name

#专业
class Major(Model):
    id = Column(Integer, primary_key=True)
    name = Column(String(50), unique=True, nullable=False)

    def __repr__(self):
        return self.name

#班级
class MClass(Model):
    __tablename__ = 'mclass'
    id = Column(Integer, primary_key=True)
    name = Column(String(50), unique=True, nullable=False)

    def __repr__(self):
        return self.name

#教师
class Teacher(Model):
    id = Column(Integer, primary_key=True)
    work_num = Column(String(30), unique=True, nullable=False)  #工号
    name = Column(String(50), nullable=False)
    college_id = Column(Integer, ForeignKey('college.id'), nullable=False)  #学院
    college = relationship("College")
    department_id = Column(Integer, ForeignKey('department.id'), nullable=False)    #部门
    department = relationship("Department")
    tel_num = Column(String(30), unique=True, nullable=False)   #电话号
    birthday = Column(Date)

    def __repr__(self):
        return self.name

assoc_teacher_student = Table('teacher_student', Model.metadata,
                                      Column('id', Integer, primary_key=True),
                                      Column('teacher_id', Integer, ForeignKey('teacher.id')),
                                      Column('student_id', Integer, ForeignKey('student.id'))
)

# 学生
class Student(Model):
    id = Column(Integer, primary_key=True)
    stu_num = Column(String(30), unique=True, nullable=False)  #学号
    name = Column(String(50), nullable=False)
    college_id = Column(Integer, ForeignKey('college.id'), nullable=False)
    college = relationship("College")
    major_id = Column(Integer, ForeignKey('major.id'), nullable=False)
    major = relationship("Major")
    mclass_id = Column(Integer, ForeignKey('mclass.id'), nullable=False)
    mclass = relationship("MClass")
    teachers = relationship('Teacher', secondary=assoc_teacher_student, backref='student')
    tel_num = Column(String(30), unique=True, nullable=False)   #电话号
    birthday = Column(Date)

    def __repr__(self):
        return self.name

5.7.2 定义视图

  • 编辑修改 ums/app/views.py 文件
# 导入相关的包和模块

from flask import render_template, flash
from flask.ext.appbuilder.models.sqla.interface import SQLAInterface
from flask.ext.appbuilder import ModelView, MultipleView, MasterDetailView
from app import appbuilder, db
from flask_appbuilder import AppBuilder, expose, BaseView, has_access, SimpleFormView
from flask_babel import lazy_gettext as _
from flask_appbuilder.charts.views import DirectByChartView

from wtforms import Form, StringField
from wtforms.validators import DataRequired
from flask_appbuilder.fieldwidgets import BS3TextFieldWidget
from flask_appbuilder.forms import DynamicForm

from flask_appbuilder.widgets import ListThumbnail
from .models import College, Department, Major, MClass, Teacher, Student
from flask_appbuilder.actions import action
from wtforms.ext.sqlalchemy.fields import QuerySelectField
from flask_appbuilder.fieldwidgets import BS3TextFieldWidget, Select2Widget

# 这里定义了学院、部门、专业、班级、教师和学生的相关视图。
# 代码比较简单,直接关联我们定义好的模型就可以了,代码如下:

class CollegeView(ModelView):
    datamodel = SQLAInterface(College)

class DepartmentView(ModelView):
    datamodel = SQLAInterface(Department)

class MajorView(ModelView):
    datamodel = SQLAInterface(Major)

class MClassView(ModelView):
    datamodel = SQLAInterface(MClass)

class TeacherView(ModelView):
    datamodel = SQLAInterface(Teacher)

class StudentView(ModelView):
    datamodel = SQLAInterface(Student)

db.create_all()

# 这里将6个视图作为子菜单添加到了`School Manage`菜单中:

appbuilder.add_view(CollegeView, "College", icon="gear", category='School Manage',)
appbuilder.add_view(DepartmentView, "Department", icon="gear",category='School Manage')
appbuilder.add_view(MajorView, "Major", icon="gear", category='School Manage')
appbuilder.add_view(MClassView, "MClass", icon="gear", category='School Manage')
appbuilder.add_view(TeacherView, "Teacher", icon="gear",category='School Manage')
appbuilder.add_view(StudentView, "Student", icon="gear",category='School Manage')

5.7.3 更改默认页面

通常在登陆之后我们都希望显示一些表示欢迎或者提示的内容,而F.A.B.自定义默认页面也非常简单。

首先我们在模板存放目录修改文件ums/app/templates/index.html

{% extends "appbuilder/base.html" %}
{% block content %}
<div class="jumbotron">
  <div class="container">
    <h1> Welcome to XX school management system! </h1>
  </div>
</div>
{% endblock %}

接下来在ums/app/目录下新建一个index.py文件,并在文件中定义一个继承自IndexView的类:

from flask_appbuilder import IndexView

class MyIndexView(IndexView):
    index_template = 'index.html'

最后我们在ums/app/__init__.py文件中,在AppBuilder初始化的时候,指定indexview的值。由于这个文件中已经自动生成了一些代码,只在如下代码做相关改动即可:

from app.index import MyIndexView
appbuilder = AppBuilder(app, db.session, indexview=MyIndexView)

5.7.4 运行效果

现在我们在项目根目录中执行如下命令:

shiyanlou:ums/ $ fabmanager run

然后打开Firefox浏览器,在地址栏中输入http://127.0.0.1:8080/,之后登陆就可以看到我们实现的学校后台管理系统了,效果如图所示:

此处输入图片的描述

此处输入图片的描述

此处输入图片的描述

此处输入图片的描述

六、实验总结

通过这节实验的学习,我们应该掌握如下知识点:

  • 如何使用fabmanager快速生成项目结构
  • 如何配置Web应用
  • 如何更改主题样式
  • 如何通过expose修饰器定义路由。
  • 如何通过has_access修饰器来保护路由。
  • 如何渲染模板。
  • 如何将路由添加到菜单。
  • 如何定义数据库和数据库模型
  • 如何定义针对数据库模型相关联的视图
  • 如何实现自定义默认页面

七、参考链接

发布了12 篇原创文章 · 获赞 218 · 访问量 63万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览