📜  MVC框架-高级示例(1)

📅  最后修改于: 2023-12-03 15:17:45.289000             🧑  作者: Mango

MVC框架-高级示例

在Web开发中,MVC模式被广泛应用于构建应用程序。MVC是Model-View-Controller的缩写,是一种将应用程序分解为三个核心组件:模型、视图和控制器的设计模式。每个组件负责处理特定的应用程序功能:

  • 模型:负责管理应用程序的数据和状态。
  • 视图:负责展示数据和用户接口。
  • 控制器:负责处理用户输入,并调用模型和视图的相应方法。

MVC框架提供了一种结构良好、可扩展、易于维护的方式来组织和管理Web应用程序。在本文中,我们将介绍一个高级示例,展示如何使用MVC框架构建一个功能完整的Web应用程序。

技术栈

在本示例中,我们将使用一下技术栈:

  • Python 3.7
  • Flask 1.1.1
  • SQLAlchemy 1.3.15
  • Jinja2 2.11.1
  • Bootstrap 4.4.1
设计模式

我们将使用以下设计模式来构建示例Web应用程序:

  • MVC模式:用于组织和管理应用程序的组件。
  • 工厂模式:用于创建和管理应用程序的核心组件。
  • 建造者模式:用于创建和管理数据模型和视图组件。
  • 依赖注入模式:用于解耦和管理组件之间的依赖关系。
应用程序结构

我们的应用程序将包含以下基本组件:

  • 数据模型:使用SQLAlchemy创建,负责管理应用程序的数据和状态。
  • 视图:使用Jinja2模板引擎构建,负责展示数据和用户接口。
  • 控制器:使用Flask路由机制汇总,负责处理用户输入,并调用模型和视图的相应方法。
  • 工厂:负责创建和管理应用程序的核心组件。
  • 依赖注入容器:负责解耦和管理组件之间的依赖关系。

应用程序的完整结构如下所示:

app/
    static/
        css/
            style.css
        js/
            app.js
    templates/
        base.html
        home.html
        login.html
        register.html
    __init__.py
    config.py
    factory.py
    controller.py
    model/
        __init__.py
        user.py
    service/
        __init__.py
        user_service.py
        auth_service.py
    repository/
        __init__.py
        user_repository.py
        auth_repository.py
    utils/
        __init__.py
        decorators.py
数据模型

我们的应用程序将有两个数据模型:User和Auth。User模型将负责管理应用程序的用户数据,而Auth模型将负责管理用户的身份验证信息。

# app/model/user.py

from app import db


class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    password = db.Column(db.String(80), nullable=False)

    def __repr__(self):
        return f'<User {self.username}>'
# app/model/auth.py

from app import db


class Auth(db.Model):
    __tablename__ = 'auth'
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    token = db.Column(db.String(80), nullable=False)

    def __repr__(self):
        return f'<Auth {self.token}>'
视图

我们的应用程序将有四个视图:Home、Login、Register和Logout。

<!-- app/templates/base.html -->

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{% block title %}Untitled{% endblock %}</title>
    <link href="{{ url_for('static', filename='css/style.css') }}" rel="stylesheet">
  </head>
  <body>
    <nav>
      <ul>
        <li><a href="{{ url_for('home') }}">Home</a></li>
        {% if current_user %}
          <li><a href="{{ url_for('logout') }}">Logout</a></li>
        {% else %}
          <li><a href="{{ url_for('login') }}">Login</a></li>
          <li><a href="{{ url_for('register') }}">Register</a></li>
        {% endif %}
      </ul>
    </nav>
    <main>
      {% block content %}{% endblock %}
    </main>
    <script src="{{ url_for('static', filename='js/app.js') }}"></script>
  </body>
</html>
<!-- app/templates/home.html -->

{% extends 'base.html' %}

{% block title %}Home{% endblock %}

