第十七章:Python の Web开发基础(四) MVC与Django
本課主題
- MVC 介绍
- Django 介紹
MVC 介绍
controllers 处理用户请求
views 放置HTML模版
models 操作数据库
MVC框架就是目录的归类
MVC 是一种软件开发的方法,它把代码的定义和数据访问的方法(模型)与请求逻辑 (控制器)还有用户接口(视图)分开来
Django 介紹
Django开发的一般流程包括模型设计、URL设计、视图编码、模板设计,搭建Django应用的一种典型流程是:先设计好模型,然后就尽快把admin 运行起来,以便你的员工、客户可以尽快开始填充数据。之后,你再考虑该如何把数据呈现给用户
创建 Django
- 创建 project
django-admin startproject mysite
- 创建一个叫 mysite 的 project,会自动生成以下几个 py 文件:
- setting.py #配置文件, 例如增加缓存功能,加密加盐
- urls.py #路由系统,URL 保存了一个函数和网页的对应关系
- wsgi.py #WSGI,封装了socket 对象和功能,它是一套规则、接口
- manage.py #负责管理 Django 程序,比如创建 APP,运行 Django 程序和在数据库中创建表
- 创建虚拟环境,用来安装 django
virtualenv -p python3 venv
- 创建 App
创建一个程序叫 app01,在这个文件夹下面有 migrations 文件夹和以下的 py 文件
cd mysite python3 manage.py startapp app01
生成的文件
migrations/ # 数据修改表结构 admin.py # Django 为我们提供的后台管理 apps.py # 配置当前 App models.py # ORM,写指定的类 通过命令创建数据库结构表 tests.py # 单元测试 views.py # 业务代码
- 运成 Django 程序
python3 manage.py runserver 127.0.0.1:8000
- 注册 App
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01', # 注册 APP ]
- 在 sqlite 中创建表
python3 manage.py makemigrations python3 manage.py migrate
- xxxx
URL设计
在 URL 中写入相对的路径,下面例子加了一个 h.html 的 url 路径,你只须要访问时 http://127.0.0.1:8000/h.html/ 就可以看到 home 函数返回的 <h1>Welcome to GRILLFISH!</h1>, django 必须返回 HttpResponse 对象,在 URL 路由中有 Function Based View (FBV) 和 Class Based View (CBV) 之分,你可以在路由中输入视图函数 views.login 或者是类视图函数 views.Home.as_view( )
from django.shortcuts import HttpResponse def home(request): return HttpResponse('<h1>Welcome to GRILLFISH!</h1>') urlpatterns = [ path('admin/', admin.site.urls), path('h.html/', home), #url 路径和函数对应关系 ]
URL 路由中正規表达式的用法
用户可以在 urls 中以正規表达式来设计路由規则,好的路由设计是一个好网站的基本条件
from django.urls import path, re_path from app01 import views urlpatterns = [ path(r'index/', views.index), re_path(r'fakedindex/(?P<year>\d+)/', views.index, name="index"), re_path(r'details/(\d+)/', views.details), # 导入 re_path 来使用正規表达式 ]
定义好路由規则,然后定义 views.py
# views.py def index(request): USER_DICT = { 1: {'username':'root1','email':'root1@live.com'}, 2: {'username':'root2','email':'root2@live.com'}, 3: {'username':'root3','email':'root3@live.com'}, 4: {'username':'root4','email':'root4@live.com'}, 5: {'username':'root5','email':'root5@live.com'}, } return render(request, 'index.html',{'user_list': USER_DICT})
和 html 的 render 方法
<p> {% for k, v in user_list.items %} <li><a target="_blank" href="/details/{{k}}/"> {{v.username}} </a></li> {% endfor %} </p>
不同正規表达式的路由定义对应的函数方法也有些不同,可以用以下几个方法生成 url,具体方法如下:
url(r'^details-(\d+)-(\d+).html', views.details) # views.py def func(request, nid, uid): pass def func(request, *args): args = (2,9) def func(request, *args, **kwargs): args = (2,9) url(r'^details-(?P<nid>\d+)-(?P<uid>\d+).html', views.details) # views.py def func(request, nid, uid): pass def func(request, **kwargs): kwargs = {'nid': 2, 'uid': 9} def func(request, *args, **kwargs): args = (2,9)
URL 路由分发
在 Django 中可以通过加入 include 来分发不同 app 中的 urls,然后进行处理,假如新增一个 app02 的子程序,然後在主程序的 urls.py 中加入以下路由关系,这样程序也照样可以正常连行
from django.conf.urls import url, include urlpatterns = [ url(r'cmdb/', include('app01.urls')), url(r'monitor/', include('app02.urls')), ]
创建额外的文件夹
创建 templates 文件夹来存放 HTML 文件,创建 static 文件夹来存放 CSS 文件和 JAVASCRIPT 文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/common.css" /> <!-- 从 /static/ 文件夹中找 common.css --> </head> <body> <form action="/login/" method="POST"> <!-- 以POST的方式提交数据到 /login/ URL --> <p> <label for='username'>用戶名,</label> <input id='username' type='text' /> </p> <p> <label for='passwd'>密碼,</label> <input id='passwd' type='password' /> <input type='submit' value='提交'/> </p> </form> <script src="/static/jquery-1.12.4.js"></script> <!-- 从 /static/ 文件夹中找 jquery --> </body> </html>
然后要在 setting.py 中定义 templates 和 staticfile directory, 这样就可以有在 views.py 中简单调用 render 函数来实现业务逻辑。
def login(request): # f = open('templates/login.html','r',encoding='utf-8') # data = f.read() # f.close() # return HttpResponse(data) return render(request, 'login.html')
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR,'templates')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
STATIC_URL = '/static/' STATICFILES_DIRS = ( os.path.join(BASE_DIR, 'static'), )
以 GET 的方式提交请求通常是用来获取数据,內容会在 URL中;以 POST 的方式提交请求時,内容会包含在 RequestBody 中;下面介紹的是 render 模板和 redirect 的作用。redirect 只接受 url 路径, 比如 www.baidu.com 或者是本地 url,/login/
from django.shortcuts import render from django.shortcuts import redirect # Create your views here. def login(request): if request.method == 'POST': user = request.POST.get('user',None) pwd = request.POST.get('pwd',None) if user == 'root' and pwd == '123': return redirect('http://google.com') return render(request, 'login.html')
文件上传的处理需要
file_obj = request.FILES.get('f1',None) import os file_path = os.path.join('uploads', file_obj.name) with open(file_path, mode='wb') as f: for i in file_obj.chunks(): f.write(i)
可以在 views.py 中创建类来封装 get 和 post 方法,
from django.views import View class Home(View): def get(self, request): print(request.method) return render(request, 'home.html') def post(self, request): print(request.method, 'POST') return render(request, 'home.html')
在 url.py 中加入以下语句就可以调用上述的类
path(r'home/', views.Home.as_view()),
class View: """ Intentionally simple parent class for all views. Only implements dispatch-by-method and simple sanity checking. """ http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] def __init__(self, **kwargs): """ Constructor. Called in the URLconf; can contain helpful extra keyword arguments, and other things. """ # Go through keyword arguments, and either save their values to our # instance, or raise an error. for key, value in kwargs.items(): setattr(self, key, value) @classonlymethod def as_view(cls, **initkwargs): """Main entry point for a request-response process.""" for key in initkwargs: if key in cls.http_method_names: raise TypeError("You tried to pass in the %s method name as a " "keyword argument to %s(). Don't do that." % (key, cls.__name__)) if not hasattr(cls, key): raise TypeError("%s() received an invalid keyword %r. as_view " "only accepts arguments that are already " "attributes of the class." % (cls.__name__, key)) def view(request, *args, **kwargs): self = cls(**initkwargs) if hasattr(self, 'get') and not hasattr(self, 'head'): self.head = self.get self.request = request self.args = args self.kwargs = kwargs return self.dispatch(request, *args, **kwargs) view.view_class = cls view.view_initkwargs = initkwargs # take name and docstring from class update_wrapper(view, cls, updated=()) # and possible attributes set by decorators # like csrf_exempt from dispatch update_wrapper(view, cls.dispatch, assigned=()) return view def dispatch(self, request, *args, **kwargs): # Try to dispatch to the right method; if a method doesn't exist, # defer to the error handler. Also defer to the error handler if the # request method isn't on the approved list. if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed return handler(request, *args, **kwargs) def http_method_not_allowed(self, request, *args, **kwargs): logger.warning( 'Method Not Allowed (%s): %s', request.method, request.path, extra={'status_code': 405, 'request': request} ) return HttpResponseNotAllowed(self._allowed_methods()) def options(self, request, *args, **kwargs): """Handle responding to requests for the OPTIONS HTTP verb.""" response = HttpResponse() response['Allow'] = ', '.join(self._allowed_methods()) response['Content-Length'] = '0' return response def _allowed_methods(self): return [m.upper() for m in self.http_method_names if hasattr(self, m)]
路由系统介绍
Djgano 的生命周期是这样的:当有新的客户端请求来的时候,会向 urls.py 查找对应的函数,路由系统进行路由匹配,然后再跟据路由系统中找到视图函数 views.py,函数会从数据库获取数据,然后再以结合 HTML 实现数据。
- 普通关系
- 动态关系
- 路由关系
模型设计
request.method request.POST.get('nid', None) request.GET.get('nid', None) #lcoalhost/home?nid=123 HttpResponse("Hello World!") render(request, 'login.html', {'error_msg': error_msg}) redirect('http://google.com')
复习题
- 如何创建一个 django 工程?
- 创建 django 工程后会有那些自动生成的文件夹,每个文件夹的功能是什么?
- Django 的生命週期是什么?
- 什么是 FBV 和 CBV ?
作业
- 登录
- 主机管理页面,查看所有主机的信息
- 增加主机信息
- 查看详细
參考資料
银角大王:Python之路【第十六篇】:Django【基础篇】
金角大王:
其他: