Django基础介绍


什么是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中数据流转和请求流程如下:

  1. 用户通过浏览器发起request请求;
  2. 通过View根据请求和业务逻辑向model获取或发送数据;
  3. model根据view的要求与数据进行交互;
  4. view将收到model返回的结果发送给template;
  5. template将接收到的数据渲染成最终的html返回给view层;
  6. 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属性获取)
  • 请求头数据
  • 路径参数: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 : 客户端的元信息(环境信息)

响应

每个视图都需要实例化,填充和返回一个响应对象。响应对象可以直接创建,一般有HttpResponseHttpResponseRedirectJsonResponse 三个类,当然也可以使用 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)

JsonResponseHttpResponse的一个子类,用来创建json编码的响应。默认的Content-Typeapplication/json。第一个参数data应该是字典实例。如果safe参数设置为False,它可以是任何json可序列化的对象。

from django.http.response import JsonResponse

data = {'name':'laobai', 'age':18}
JsonResponse(data)

对字典以外的对象进行序列化,需要将safe参数设置为False,否则就会引发错误。


文章作者: 老百
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 老百 !
 上一篇
Djang的ORM详解 Djang的ORM详解
对象关系映射(Object Relational Mapping,简称ORM)。简单的说就是用面向对象的方式,描述数据库,操作数据库,达到不用编写SQL语句就能对数据库进行增删改查等操作。
2023-10-19
下一篇 
前端框架Vue(总结) 前端框架Vue(总结)
Vue 是一款用于构建用户界面的 JavaScript 框架。本小节复习一遍之前所学的Vue知识,重新捋一遍,温故而知新。
2023-09-14
  目录