- 安装&启动
- HTTP请求URL路由
- 项目APP
- 返回 页面内容 给浏览器
- 路由
- 路由子表
- 创建数据库
- 定义数据库表
- 创建数据库表
- Django Admin
- 读取数据库数据
- 过滤条件
- 对资源的增查改删处理
- CRUD
- 临时取消 CSRF 校验
- 前端整合
- 实现登录
- session 方案
- 数据库表的关联
- 参考
IDEA 这里安装即可。安装后 new 一个新项目, 在控制台使用
python manage.py runserver 0.0.0.0:80
出现 Django 的图标就说明好了。
- manage.py:命令行工具,内置多种方式与项目进行交互。
- _ _init _ _.py:初始化文件,一般情况下无需修改。
- settings.py:项目配置文件,项目的所有功能都需要在该文件中进行配置。
- urls.py:项目的路由设置,设置网站的具体内容。
- wsgi.py:全称为 Python Web Server Gateway Interface ,即 Python 服务器网关接口,是 Python 应用与Web服务器之间的接口,用于Django项目在服务器上的部署和上线,一般不需要修改。
在Web部署的方案上,有一个方案是目前应用最广泛的:
- 部署一个Web服务器专门用来处理HTTP协议层面相关的事情,比如如何在一个物理机上提供多个不同的Web服务(单IP多域名,单IP多端口等)这种事情。
- 部署一个用各种语言编写(Java, PHP, Python, Ruby等)的应用程序,这个应用程序会从Web服务器上接收客户端的请求,处理完成后,再返回响应给Web服务器,最后由Web服务器返回给客户端。
要采用第2种方案,Web服务器和应用程序之间就要知道如何进行交互。
为了定义Web服务器和应用程序之间的交互过程,就形成了很多不同的规范。
比如改进CGI性能的FasgCGI,Java专用的Servlet规范,还有Python专用的WSGI规范等。
提出这些规范的目的就是为了定义统一的标准,提升程序的可移植性。
WSGI存在的目的有两个:
- 让Web服务器知道如何调用Python应用程序,并且把用户的请求告诉应用程序。
- 让Python应用程序知道用户的具体请求是什么,以及如何返回结果给Web服务器。
在WSGI中定义了两个角色,Web服务器端称为server或者gateway,应用程序端称为application或者framework(因为WSGI的应用程序端的规范一般都是由具体的框架来实现的)。
server端会先收到用户的请求,然后会根据规范的要求调用application端,调用的结果会被封装成HTTP响应后再发送给客户端。高效的 wsgi web sever 产品,例如:gunicorn,uwsgi等。
如果崇尚自由可定制,可以尝试wsgi web开发的信马由韁,如果坚守稳定可靠, servlet的规范一致给你足够的信心。好坏只在你的需求,无关对错。
HTTP请求URL路由 项目APP
Django 中的一个app 就是项目里面的一个应用的意思。
一个项目包含多个app。一个app 通常就是一个相对独立的模块 ,实现相对独立的功能。
一个app 本质上 就是一个 Python 包, 里面包含了一些应用相关的代码文件。
当然所有功能放一起也可以,就是比较臃肿。
控制台 new 一个 新项目:
django-admin startproject 项目名
控制台 new 一个 新app:
python manage.py startapp app名字
app名字/
__init__.py
admin.py
apps.py
migrations/
models.py
views.py
这个目录其实就是一个python package
返回 页面内容 给浏览器
比如,我们 设计 凡是浏览器访问的http 请求的 url 地址 是 /sales/orders/
, 就由 views.py 里面的函数 listorders
来处理, 返回一段字符串给浏览器。
请大家 打开 views.py , 在里面加入如下内容
from django.http import HttpResponse
def listorders(request):
return HttpResponse("下面是系统中所有的订单信息:test")
注意
-
这里面最终的返回结果是
HttpResponse
对象的参数字符串 ,也就是这句话下面是系统中所有的订单信息。。。
-
listorders的参数
request
是Django中的HttpRequest
对象,包含了HTTP请求中的信息。
光是定义了这样一个函数不行的,我们需要 告诉 Django :
当前端发送过来的HTTP请求 url地址 是 /sales/orders/
, 就由 views.py 里面的函数 listorders
来处理。
怎么告诉Django呢?
这就是 Django中的 url路由设置。
路由urls.py 文件
打开该文件,在 urlpatterns
列表变量中添加一条路由信息,结果如下
from django.contrib import admin
from django.urls import path
# 别忘了导入 listorders 函数
from sales.views import listorders
urlpatterns = [
path('admin/', admin.site.urls),
# 添加如下的路由记录
path('sales/orders/', listorders),
]
urlpatterns
列表 就是 Django 的 url 路由的入口。
所以,所谓 路由
就是指 : 根据 HTTP请求的url路径, 设置 由哪个 函数来处理这个请求。
通常我们项目代码的修改, Django的测试服务可以自动检测到,并且重新加载,不需要我们重启 Django Web 服务。
路由子表url 路由表就是可以像上面这样,一个请求对应一个处理函数。
但是有的时候,我们的项目比较大的时候, 请求的url 会特别多。
比如我们的系统提供给 客户、销售商、管理员 访问的url是不一样的,如下
customer/
customer/orders/
sales/
sales/orders/
mgr/
mgr/customers/
mgr/medicines/
mgr/orders/
复杂的系统url条目多达几百甚至上千个, 放在一个表中,查看时,要找一条路由记录就非常麻烦。
这时,我们通常可以将不同的路由记录 按照功能 分拆到不同的 url路由子表 文件中。
例如:
创建数据库
现在我们先使用另一种更简单的 数据库 sqlite。
sqlite 没有 独立的数据库服务进程,数据操作被做成库直接供应用程序调用。 Django中可以直接使用,无须先搭建数据服务。
后面要使用mysql 等其他数据库 只需修改一些配置就可以了。
创建数据库
项目中数据库的配置在 bysms/settings.py
中,这里
# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
记得导入 import os
首先我们需要创建数据库,执行如下命令
python manage.py migrate
就会在 项目的根目录下面 生成一个配置文件中指定的数据库文件 db.sqlite3
。
Django 里面, 数据库表的操作,包括 表的定义、表中数据的增删改查,都可以通过 Model 类型的对象进行的。
通常,在Django中
- 定义一张数据库的表 就是定义一个继承自 django.db.models.Model 的类
- 定义该表中的字段(列), 就是定义该类里面的一些属性
- 类的方法就是对该表中数据的处理方法,包括 数据的增删改查
这样,开发者对数据库的访问,从原来的使用底层的 sql 语句,变成 面向对象的开发,通过一系列对象的类定义 和方法调用就可以 操作数据库。
这样做:
- 首先 极大的简化了我们应用中的数据库开发,因为无需使用sql语句操作数据库了, 提高了开发的效率;
- 其次 屏蔽了 不同的数据库访问的底层细节,基本做到了 开发好代码后,如果要换数据库,几乎不需要改代码, 修改几个配置项就可以了。
这种 通过 对象 操作数据库 的方法 被称之为 ORM (object relational mapping)。
定义数据库表
python manage.py startapp common
就会创建一个目录名为 common, 对应 一个名为 common 的app。
我们要定义数据库的表,无需执行sql语句,只需要在app目录下面 定义特殊的类就可以了。
数据库表的定义,一般是放在app目录中的 models.py里面的。
打开 common/models.py,发现里面是空的,因为我们还没有定义我们的业务所需要的表。
我们修改它,加入如下内容
from django.db import models
class Customer(models.Model):
# 客户名称
name = models.CharField(max_length=200)
# 联系电话
phonenumber = models.CharField(max_length=200)
# 地址
address = models.CharField(max_length=200)
这个 Customer 类继承自 django.db.models.Model, 就是用来定义数据库表的。
Djanog 有很多字段对象类型, 对应不同的类型的数据库字段。
大家可以参考官方文档
创建数据库表
在项目的配置文件 settings.py
中, INSTALLED_APPS 配置项 加入如下内容
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# 加入下面这行
'common.apps.CommonConfig',
]
CommonConfig 是 AppConfig的 子类, 就表示这个是应用的配置类。
这里 name = ‘common’ , name 是用来定义 应用的python模块路径的。 这里就是说 应用 模块路径为 common 。
关于 其他的配置参数, 大家可以参考官方文档
现在Django知道了我们的 common 应用, 我们可以在项目根目录下执行命令
python manage.py makemigrations common
得到如下结果
Migrations for 'common':
common\migrations\0001_initial.py
- Create model Customer
这个命令,告诉Django , 去看看common这个app里面的models.py ,我们已经修改了数据定义, 你现在去产生相应的更新脚本。
执行一下,会发现在 common\migrations 目录下面出现了0001_initial.py, 这个脚本就是相应要进行的数据库操作代码。
随即,执行如下命令
python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, common, contenttypes, sessions
Running migrations:
Applying common.0001_initial... OK
就真正去数据库创建表了。
如果以后我们修改了Models.py 里面的库表的定义,都需要再次运行 python manage.py makemigrations common 和 python manage.py migrate 命令,使数据库同步该修改结果。
Django AdminDjango提供了一个管理员操作界面可以方便的 添加、修改、删除你定义的 model 表数据。
首先,我们需要创建 一个超级管理员账号。
进入到项目的根目录,执行如下命令,依次输入你要创建的管理员的 登录名、email、密码。
python manage.py createsuperuser
Username (leave blank to use 'root'): root
Email address: 1290267280@qq.com
Password:88888888
Password (again):88888888
Superuser created successfully.
然后我们需要修改应用里面的 管理员 配置文件 common/admin.py,注册我们定义的model类。这样Django才会知道。
from django.contrib import admin
from .models import Customer
admin.site.register(Customer)
好了,现在就可以访问 http://127.0.0.1/admin/
,输入刚才注册的用户密码登录。
如果你是中文的操作系统,想使用中文的admin界面,应该在配置文件 settings.py
中 MIDDLEWARE
最后加入如下配置:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
# admin界面语言本地化
'django.middleware.locale.LocaleMiddleware',
]
读取数据库数据
现在我们来实现一个功能:浏览器访问 sales/customers/
,我们的服务端就返回系统中所有的客户记录给浏览器。
在文件sales/views.py 中,定义一个listcustomers 函数,内容如下:
# 导入 Customer 对象定义
from common.models import Customer
def listcustomers(request):
# 返回一个 QuerySet 对象 ,包含所有的表记录
# 每条表记录都是是一个dict对象,
# key 是字段名,value 是 字段值
qs = Customer.objects.values()
# 定义返回字符串
retStr = ''
for customer in qs:
for name,value in customer.items():
retStr += f'{name} : {value} | '
# <br> 表示换行
retStr += '<br>'
return HttpResponse(retStr)
我们只需修改 sales/urls.py
即可,添加如下记录
path('customers/', views.listcustomers),
过滤条件
我们可以通过 filter
方法加入过滤条件,修改view里面的代码,如下所示 :
def listcustomers(request):
# 返回一个 QuerySet 对象 ,包含所有的表记录
qs = Customer.objects.values()
# 检查url中是否有参数phonenumber
ph = request.GET.get('phonenumber',None)
# 如果有,添加过滤条件
if ph:
qs = qs.filter(phonenumber=ph)
# 定义返回字符串
retStr = ''
for customer in qs:
for name,value in customer.items():
retStr += f'{name} : {value} | '
# <br> 表示换行
retStr += '<br>'
return HttpResponse(retStr)
Django 框架在 url 路由匹配到函数后, 调用函数时,会传入 一个 HttpRequest 对象给参数变量 request,该对象里面 包含了请求的数据信息。
HTTP 的 Get 请求url里面的参数(术语叫 querystring 里面的参数), 可以通过 HttpRequest对象的 GET 属性获取。这是一个类似dict的对象。
有了这个过滤条件,Django 会在底层执行数据库查询的SQL语句 加上相应的 where 从句,进行过滤查询。
注意,参数名 phonenumber 是和 定义的表 model 的属性名 phonenumber 一致的。
filter的过滤条件可以有多个,只要继续在后面的参数添加过滤条件即可。
对资源的增查改删处理
再为 管理员用户 专门创建一个应用 mgr 来处理相关的 请求。
python manage.py startapp mgr
前面,我们都是在views.py 里面定义函数,处理 http请求的。
但是可以想象, 以后,这个mgr应用要处理很多类型的http请求。
都用这个views.py 就会让这个文件非常的庞大, 不好维护。所以,我们可以用不同的 py 文件处理不同类型的http请求。
比如,这里我们可以新增一个文件 customer.py, 专门处理 客户端对 customer 数据的操作。
将来如果客户端有对其他类型数据的操作, 比如 order 数据, 我们就可以添加 orders.py 来处理。
注意:Django 的 url路由功能 不支持 根据 HTTP 请求的方法 和请求体里面的参数 进行路由。
就是不能像下面这样,来根据请求 是 post 还是 get 来 路由
path('customers/', 'app.views.list_customer', method='get'),
path('customers/', 'app.views.add_customer', method='post'),
自己编写一个函数, 来 根据 http请求的类型 和请求体里面的参数 分发(或者说路由)给 不同的函数进行处理。
我们可以 在 customer.py 中定义如下 dispatcher 函数:
from django.http import JsonResponse
import json
def dispatcher(request):
# 将请求参数统一放入request 的 params 属性中,方便后续处理
# GET请求 参数在url中,同过request 对象的 GET属性获取
if request.method == 'GET':
request.params = request.GET
# POST/PUT/DELETE 请求 参数 从 request 对象的 body 属性中获取
elif request.method in ['POST', 'PUT', 'DELETE']:
# 根据接口,POST/PUT/DELETE 请求的消息体都是 json格式
request.params = json.loads(request.body)
# 根据不同的action分派给不同的函数进行处理
action = request.params['action']
if action == 'list_customer':
return listcustomers(request)
elif action == 'add_customer':
return addcustomer(request)
elif action == 'modify_customer':
return modifycustomer(request)
elif action == 'del_customer':
return deletecustomer(request)
else:
return JsonResponse({'ret': 1, 'msg': '不支持该类型http请求'})
接下来,根据 API 接口 ,我们发现 凡是 API 请求url为 /api/mgr/customers
的,都属于 客户 相关的API, 都应该交由 我们上面定义的dispatch函数进行分派处理。
那么我们需要在Django的url路由文件中加入对应的路由。
CRUD
def listcustomers(request):
# 返回一个 QuerySet 对象 ,包含所有的表记录
qs = Customer.objects.values()
# 将 QuerySet 对象 转化为 list 类型
# 否则不能 被 转化为 JSON 字符串
retlist = list(qs)
return JsonResponse({'ret': 0, 'retlist': retlist})
def addcustomer(request):
info = request.params['data']
# 从请求消息中 获取要添加客户的信息
# 并且插入到数据库中
# 返回值 就是对应插入记录的对象
record = Customer.objects.create(name=info['name'],
phonenumber=info['phonenumber'],
address=info['address'])
return JsonResponse({'ret': 0, 'id': record.id})
def modifycustomer(request):
# 从请求消息中 获取修改客户的信息
# 找到该客户,并且进行修改操作
customerid = request.params['id']
newdata = request.params['newdata']
try:
# 根据 id 从数据库中找到相应的客户记录
customer = Customer.objects.get(id=customerid)
except Customer.DoesNotExist:
return {
'ret': 1,
'msg': f'id 为`{customerid}`的客户不存在'
}
if 'name' in newdata:
customer.name = newdata['name']
if 'phonenumber' in newdata:
customer.phonenumber = newdata['phonenumber']
if 'address' in newdata:
customer.address = newdata['address']
# 注意,一定要执行save才能将修改信息保存到数据库
customer.save()
return JsonResponse({'ret': 0})
def deletecustomer(request):
customerid = request.params['id']
try:
# 根据 id 从数据库中找到相应的客户记录
customer = Customer.objects.get(id=customerid)
except Customer.DoesNotExist:
return {
'ret': 1,
'msg': f'id 为`{customerid}`的客户不存在'
}
# delete 方法就将该记录从数据库中删除了
customer.delete()
return JsonResponse({'ret': 0})
当然在文件的开头,我们需要 先导入 Customer 定义,像下面这样
# 导入 Customer
from common.models import Customer
临时取消 CSRF 校验
注意,缺省创建的项目, Django 会启用一个 CSRF (跨站请求伪造) 安全防护机制。
在这种情况下, 所有的Post、PUT 类型的 请求都必须在HTTP请求头中携带用于校验的数据。
为了简单起见,我们先临时取消掉CSRF的 校验机制,等以后有需要再打开。
前端整合要临时取消掉CSRF的 校验机制,非常简单,只需要在 项目的配置文件
项目/settings.py
中MIDDLEWARE
配置项 里 注释掉 ‘django.middleware.csrf.CsrfViewMiddleware’ 即可。
资源下载:点击这里下载
下载好以后,可以解压该 z_dist.zip 文件到项目根目录下面,形成一个目录 z_dist。
该目录下面就是前端的 代码资源文件。
但是前端文件都是静态文件,需要我们配置一下Django的配置文件, 指定http请求如果访问静态文件,Django在哪个目录下查找。
注意,接下来我们配置 Django 静态文件服务, 是 开发时
使用的 一种 临时方案
,性能很低,这是方便我们调试程序用的。
现在,请打开 项目/urls.py
文件,在末尾 添加一个
+ static("/", document_root="./z_dist")
并添加如下声明
# 静态文件服务
from django.conf.urls.static import static
就是在url 路由中加入 前端静态文件的查找路径。
这样如果 http请求的url 不是以 admin/ sales/ api/mgr/ 开头, Django 就会认为是要访问 z_dist目录下面的静态文件。
实现登录我们可以在mgr目录里面创建一个代码文件 sign_in_out.py
这个代码文件就是用来处理 管理员登录和登出 的API 请求的。
Django中有个内置app 名为 django.contrib.auth
,缺省包含在项目Installed App设置中。
这个app 的 models 定义中包含了一张 用户表,名为 auth_user
。
当我们执行 migrate 创建数据库表时,根据,就会为我们创建 用户表 auth_user,如下所示:
django.contrib.auth
这个app 已经 为我们做好了登录验证功能。
我们只需要使用这个app库里面的方法就可以了。
from django.http import JsonResponse
from django.contrib.auth import authenticate, login, logout
# 登录处理
def signin(request):
# 从 HTTP POST 请求中获取用户名、密码参数
userName = request.POST.get('username')
passWord = request.POST.get('password')
# 使用 Django auth 库里面的 方法校验用户名、密码
user = authenticate(username=userName, password=passWord)
# 如果能找到用户,并且密码正确
if user is not None:
if user.is_active:
if user.is_superuser:
login(request, user)
# 在session中存入用户类型
request.session['usertype'] = 'mgr'
return JsonResponse({'ret': 0})
else:
return JsonResponse({'ret': 1, 'msg': '请使用管理员账户登录'})
else:
return JsonResponse({'ret': 0, 'msg': '用户已经被禁用'})
# 否则就是用户名、密码有误
else:
return JsonResponse({'ret': 1, 'msg': '用户名或者密码错误'})
# 登出处理
def signout(request):
# 使用登出方法
logout(request)
return JsonResponse({'ret': 0})
创建URl
们只需要 在mgr 目录下面 的子路由文件 urls.py
里添加如下内容 :
urlpatterns = [
path('signin', sign_in_out.signin),
path('signout', sign_in_out.signout),
]
session 方案
Django 中 该表 名字就叫 django_session, 如下所示:
# 在session中存入用户类型
request.session['usertype'] = 'mgr'
这行代码的作用 就是在登录认证后,将 用户类型保存到session数据中, 也就是存入前面数据库的那张图的 会话数据记录中。
Django 框架 会自动在 HTTP 响应消息头中 加入 类似下面的 sessionid cookie。 后续的HTTP请求就会携带这个sessionid。
前面实现的代码中, 这些请求都是在dispatcher入口函数处理的,我们就只需在该dispatch中进行验证。
修改 mgr/customer.py
的dispatcher 函数,在前面加上如下代码:
# 根据session判断用户是否是登录的管理员用户
if 'usertype' not in request.session:
return JsonResponse({
'ret': 302,
'msg': '未登录',
'redirect': '/mgr/sign.html'},
status=302)
if request.session['usertype'] != 'mgr' :
return JsonResponse({
'ret': 302,
'msg': '用户非mgr类型',
'redirect': '/mgr/sign.html'} ,
status=302)
注意request对象里面的session属性对应的就是 session记录里面的数据。
该数据对象类似字典,所以检查是否有usertype类型为mgr的信息,就是这样写
if request.session['usertype'] != 'mgr'
数据库表的关联
python manage.py makemigrations common
python manage.py migrate
以后我们修改了models.py 里面的库表的定义,都需要再次运行 python manage.py makemigrations common 和 python manage.py migrate 命令,使数据库同步该修改结果。
Django中定义外键 的方法就是 Model类的该属性字段 值为 ForeignKey
对象 。
另外一个参数 on_delete 指定了 当我们想 删除
外键指向的主键 记录时, 系统的行为。
class Order(models.Model):
# 订单名
name = models.CharField(max_length=200,null=True,blank=True)
# 创建日期
create_date = models.DateTimeField(default=datetime.datetime.now)
# 客户
customer = models.ForeignKey(Customer,on_delete=models.PROTECT)
Django是通过 ManyToManyField
对象 表示 多对多的关系的。
像这样
medicines = models.ManyToManyField(Medicine, through='OrderMedicine')
指定Order表和 Medicine 表 的多对多关系, 其实Order表中并不会产生一个 叫 medicines 的字段。
基础功能大致就是这些,有新坑,也有新的感悟。
参考- https://docs.djangoproject.com/
- https://www.byhy.net/
- wsgi vs servlet https://www.cnblogs.com/jackiesteed/articles/4287223.html