{% block content %}
  <h1>Welcome to our application!</h1>
{% endblock %}
<!-- app/templates/login.html -->

{% extends 'base.html' %}

{% block title %}Login{% endblock %}

{% block content %}
  {% if error %}
    <div class="alert">{{ error }}</div>
  {% endif %}
  <form action="{{ url_for('login') }}" method="POST">
    <label>Username</label>
    <input type="text" name="username" required>
    <label>Password</label>
    <input type="password" name="password" required>
    <input type="submit" value="Login">
  </form>
{% endblock %}
<!-- app/templates/register.html -->

{% extends 'base.html' %}

{% block title %}Register{% endblock %}

{% block content %}
  {% if error %}
    <div class="alert">{{ error }}</div>
  {% endif %}
  <form action="{{ url_for('register') }}" method="POST">
    <label>Username</label>
    <input type="text" name="username" required>
    <label>Email</label>
    <input type="email" name="email" required>
    <label>Password</label>
    <input type="password" name="password" required>
    <input type="submit" value="Register">
  </form>
{% endblock %}
控制器

我们的应用程序将有四个路由:/、/login、/register和/logout。控制器将负责处理这些路由,并调用相应的模型和视图方法。

# app/controller.py

from flask import Blueprint, render_template, request, redirect, url_for, session
from app.factory import create_app
from app.service.user_service import UserService
from app.service.auth_service import AuthService

bp = Blueprint('site', __name__)


@bp.route('/')
def home():
    return render_template('home.html')


@bp.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        user = UserService().authenticate(username, password)
        if user is None:
            return render_template('login.html', error='Invalid username or password')
        session['user_id'] = user.id
        return redirect(url_for('site.home'))
    return render_template('login.html')


@bp.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':
        username = request.form['username']
        email = request.form['email']
        password = request.form['password']
        user_service = UserService()
        user = user_service.create(username=username, email=email, password=password)
        if user is None:
            return render_template('register.html', error='Unable to register user')
        auth_service = AuthService()
        auth_token = auth_service.create_token(user.id)
        session['user_id'] = user.id
        return redirect(url_for('site.home'))
    return render_template('register.html')


@bp.route('/logout')
def logout():
    session.pop('user_id', None)
    return redirect(url_for('site.home'))
工厂

我们将使用工厂模式来创建和管理应用程序的核心组件。我们的工厂将注册所有视图、控制器和数据模型,并返回一个Flask程序实例。

# app/factory.py

from flask import Flask
from app.config import Config
from app.model import User, Auth
from app.controller import bp
from app.repository import UserRepository, AuthRepository
from app.service import UserService, AuthService
from app.utils.decorators import requires_auth


def create_app(config=Config):
    app = Flask(__name__)
    app.config.from_object(config)

    # Register controllers
    app.register_blueprint(bp)

    # Register data models
    db.init_app(app)
    with app.app_context():
        db.create_all()

    # Register repositories
    user_repository = UserRepository(User)
    auth_repository = AuthRepository(Auth)

    # Register services
    user_service = UserService(user_repository)
    auth_service = AuthService(user_repository, auth_repository)

    # Register decorators
    app.before_request(requires_auth)

    return app
依赖注入容器

我们将使用依赖注入容器来解耦和管理组件之间的依赖关系。我们的容器将提供一个register方法,用于向容器注册新组件,以及get方法,用于检索已注册的组件。在本示例中,我们将使用简单的字典作为我们的容器。

# app/utils/container.py

class Container:
    def __init__(self):
        self._registry = {}

    def register(self, key, value):
        self._registry[key] = value

    def get(self, key):
        return self._registry.get(key)
启动应用程序

现在,我们可以启动应用程序并访问/路由来进入我们的Web应用程序。对于本示例,我们将执行以下命令:

export FLASK_APP=app
export FLASK_ENV=development
flask run

现在,我们可以在我们的浏览器中访问http://localhost:5000,并查看我们的应用程序。