什么是Django
Django简介
Django是Python Web开发的主流框架,它鼓励快速开发和简洁、实用的设计,旨在帮助开发人员尽快将应用程序从概念到完成。
Django的官网地址:https://docs.djangoproject.com/zh-hans/4.2/
Django目前的最新长期支持版本是4.2,所以这里采用4.2版本

在Web开发领域最著名的设计模式为MVC,而Django的设计模式被叫做MTV。其实本质跟MVC是一样的,只是叫法不同。
MVC是Model-View-Controller
的缩写,
- M(model) 数据存储层即业务逻辑层,代表一个存储数据的对象
- V(view) 视图层,代表模型数据的可视化
- C(controller) 控制层,作用于模型和视图上。它控制数据流向,并在数据变化时更新视图。它使视图与模式解耦。

Django借鉴了经典的MVC模式,也就是MTV设计模式
- M(Model 模型) 与MVC中的M功能相同,负责和数据库交互,进行数据处理
- T(Template 模板) 与MVC中的V功能相同,负责构造要返回的html页面
- V(View 视图) 与MVC中的C的功能相同,接收请求,进行业务处理,返回响应

Django中数据流转和请求流程如下:
- 用户通过浏览器发起request请求;
- 通过View根据请求和业务逻辑向model获取或发送数据;
- model根据view的要求与数据进行交互;
- view将收到model返回的结果发送给template;
- template将接收到的数据渲染成最终的html返回给view层;
- View层将最终渲染后的即包含结果数据的html返回给用户;
Python的web开发框架
Python常用的web开发框架有几十个,最常用的有三个:Django
,Flask
,Fastapi
。(本来写了第四个Tornado
,但查了一下使用率,相较于前面3个量太少了。)

后续的平台系统设计都会使用Django
来完成。
Django的安装
Django与Python的版本匹配
官网上有明确的描述:https://docs.djangoproject.com/zh-hans/4.2/faq/install/#faq-python-version-support
Django版本 | Python版本 |
---|---|
3.2 | 3.6,3.7,3.8,3.9,3.10 (在3.2.9版本中加入支持) |
4.0 | 3.8,3.9,3.10 |
4.1 | 3.8,3.9,3.10,3.11(在4.1.3版本中加入支持) |
4.2 | 3.8,3.9,3.10,3.11 |
新电脑安装的是Python3.11.x,这里Django选择最新的长期支持版4.2.x。
安装Django
安装Django可以通过命令行安装,也可以在IDE中创建项目时进行安装。
虚拟环境
在进行python项目开发过程中,建议使用虚拟环境。这样可以隔离各个项目环境及避免相互造成的不良的影响。比如新开发的Web应用项目使用的是Django4.2,而之前的某个旧系统用的是Django3.2,某个大数据分析项目使用的numpy、pandas等需要安装上百个依赖包,而这些包对Django应用项目来说可能是无用的,等等。若不使用虚拟环境,势必会造成开发环境的混乱。
这里选择的虚拟环境软件是:virtualenvwrapper
。它会将所有创建的虚拟环境目录安放到自定义指定的目录下。而其他的虚拟环境软件都是将虚拟环境创建在当前目录下,很容易造成误操作,创建到了不应该创建的目录下。
安装virtualenvwrapper
安装命令:
pip install virtualenvwrapper
安装完成后,在没有自定义指定虚拟环境目录时,默认的虚拟环境路径是当前创建虚拟环境用户目录/Users/laobai
下的Envs
目录。
自定义指定虚拟环境路径
在系统环境变量中增加一项WORKON_HOME
,设置为需要指定的目录即可。
vim .zprofile
export WORKON_HOME=/Users/laobai/workspaces
source .zprofile
创建虚拟环境
mkvirtualenv djangotest
创建虚拟环境后,会自动进入刚刚创建的这个虚拟环境中。
进入/切换到某个虚拟环境
workon djangotest
退出当前虚拟环境
deactivate
删除某个虚拟环境
rmvirtualenv mytest
列出所有虚拟环境
lsvirtualenv
命令行安装
进入到django项目的虚拟环境中
workon djangotest
执行安装命令,当前最新版本是4.2.6,可以指定版本安装,也可以默认安装最新版本
pip install django
pip install django==4.2.6
命令行创建Django项目
django-admin startproject djangotest01
IDE安装
开发Python应用有多款IDE可以选择,这里选择Pycharm Professional(需购买授权)。(之后所有文章中提到的Pycharm,都特指“Pycharm Professional”)
通过Pycharm创建的Django项目,会自动安装最新版本的django。

