Please enable Javascript to view the contents

Django 浅析与工程目录结构实践

 ·  ☕ 7 分钟

1. Django是什么

Django是一套由Python完成的Web开发框架,起初被开发者用于管理以新闻内容为主的网站,2005年7月在BSD许可证发布下开源,2008年6月17日正式成立基金会。与Django一起的Python Web框架还有,Tornado和Flask。

Django提供全套的解决方案,包括但不限于,中间件、缓存、Session、ORM、Auth、后台管理、模板、URL映射等。

2. Django的工作原理

首先介绍一下Django的MTV模式:

  • M 代表模型(Model): 负责业务对象和数据库的关系映射(ORM)。
  • T 代表模板 (Template): 负责如何把页面展示给用户(html)。
  • V 代表视图(View): 负责业务逻辑,并在适当时候调用Model和Template。

那么,当一个http请求发送给一个Django网站时,Django是如何处理的呢?

  1. 中间件收到一个http请求,对请求进行处理。例如,登录状态,参数的转义,合法性验证等。整个网站处理逻辑中通用的部分可以放在中间件处理。

  2. Django在URL分发器中通过正则规则匹配http请求的路径,将请求转发给view函数处理。例如,请求为: http://localhost:8000/index/ ,会匹配到配置的URL路由url(r’^index/$’, ‘myapp.views.index’),意思也就是说,指定了myapp包的views.py文件中的index函数来处理这一类请求。

  3. views中的函数index,处理这个请求,调用Model层提供的方法,获取数据。

  4. MTV模型中对数据的操作是在Model层,只需要让Model的Manager类继承Django的model.Manager类,可以非常方便的对view函数提供接口。Model的Manager类中的方法收到View层的调用后,使用Django封装的ORM,利用对象操作数据库,然后将结果返回给View层。

  5. View层的index函数获取到Model层传回的数据对象后,以参数的形式传给Template。

  6. Template中描述的是数据展示的格式,比如一页多少文章,可不可以编辑等。可以使用for、if等常见的语法格式。Template层收到Model的数据对象之后,将数据渲染成html格式,然后返回给中间件处理。

  7. 中间件会对Response进行最后的处理。例如:静态文件的目录URL、Media文件的URL、还有每个页面都会用到的一些变量,都可以通过中间件写入返回的html中。

  8. 返回给浏览器,呈现给用户。

3. Django的目录结构

3.1 创建Django Project

安装好Django后,使用如下命令创建一个Django项目myproject

1
django-admin startproject myproject

  1. manage.py脚本:用于管理Django站点

  2. 目录myproject

. __init__.py: 用来告诉python,当前目录是python模块

. settings.py: 包含项目的所有配置参数

. urls.py: URL根配置

. wsgi.py: 内置runserver命令的WSGI应用配置

3.2 创建Django App

执行命令,在工程目录下创建两个Django App。

1
2
3
cd myproject
django-admin startapp myapp1
django-admin startapp myapp2

然后,在myproject目录下settings.py文件中的INSTALLED_APPS添加上‘myapp1’,‘myapp2’,以便django给这些app建立数据库表;在myproject目录下urls.py文件中import myapp1, myapp2,在urlpatterns字段添加url(r’^myapp1/’, include(myapp1.urls)), url(r’^myapp2/’, include(myapp2.urls)),告知django哪些url请求分发到这两个app中。

到这里,通常我们就可以去填充myapp1、myapp2中的业务逻辑了,最后执行manager.py syncdb、makemigrations、migrate这些命令创建数据库表就可以将整个网站运行起来了。

3.3 为什么要规范Django的目录结构

如果是一个对可维护性没要求、迭代少的业务,读完上面一部分,就可以了。毕竟,适合的才是最好的,一个简单的业务运用复杂的系统实现是不合理的,理解和学习系统也需要成本。

但是,如果你不仅仅需要写出一个Django Project,而是以后需要写很多的Django Project、频繁地维护这些Project,那么你就需要好好的考虑一下Django Project的目录结构了。如果你在生产环境发布过Django Project,你一定会发现,上面还缺少一部分的讨论,没错,那就是多环境的配置,至少涉及两个环境:本地开发环境和生产环境。多环境怎样去组织你的项目配置文件呢?

一个人要开发和维护很多个Django Project,而一个Django Project有很多个Django App,一个App内部封装了大量的业务逻辑,还有上面提到的多环境配置文件。涉及如此之多的文件,我突然想到了管理学,管理学不就是研究如何计划、组织、领导和控制等行为活动,对所拥有的资源进行整合,然后达到目标的过程吗?我们的目标就是,构建一个易维护、可重用的的Django Project。而管理研究产出的是规范,这就是为什么我要规范Django目录结构的原因

3.4 Django目录结构实践建议

3.4.1 关于粒度 - 一个Django App

Django App是一个完整的,可以处理路由请求、返回响应的功能单元。以最小的、可提供服务的单元作为划分,可以减少复用时学习内部逻辑的成本,同时也可以很好的实现单元之间的隔离。
这样划分粒度对我们构建一个Django App提出要求:能够在App的内部独立完成一次Request请求。

在生产环境中,我们很少从零开始构建一个Django Project,至少从一个内部已有Django Framework开始填充业务逻辑部分。这样考虑是合理的,但是开发人员在PM的催促下匆忙填充业务逻辑,而没有考虑到Django App的拆分,这样就造成每次都需要从原有Django Project拷贝代码,或者从Django Framework开始一次又一次的重复实现功能

3.4.2 怎样去构建一个能够独立完成请求的Django App

重点就是: 每个功能块都使用django-admin创建一个Django App

django已经很明确地告诉我们要怎样构建可以复用的Django App,那就是使用命令行去创建,然后将业务逻辑填充在其中。

使用命令 django-admin startapp myapp1,创建的 Django App 主要有 admin.py、models.py 文件、tests.py 文件、views.py 文件,但这还不够,还需要 forms.py 对表单进行验证;需要 settings.py 对 APP 中的常量进行配置,这里建议常量以 APP 名开头,比如,MYAPP1_XXX 这样;需要 utils.py 放一些工具函数;需要 template 放模板,这里建议在 template 中再创建一个与APP同名的文件夹放模板,这样可以直接拷贝到 Project 的 Template 目录下;还需要 requirements.txt 写依赖文件。
由于有两个 myproject 目录,而子 myproject 内只有几个文件,将其拷贝到与APP同一级目录(需要去掉settings.py文件中包头为myproject.部分)。这样下来,Django 的目录结构就成了这样:

最后是关于多环境的配置:
Django通常会配合Nginx和uwsgi一起部署。可以从uwsgi中获取DJANGO_CONF_MODULE变量值来判断所处的运行环境。
project目录下的setting.py文件,增加如下内容:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
WSGI_ENV = os.environ.get("DJANGO_CONF_MODULE", "")
# 运行模式, DEVELOP(开发模式),PRODUCT(正式模式)
RUN_MODE = 'DEVELOP'    
if WSGI_ENV.endswith("production"):
    RUN_MODE = "PRODUCT"
    DEBUG = False
else:
    RUN_MODE = "DEVELOP"
    DEBUG = True
# 加载app中的配置项

from myapp1.settings import *
from myapp2.settings import *

app 目录下的settings.py文件,增加如下内容:

1
2
3
4
5
6
7
# 正式环境配置
if RUN_MODE == 'PRODUCT':
    #设置变量值
    APP_XXX = 'PRODUCT'
else:
    #设置变量值
    APP_XXX = 'DEVELOP'

4. 参考


微信公众号
作者
微信公众号