基础初始化
(这里是已经创建好了项目,但还没有做任何设置、处理)
配置MySQL
Django默认配置的是sqlite3,由于之后的所有开发应用基本都使用MySQL数据,所以这里将sqlite3配置改为mysql的配置。
MySQL的安装:略。(这里使用的是docker,mysql的版本为8.0.29)
settings.py配置
在settings.py的配置文件中,注释掉sqlite3的配置,增加mysql数据库的配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'djs01',
'USER': 'root',
'PASSWORD': 'xxxxxxx',
'HOST': '127.0.0.1',
'PORT': '3306'
}
}
数据库驱动
python操作mysql有多个驱动程序,这里使用mysqlclient
pip install mysqlclient
安装mysqlclient可能会出现错误,可以尝试下面的方法进行解决
安装mysql (由于使用的是docker的mysql,本机是没有安装mysql的)
brew install mysql
安装pkg-config
brew install pkg-config
Django常用命令
创建项目
django-admin startproject djangotest01 // 其中djangotest01为项目名称
创建APP
app是django项目的组成部分。一个app代表项目中的一个模块,所有url请求的响应都是由app来处理的。即一个django项目由多个app组成,一个app可能被用到多个项目中。 (命令行工具中,需要使用python
命令,若是在IED中的命令行不需要python命令,直接使用manage.py
即可。以下命令都是如此。)
python manage.py startapp blog
数据迁移
django中经常使用model创建模型,然后将模型导入数据库,会使用到下面两个命令
将模型转化为ORM
python manage.py makemigrations
执行迁移命令
python manage.py migrate
运行服务
python manage.py runserver
Django version 4.2.6, using settings 'djangoProject01.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
上面的命令默认使用的是8000端口,若要使用其他端口号
python manage.py runserver 8001
Django version 4.2.6, using settings 'djangoProject01.settings'
Starting development server at http://127.0.0.1:8001/
Quit the server with CONTROL-C.
若需要局域网的其他用户可以访问这个服务,需在启动时使用0.0.0.0
python manage.py runserver 0.0.0.0:8001
Django version 4.2.6, using settings 'djangoProject01.settings'
Starting development server at http://0.0.0.0:8001/
Quit the server with CONTROL-C.
此时浏览器访问http://192.168.18.175:8181/ ,可能会报错
DisallowedHost at /
Invalid HTTP_HOST header: '192.168.18.175:8181'. You may need to add '192.168.18.175' to ALLOWED_HOSTS.
Request Method: GET
Request URL: http://192.168.18.175:8181/
Django Version: 4.2.5
Exception Type: DisallowedHost
Exception Value:
Invalid HTTP_HOST header: '192.168.18.175:8181'. You may need to add '192.168.18.175' to ALLOWED_HOSTS.
Exception Location: C:\Users\sunchanghai\Envs\djangotest1\lib\site-packages\django\http\request.py, line 150, in get_host
提示中已经明确给出了解决方法。
在settings.py做如下设置
ALLOWED_HOSTS = ['192.168.18.175']
再次访问即可成功访问到对应的页面了。
Django项目目录结构
下面是Django项目的文件目录结构

其中:
- 顶层的djangoProject01:Django项目目录,项目的所有文件都存放在这里面
- manage.py:Django项目的命令行工具,用于执行各种管理任务,创建应用、运行服务、执行数据迁移等;
- djangoProject01:项目的主目录,通常与项目的名称相同;
- settings.py:包含项目的设置和配置,如数据库连接、静态文件路径、应用程序列表等;
- urls.py:定义URL路由规则,将URL映射到相应的视图函数;
- asgi:py:ASGI服务器配置文件,用于异步Web服务器;
- wsgi.py:WSGI入口文件,用于将Django应用程序与Web服务器连接起来;
- blog、crm:项目的应用app目录,一个Django项目会有多个应用app;
- migrations/:包含数据库迁移文件的目录,Django使用迁移来管理数据库模式的变化;
- admin.py:用于配置应用程序在 Django后台管理界面中的显示和行为;
- apps.py:应用程序的配置文件,包含有关应用程序的元数据;
- models.py:定义应用程序的数据模型,通常使用Django的ORM功能。
- views.py:定义处理HTTP请求并生成HTTP响应的视图函数;
- urls.py:app中的urls,实现url的多层解析,减少主urls.py中的复杂度;
- tests.py:用于编写应用程序的测试代码;
- static:存放静态文件
- template:存放模板文件
配置settings.py
ALLOWED_HOSTS
DEBUG = True
ALLOWED_HOSTS = []
ALLOWED_HOSTS
,允许的主机,默认为空值。用于配置能够访问当前网站的域名或IP。
DEBUG
为True时,ALLOWED_HOSTS
可以为空,也可以设置为[‘127.0.0.1,’localhost’]
DEBUG
为True时,ALLOWED_HOSTS
必须填值。一般是服务器公网IP或域名。
尽量不要设置为ALLOWED_HOSTS = [*]
。
INSTALLED_APPS
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'crm',
'blog',
]
只要创建了app,就需要将app的名字追加到INSTALLED_APPS
中,需用逗号分割。
本地化设置
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_TZ = True
静态文件
增加静态文件目录设定
STATIC_URL = 'static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
Web应用开发模式
Web应用开发模式主要有两种:前后端不分离和前后端分离
前后端不分离
前后端不分离,是以后端直接渲染模版完成响应的一种开发模式。
浏览器向服务器端发起请求,服务器接收到请求后去数据库中获得数据,然后渲染html模板并返回渲染后的html数据,或返回一个重定向。绝大部分工作都子后端进行处理,客户端(前端)只负责页面的展示和用户交互。
前后端分离
随着ajax技术的出现,可以在不刷新页面向服务器端发送http请求,就出现了前后端分离的开发模式。
后端只需要开发接口即可,前端可以通过发起ajax请求,拿到后端的数据,如何渲染和调用接口的事由前端负责。
前后端分离的开发模式,是当下主流的开发模式。
路由
Django中路由的作用和路由器类似,当一个用户请求站点的一个页面时,路由系统通过对url的路径部分进行匹配,匹配成功就会执行对应的视图来返回响应结果。
django处理请求的过程:
当接收到一个请求,django首先会到settings文件中查找根URLconf模块ROOT_URLCONF = 'djangoProject01.urls'
,在这个模块中查找路由匹配规则。django加载该模块查找变量urlpatterns
,urlpatterns
列表中定义了url和视图函数的对应关系,它是django.urls.path()
和/或django.urls.re_path()
实例的列表。django会按顺序运行每个url模块(包括每个应用下的urls.py),并在与请求的url匹配成功的第一个url停止,django将导入url并调用对应的视图。
根路由模块,Django通过设置ROOT_URLCONF
指定的主路由模块,通常是项目主目录下的urls.py
文件。
子路由模块,各自应用目录下的urls.py
都是子路由.
path()
语法格式:path(route, view, kwargs=None, name=None)
其中:
- path():函数防护一个对象,表示一个路由规则;
- route:一个字符串,表示url规则;
- view:一个视图;
- kwargs:一个字典,需要传入的额外参数;
- name:url的命名;
实例:
首先创建两个APP
manage.py startapp user
manage.py startapp goods
分别在两个应用中的views.py中写个简单的页面返回
user-views.py
from django.http.response import HttpResponse
def user(request):
return HttpResponse('我是用户页面!')
goods-views.py
from django.http.response import HttpResponse
def goods(request):
return HttpResponse('我是商品页面!')
在主目录下的urls.py中配置路由
from django.urls import path
from user.views import user
from goods.views import goods
urlpatterns = [
path('user/', user),
path('goods/', goods),
]
上面的代码有一处需要特别注意,path('user/', user)
,path()函数的第二个参数,是url对应的函数名称user
,即内存地址,而不是user()
。千万不要带括号,带括号是执行user函数,并将执行后的返回结果作为path()函数的第二个参数。
启动服务,分别访问 http://127.0.0.1:8000/user/ 和 http://127.0.0.1:8000/goods/ 两个url地址,能够正确显示views视图文件中返回的内容。
比如 http://127.0.0.1:8000/user/ 这个url请求发送到服务端,会交给路由模块即URLconf,也就是上面的urls.py。首先会从url端口号后面的/
切割,剩下的部分user/
在urlpatterns
中进行匹配,匹配成功即会触发对应的视图函数,若匹配不成功就会报错或进行错误处理等。
另外需要注意,进行匹配时,只匹配url路径,url中的参数不会参与匹配。比如http://127.0.0.1:8000/user/?username="laobai"
,还是匹配user/
参数不会参与匹配,也就是说URL无论是POST请求还是GET请求等,路由都是一样的,都会路由到同一个视图函数。
include()
当项目创建的app较多时,urls.py中的urlpatterns
会配置的比较复杂,通常会在每个app的目录下创建一个urls.py文件(这个文件在创建app不会自动创建),各个app目录下的urls.py叫做子路由。使用include()
方法来将子路由导入的一个URLconf模块中。通过使用include()
将子路由包裹到父路由中,也被称作为路由分发。
语法格式:include(module, namespace=none)
其中:
- module:URLconf模块(或模块名称)
实例:
将上面创建的user和goods两个app通过include()
来配置子路由
分别在user app和goods app下创建urls.py
user-urls.py
from django.urls import path
from user.views import user
urlpatterns = [
path('', user),
]
goods-urls.py
from django.urls import path
from goods.views import goods
urlpatterns = [
path('', goods),
]
修改项目主目录下的urls.py
from django.urls import path, include
urlpatterns = [
path('user/', include('user.urls')),
path('goods/', include('goods.urls')),
]
这样就将user/
和子路由user.urls
进行映射,goods/
和子路由goods.urls
进行映射。
再次分别访问 http://127.0.0.1:8000/user/ 和 http://127.0.0.1:8000/goods/ 两个url地址,都可以正确返回结果。
在URL中捕获参数
django允许在url中捕获值,若要从url中捕获值,需要使用<>
尖括号。这里的参数是指用某个参数作为url中两个/
中间的内容。尖括号定义变量名,捕获的值传递给视图函数相同名称的参数,具体格式<转换器:参数名>
。
例:
user/views.py
from django.http.response import HttpResponse
def user(request):
return HttpResponse('我是用户页面!')
def detail(request, pk):
return HttpResponse('id为{}的用户详情'.format(pk))
user/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.user),
path('<int:pk>/', views.detail),
]
上面代码中<int:pk>
即为需要匹配捕获的内容,而冒号前面的int
被称作路径转换器。
路径转换器一般有5种:
str
:匹配除了路径分隔符/
之外的任何非空字符串。如果<>尖括号内不包含转换器,默认为字符串str转换器;int
:匹配0或任何整数。返回一个整数类型;slug
:匹配任何由ASCII字符或数字组成的slug字符串,加上连字符和下划线;uuid
:匹配格式化的UUID(必须含破折号并且必须是小写字母);path
:匹配任何非空字符串,包括路径分隔符/
。这就允许匹配完整的URL路径。
上面的示例中是接收一个参数,也可以接收多个参数。
user/views.py
from django.http.response import HttpResponse
def user(request):
return HttpResponse('我是用户页面!')
def detail(request, pk):
return HttpResponse('id为{}的用户详情'.format(pk))
def user_list(request, year, month):
return HttpResponse('{}年{}月注册的用户列表'.format(year, month))
user/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.user),
path('<int:pk>/', views.detail),
path('<int:year>/<int:month>/', views.user_list)
]
对应的访问路径为:http://127.0.0.1:8000/user/2022/03/
re_path()
语法格式:path(route, view, kwargs=None, name=None)
其中:
- re_path():函数防护一个对象,表示一个路由规则;
- route:一个字符串,表示一个url规则,字符串中需包含正则表达式;
- view:一个视图;
- kwargs:一个字典,需要传入的额外参数;
- name:url的命名;
与path()
不同的是,route
部分包含正则表达式。当进行匹配时,从正则表达式中捕获的内容会被传递到视图中。
例子,将上面的用户注册年月列表的urls.py改写为如下:
from django.urls import path, re_path
from . import views
urlpatterns = [
path('', views.user),
path('<int:pk>/', views.detail),
# path('<int:year>/<int:month>/', views.user_list),
re_path(r'(?P<year>[1-2]\d{3})/(?P<month>0?[1-9]|1[0-2])/', views.user_list),
]
url命名
path()
和re_path()
有个参数name
,这个参数可以给url命名。
name
参数的作用,是可以通过django.shortcuts.reverse('name')
来反向解析出url的路径
user/urls.py增加name参数,并新增login url:
from django.urls import path, re_path
from . import views
urlpatterns = [
path('', views.user, name='user'),
path('<int:pk>/', views.detail, name='user_detail'),
# path('<int:year>/<int:month>/', views.user_list, name='user_list'),
re_path(r'(?P<year>[1-2]\d{3})/(?P<month>0?[1-9]|1[0-2])/', views.user_list, name='user_list'),
path('login/', views.login,name='login')
]
user/views.py增加login视图函数,登录后跳转到用户页面。可以通过reverse('user')
来反向解析出对应的url,而不是写死为/user/
from django.http.response import HttpResponse
from django.shortcuts import reverse, redirect
def user(request):
return HttpResponse('我是用户页面!')
def detail(request, pk):
return HttpResponse('id为{}的用户详情'.format(pk))
def user_list(request, year, month):
return HttpResponse('{}年{}月注册的用户列表'.format(year, month))
def login(request):
return redirect(reverse('user'))
有一个或多个参数的形式
def login(request):
return redirect(reverse('user_detail', args=(11,)))
def login(request):
return redirect(reverse('user_list', kwargs={"year": 1999, "month": 11}))
App命名
一个项目会有多个app,每个app下面的的url可能会有相同的情况,比如user/detail/
和goods/detail
两个不同app,user和goods有相同的url/detail/
。为了避免跳转页面出错,会为每个app定义一个app_name
,在执行reverse()
时带上app_name
user/urls.py中增加app_name
from django.urls import path, re_path
from . import views
app_name = 'user'
urlpatterns = [
path('', views.user, name='user'),
path('<int:pk>/', views.detail, name='user_detail'),
# path('<int:year>/<int:month>/', views.user_list, name='user_list'),
re_path(r'(?P<year>[1-2]\d{3})/(?P<month>0?[1-9]|1[0-2])/', views.user_list, name='user_list'),
path('login/', views.login)
]
goods/urls.py中增加app_name
from django.urls import path
from goods.views import goods
app_name = 'goods'
urlpatterns = [
path('', goods),
]
user/views.py中进行反向解析的reverse()
中url名称前需要加上app_name
def login(request):
return redirect(reverse('user:user_list', kwargs={"year": 1999, "month": 11}))
视图
视图是MTV模式中的View层,用于处理客户端的请求并生成响应数据。在Django中,视图有两种:FBV(function base views),基于函数的视图,就是在视图里使用函数处理请求,CBV(class base views),基于类的视图,就是在视图里使用类处理请求。
FBV(函数视图)
基于函数的视图,user/views.py
from django.http.response import HttpResponse
from django.shortcuts import reverse, redirect
def user(request):
return HttpResponse('我是用户页面!')
def detail(request, pk):
return HttpResponse('id为{}的用户详情'.format(pk))
def user_list(request, year, month):
return HttpResponse('{}年{}月注册的用户列表'.format(year, month))
def login(request):
return redirect(reverse('user:user_list', kwargs={"year": 1999, "month": 11}))
配置user/urls.py
from django.urls import path, re_path
from . import views
app_name = 'user'
urlpatterns = [
path('', views.user, name='user'),
path('<int:pk>/', views.detail, name='user_detail'),
# path('<int:year>/<int:month>/', views.user_list, name='user_list'),
re_path(r'(?P<year>[1-2]\d{3})/(?P<month>0?[1-9]|1[0-2])/', views.user_list, name='user_list'),
path('login/', views.login),
]
CBV(类视图)
基于类的视图,可以与特定的HTTP方法关联,能通过单独的方法替代条件分支的方式处理不同的HTTP请求。goods/views.py
from django.http import HttpResponse
from django.views import View
class Goods(View):
def get(self, request):
print("Get请求")
return HttpResponse("Get请求")
def post(self, request):
print("Post请求")
return HttpResponse("Post请求")
配置goods/urls.py
from django.urls import path
from goods import views
app_name = 'goods'
urlpatterns = [
path('', views.Goods.as_view()),
]
请求和响应
web框架本质就是处理用户发起的请求,然后返回响应结果。请求和响应就是框架中的数据流。
请求
当页面被请请求时,django会创建一个HttpRequest
对象,该对象包含关羽请求的元数据。然后django加载相应的视图,将HttpRequest
对象作为第一个参数传递给视图函数。每个视图负责返回一个HttpResponse
对象。
请求参数类型
Django前端向后端传参的方式有以下几种:
- 拼接在url后面的参数(
?key=value
) (可以通过request.GET
属性获取) - 请求体参数
- form表单格式(可以通过
request.POST
属性获取) - json格式(可以通过
request.body
属性获取) - 上传文件(可以通过
request.FILES
属性获取)
- form表单格式(可以通过
- 请求头数据
- 路径参数:url路径中的参数<>
request 对象的常用属性和方法有:
- request.content_type : 获取请求正文类型
- request.encoding : 请求编码
- request.is_ajax : 请求是否是 ajax 请求
- request.GET : 获取GET请求信息
- request.POST : 获取POST请求信息
- request.FILES : 获取文件信息
- request.COOKIES : 获取COOKIE信息
- request.path : 获取请求路径信息
- request.body : 获取请求体(字节类型)
- request.META : 客户端的元信息(环境信息)
响应
每个视图都需要实例化,填充和返回一个响应对象。响应对象可以直接创建,一般有HttpResponse
、HttpResponseRedirect
、JsonResponse
三个类,当然也可以使用 render()
、redirect()
等函数快速创建。
from django.http.response import HttpResponse
from django.http.response import JsonResponse
from django.shortcuts import reverse, redirect, render
传字符串
可以将页面的内容以字符串的形式传递给HttpResponse
构造函数,也可以增量的形式添加内容。
from django.http.response import HttpResponse
def user(request):
return HttpResponse('我是用户页面!')
from django.http.response import HttpResponse
response = HttpResponse()
response.write("<p>这是用户页面!</p>")
设置headers
使用HttpResponse.headers
设置或删除一个字段
from django.http.response import HttpResponse
response = HttpResponse()
response.headers['token'] = '1234567'
del response.headers['token']
设置状态码
直接传递参数status
给构造函数
from django.http.response import HttpResponse
def user(request):
return HttpResponse('我是用户页面!', status=200)
JsonResponse
对象
格式:class JsonResponse(data, encoder=DjangoJSONEncoder, safe=True, json_dupms_params=None, kwargs)
JsonResponse
是HttpResponse
的一个子类,用来创建json编码的响应。默认的Content-Type
为application/json
。第一个参数data应该是字典实例。如果safe参数设置为False,它可以是任何json可序列化的对象。
from django.http.response import JsonResponse
data = {'name':'laobai', 'age':18}
JsonResponse(data)
对字典以外的对象进行序列化,需要将safe
参数设置为False
,否则就会引发错误。