Django2.2.2Python3.6.21.MVC与MTV模型1.1MVC

所谓MVC,就是把Web应用分为模型(M),控制器(C)和视图(V)三层,他们之间以一种插件式的,松耦合的方式连接在一起。模型负责业务对象与数据库的映射(ORM),视图负责与用户的交互(页面),控制器接收用户的输入,调用模型和视图完成用户的请求。

1.2MTV

Django的MTV模式本质上和MVC是一样的,也是为了各组件间保持松耦合关系,只是定义上有些不同。Django的MTV分别为M 代表模型(Model):负责业务对象和数据库的关系映(ORM)。T 代表模板(Template):负责展示页面给用户(HTML)。V 代表视图(View):负责业务逻辑,并在适当的时候调用Model和Template。除了上面三层,还需要一个URL分发器,它的作用是将一个个URL页面请求分发给不同的View处理,View再调用相应的Model和Template。

2.Django的下载2.1下载django

pip3 install django

2.2查看django脚本路径

2.3创建一个django project

windows上django-admin.exe startproject mysiteLinux上django-admin.py startproject mysite


manage.py Django项目中的工具,可以通过它调用django shell和数据库等。settings.py 包含了项目的默认设置,包含数据库信息,调试标志及其他工作变量。urls.py 负责把URL映射到应用程序。2.4在mysite下创建应用

python manage.py startapp blog

2.5启动django项目

python manage.py runserver 8080


2.6一个简易的Django示例

除了默认生成的信息,下面的内容是新加的

urls.py

from django.contrib import adminfrom django.urls import pathfrom app1 import viewsurlpatterns = [ path('admin/', admin.site.urls), path('index/', views.index),]

views.py

from django.shortcuts import renderimport datetime# Create your views here.def index(request): now = datetime.datetime.now() ctime = now.strftime("%Y-%m-%D") return render(request, "index.html", {"ctime": ctime})

index.html

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body><h5>当前时间为:{{ ctime }}</h5></body></html>

3.静态文件配置

settings.py

# 这是Django默认给我们设置的# 这里是static的访问URL,全路径为--http://127.0.0.1:8000/static/jquery-3.3.1.js,可以修改,但默认我们都不修改STATIC_URL = '/static/'# 这里是静态文件存放的路径,访问上面的URL,就能找到该路径下的js文件。# 该目录也可以命名为别的,但我们默认都命名为static# 这是我们自己设置的STATICFILES_DIRS = [ os.path.join(BASE_DIR, "static")]

index.html中

<!-- 这里的src中的static实际是STATIC_URL = '/static/' 中的static。正是因为配置了STATICFILES_DIRS,所以才能通过STATIC_URL找到jquery-3.3.1.js文件--><script type="text/javascript" src="/static/jquery-3.3.1.js"></script><script type="text/javascript" src="/static/app1/index.js"></script><link rel="stylesheet" type="text/css" href="/static/app1/index.css">







4.路由控制

URL配置就是用户请求的URL与views.py中函数的映射表。通过urls.py,找到对应的函数。在Django-1.x版本中,使用urlfrom django.conf.urls import urlurlpatterns = [ url(r'^articles/2003/$', views.special_case_2003), url(r'^articles/([0-9]{4})/$', views.year_archive), url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive), url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),]在Django-2.x版本中,使用path和re_pathfrom django.urls import path, re_pathfrom app1 import viewsurlpatterns = [ path('admin/', admin.site.urls), path('index/', views.index), re_path("^articles/2003/$",views.special_case_2003),]4.1简单的路由配置

urls.py

from django.contrib import adminfrom django.urls import path, re_pathfrom app1 import viewsurlpatterns = [ path('admin/', admin.site.urls), re_path(r"^articles/2003/$", views.special_case_2003), re_path(r"^articles/([0-9]{4})/$", views.year_archive), re_path(r"^articles/([0-9]{4})/([0-9]{2})/$", views.month_archive), re_path(r"^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$", views.article_detail),]

views.py

from django.shortcuts import HttpResponsedef special_case_2003(request): return HttpResponse("^articles/2003/$")def year_archive(request, year): return HttpResponse("^articles/([0-9]{4})/$ year:%s" % year)def month_archive(request, year,month): return HttpResponse("^articles/([0-9]{4})/([0-9]{2})/$ year:%s,month:%s" % (year, month))def article_detail(request, year, month, article_id): return HttpResponse("^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$ year:%s,month:%s,id:%s" % (year, month, article_id))




说明:1.要从URL中捕获一个值,只需要用()括起来。2.不需要添加一个前导的/,因为每个URL都有。例如,应该是^article,而不是^/article。3.每个正则表达式前面的'r'是可选的,但是建议加上。它告诉python这个字符串是'原始的'----字符串中的任何字符都不应该转义。特殊例子说明一些请求的例子:/articles/2005/03/ 请求将匹配列表中的第三个模式。Django 将调用函数views.month_archive(request, '2005', '03')。/articles/2005/3/ 不匹配任何URL 模式,因为列表中的第三个模式要求月份应该是两个数字。/articles/2003/ 将匹配列表中的第一个模式不是第二个,因为模式按顺序匹配,第一个会首先测试是否匹配。请像这样自由插入一些特殊的情况来探测匹配的次序。/articles/2003 不匹配任何一个模式,因为每个模式要求URL 以一个反斜线结尾。/articles/2003/03/03/ 将匹配最后一个模式。Django 将调用函数views.article_detail(request, '2003', '03', '03')。4.2有名分组

上面的例子中,是按照参数的顺序,把URL中匹配到的内容传递给函数。可以按照参数的名称进行匹配,语法是:(?P<name>pattern),其中name是参数名称,pattern是匹配的正则表达式。注意:urls.py中re_path(r"^articles/(?P<year>[0-9]{4})/$", views.year_archive)函数定义中def year_archive(request, year)参数名称要保持相同,都要是year。

urls.py

from django.contrib import adminfrom django.urls import path, re_pathfrom app1 import viewsurlpatterns = [ path('admin/', admin.site.urls), re_path(r"^articles/2003/$", views.special_case_2003), re_path(r"^articles/(?P<year>[0-9]{4})/$", views.year_archive), re_path(r"^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$", views.month_archive), re_path(r"^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<article_id>[0-9]+)/$", views.article_detail),]

views.py

from django.shortcuts import HttpResponsedef special_case_2003(request): return HttpResponse("^articles/2003/$")#注意,这里的参数名一定要和url中的命名相同才能进行相互匹配def year_archive(request, year): return HttpResponse("^articles/([0-9]{4})/$ year:%s" % year)def month_archive(request, month, year): return HttpResponse("^articles/([0-9]{4})/([0-9]{2})/$ year:%s,month:%s" % (year, month))def article_detail(request, article_id, year, month): return HttpResponse("^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$ year:%s,month:%s,article_id:%s" % (year, month, article_id))



这个实现是捕获的值作为关键字参数而不是位置参数传递给视图函数。/articles/2005/03/ 请求将调用views.month_archive(request, year='2005', month='03')函数,而不是views.month_archive(request, '2005', '03')。/articles/2003/03/03/ 请求将调用函数views.article_detail(request, year='2003', month='03', day='03')。在实际应用中,这意味着你的urls.py中会更加清晰且不容易产生参数顺序问题。你可以在视图函数中重新安排参数的顺序。4.3分发

当有多个app的时候,url配置可以分别写在每个app中的urls.py中。

urlpatterns = [ path('admin/', admin.site.urls), re_path(r"^app1/", include('app1.urls')),]




4.4登录验证实例








4.5反向解析

在使用Django项目时,一个常见的需求是获得URL的最终形式,以用于嵌入到视图或html中,或用于处理服务器端的重定向。我们不希望硬编码这些URL,希望设计一种与urls.py中配置对应的动态的URL。在html中:使用url模板标签在python代码中:使用django.urls.reverse

urls.py

from django.contrib import adminfrom django.urls import path, re_path, includefrom app1 import viewsurlpatterns = [ path('admin/', admin.site.urls), re_path(r"^article/([0-9]{4})/$", views.year_archive, name='year_archive'),]

views.py

from django.shortcuts import HttpResponse, render,HttpResponseRedirectfrom django.urls import reversedef year_archive(request, year): redirect_url = reverse('year_archive', args=(year,)) print(redirect_url) # /article/2004/ year_list = [2014, 2015] # return HttpResponseRedirect(reverse('year-archive', args=(year,))) # 在render的时候,就会对index.html进行渲染,所以就把a标签中的href设置了值 return render(request, 'index.html', {'year_list': year_list})

index.html

<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title></head><body><a href="{% url 'year_archive' 2013 %}">2013</a>{% for yearvar in year_list %} <a href="{% url 'year_archive' yearvar %}">{{ yearvar }}</a>{% endfor %}</body></html>

4.6名称空间

由于name没有作用域,Django在反向解析URL时,会在全局项目搜索,找到一个name指定的URL时,立即返回。当我们项目中有多个app时,可能会出现name相同的情况,为了避免name相同引发的解析异常,引入了名称空间。

project的urls.py

from django.contrib import adminfrom django.urls import path, re_path, includeurlpatterns = [ path('admin/', admin.site.urls), re_path(r"^app1/", include('app1.urls', namespace="app1")), re_path(r"^app2/", include('app2.urls', namespace="app2")),]

app1的urls.py

#!/usr/bin/env python# -*- coding:utf-8 -*-# Author: vitafrom django.urls import path, re_path, includefrom app1 import viewsapp_name = '[app1]'urlpatterns = [ re_path(r"^articles/([0-9]{4})/$", views.year_archive, name="year_archive" ),]

app1的views.py

from django.shortcuts import HttpResponse, render,HttpResponseRedirectfrom django.urls import reversedef year_archive(request, year): redirect_url = reverse('app1:year_archive', args=(year,)) return HttpResponse("app1 %s"% redirect_url)

app2的urls.py

#!/usr/bin/env python# -*- coding:utf-8 -*-# Author: vitafrom django.urls import path, re_path, includefrom app2 import viewsapp_name = '[app2]'urlpatterns = [ re_path(r"^articles/([0-9]{4})/$", views.year_archive, name="year_archive"),]

app2的views.py

from django.shortcuts import HttpResponse, render,HttpResponseRedirectfrom django.urls import reversedef year_archive(request, year): url = reverse('app2:year_archive', args=(year,)) return HttpResponse("app2 %s" % url)


4.7django2.0版的path

urlpatterns = [ path('admin/', admin.site.urls), re_path(r"^articles/2003/$", views.special_case_2003), re_path(r"^articles/(?P<year>[0-9]{4})/$", views.year_archive), re_path(r"^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$", views.month_archive), re_path(r"^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<article_id>[0-9]+)/$", views.article_detail),]上面的内容,考虑如下两个问题:1.默认ulr中匹配到的year,month是字符串类型,如果我们想要使用int类型,需要在views.py中手动进行类型转换。那是否可以直接在ulr中自动转换为int类型呢。2.上面的year,month都是相同的正则表达式,需要写三次,如果一处修改,三处都要修改。那是否能只修改一处呢4.7.1基本示例

基本规则:1.使用<>从url中捕获值2.捕获值中可以包含一个转化器类型,比如捕获一个整数变量。如果没有转化器,将匹配任何字符串,也包含/字符。3.无需添加前导斜杠/。

urls.py

"""first_django URL ConfigurationThe `urlpatterns` list routes URLs to views. For more information please see: https://docs.djangoproject.com/en/2.2/topics/http/urls/Examples:Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: path('', views.home, name='home')Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))"""from django.contrib import adminfrom django.urls import path, re_path, includefrom app1 import viewsurlpatterns = [ path('admin/', admin.site.urls), path("articles/2003/", views.special_case_2003), path("articles/<int:year>/", views.year_archive), path("articles/<int:year>/<int:month>/", views.month_archive), path("articles/<int:year>/<int:month>/<slug:article_id>/", views.article_detail),]

views.py

from django.shortcuts import HttpResponsedef special_case_2003(request): return HttpResponse("articles/2003/")def year_archive(request, year): print(type(year))#<class 'int'> return HttpResponse("articles/<int:year>/ year:%s" % (year))def month_archive(request, month, year): return HttpResponse("articles/<int:year>/<int:month>/ year:%s ,month:%s " % (year, month))def article_detail(request, article_id, year, month): return HttpResponse("articles/<int:year>/<int:month>/<slug:article_id>/ year:%s ,month:%s ,article_id:%s " % (year, month, article_id))



4.7.2path转化器

1.str匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式。2.int匹配正整数,包含0。3.slug匹配字母、数字以及横杠、下划线组成的字符串4.uuid匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。5.path匹配任何非空字符串,包含路径分隔符4.7.3自定义转化器

对于一些复杂或者复用的需要,可以定义自己的转化器。转化器是一个类或接口,要求下面的三点。1.regex类属性,字符串类型。2.to_python(self,value)方法,value是由regex匹配到的字符串,返回具体的python变量值,以供对应的视图函数使用。3.to_url(self,value)方法,和to_python相反,value是一个具体的python变量值,返回其字符串,通常用于url反向引用,即reverse。

converters.py

#!/usr/bin/env python# -*- coding:utf-8 -*-# Author: vitaclass YearConverter: regex = '[0-9]{4}' def to_python(self,value): print("to_python",value) return int(value) def to_url(self,value): print("to_url",value) return '%04d' % value

urls.py

from django.contrib import adminfrom django.urls import path, register_converterfrom app1 import views,convertersregister_converter(converters.YearConverter,"int_con")urlpatterns = [ path('admin/', admin.site.urls), path("articles/2003/", views.special_case_2003), path("articles/<int_con:year>/<int_con:month>/", views.month_archive,name="month_archive"),]

views.py

from django.shortcuts import HttpResponsefrom django.urls import reversedef special_case_2003(request): return HttpResponse("articles/2003/")def month_archive(request, year, month): print(reverse(month_archive,args=(121,234)))#<class 'int'> return HttpResponse("articles/<int:year>/ year:%s" % (year))


从上面结果可以看出,to_python调用了两次,to_url调用了两次在to_url中,是把args=(121,234)中的参数,每一个参数调用一次to_url。5.视图层--views.py5.1视图函数

from django.shortcuts import render, HttpResponse, HttpResponseRedirect, redirectimport datetimedef current_datetime(request): now = datetime.datetime.now() html = "<html><body>It is now %s.</body></html>" % now return HttpResponse(html)解读代码:1.首先导入模块:from django.shortcuts import render, HttpResponse, HttpResponseRedirect, redirect2.定义视图函数current_datetime。每个视图函数第一个参数都是HttpRequest对象。3.这个视图会返回一个HttpResponse对象,每个视图都负责返回一个HttpResponse对象。5.2HttpRequest对象

django将请求报文中的请求行、首部信息,内容主体封装成HttpRequest类中的属性。1.HttpRequest.GET 一个类似于字典的对象。 get请求时, 浏览器中手动设置参数http://127.0.0.1:8000/login/?user_name=vita&passwd=123 <QueryDict: {'user_name': ['vita'], 'passwd': ['123']}> 不手动设置参数http://127.0.0.1:8000/login/ <QueryDict: {}>2.HttpRequest.POST post请求时, form表单提交 <QueryDict: {'user_name': ['vita'], 'passwd': ['123']}> http://127.0.0.1:8000/index/ url:协议://IP:port/路径?get请求数据3.HttpRequest.path 表示请求的路径 例如:请求http://127.0.0.1:8000/login/?user=vita&passwd=123 print(request.path) # /login/ print(request.get_full_path()) #/login/?user=vita&passwd=1234.HttpRequest.get_full_path() 返回path,如果有查询字符串,查询字符串也在路径中。5.HttpRequest.method 用于判断是GET请求还是POST请求6.HttpRequest.FILES print(request.FILES)#<MultiValueDict: {'myfile': [<InMemoryUploadedFile: Java快捷键.txt (text/plain)>]}> print(type(request.FILES.get("myfile")))# <class 'django.core.files.uploadedfile.InMemoryUploadedFile'> 一个类似于字典的对象,包含所有的上传文件信息。 FILES总的每个键为<input type="file" name="" /> 中的name,值则为对应的数据。 注意:FILES只有在请求的方法为POST且提交的<form>表单中带有属性enctype="multipart/form-data"的情况下才会有数据。 否则,FILES将是一个空的类似于字典的对象。7.HttpRequest.META 一个标准的Python 字典,包含所有的HTTP 首部。具体的头部信息取决于客户端和服务器,下面是一些示例: CONTENT_LENGTH —— 请求的正文的长度(是一个字符串)。 CONTENT_TYPE —— 请求的正文的MIME 类型。 HTTP_ACCEPT —— 响应可接收的Content-Type。 HTTP_ACCEPT_ENCODING —— 响应可接收的编码。 HTTP_ACCEPT_LANGUAGE —— 响应可接收的语言。 HTTP_HOST —— 客服端发送的HTTP Host 头部。 HTTP_REFERER —— Referring 页面。 HTTP_USER_AGENT —— 客户端的user-agent 字符串。 QUERY_STRING —— 单个字符串形式的查询字符串(未解析过的形式)。 REMOTE_ADDR —— 客户端的IP 地址。 REMOTE_HOST —— 客户端的主机名。 REMOTE_USER —— 服务器认证后的用户。 REQUEST_METHOD —— 一个字符串,例如"GET" 或"POST"。 SERVER_NAME —— 服务器的主机名。 SERVER_PORT —— 服务器的端口(是一个字符串)。 从上面可以看到,除 CONTENT_LENGTH 和 CONTENT_TYPE 之外,请求中的任何 HTTP 首部转换为 META 的键时, 都会将所有字母大写并将连接符替换为下划线最后加上 HTTP_ 前缀。 所以,一个叫做 X-Bender 的头部将转换成 META 中的 HTTP_X_BENDER 键。8.HttpRequest.encodingprint(request.encoding) # None一个字符串,表示提交的数据的编码方式(如果为None,表示使用DEFAULT_CHARSET的设置,默认为utf-8)。这个属性是可写的,你可以修改它来修改访问表单数据使用的编码。接下来对属性的任何访问,将使用新的encoding值。如果你知道表单数据的编码不是DEFAULT_CHARSET,则使用它。5.3HttpResponse对象

响应对象主要有三种形式:1.HttpResponse() HttpResponse()括号中直接跟一个具体的字符串作为响应体,比较简单直接。2.render(request, template_name,{"ctime":ctime})def render(request, template_name, context=None, content_type=None, status=None, using=None): """ Return a HttpResponse whose content is filled with the result of calling django.template.loader.render_to_string() with the passed arguments. """ content = loader.render_to_string(template_name, context, request, using=using) return HttpResponse(content, content_type, status)render(request, "index.html",{"ctime":ctime})结合一个给定的模板和一个上下文字典,把字典中的数据渲染到html页面中。request:用于生成响应的请求对象。template_name:html模板名称,可选参数。context:默认是一个字典。如果字典中的某个值是可调用的,视图将在渲染模板之前调用它。render方法就是把一个模板页面中的模板语法进行渲染,最终渲染成一个html页面为响应体。3.redirect()1)传递要重定向的一个硬编码URLdef my_view(request): ... return redirect('/some/url/')def my_view(request): ... return redirect('http://example.com/')2)也可以是一个完整的URLdef my_view(request): ... return redirect('http://example.com/')3)发送了两次请求 ,返回码是302(login为旧地址,auth为跳转的地址)[23/Jun/2019 10:26:15] "GET /login/ HTTP/1.1" 302 0[23/Jun/2019 10:26:15] "GET /auth/ HTTP/1.1" 200 44)return http.HttpResponsePermanentRedirect ('http://www.baidu.com/'),返回码是301,发送了一次请求[23/Jun/2019 10:32:50] "GET /login/ HTTP/1.1" 301 05)302与301的区别:django实现302临时重定向的代码,也可以使用上面的redirectfrom django import httpdef view(request): return http.HttpResponseRedirect('http://www.baidu.com/')django实现301永久重定向from django import httpdef view(request): return http.HttpResponsePermanentRedirect ('http://www.baidu.com/')相同点:1)都表示重定向,浏览器自动跳转到一个新地址,用户看到的效果是输入的A地址瞬间变为了B地址。不同点:1)301表示永久跳转,旧地址被永久的移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时,也将旧地址转换为新地址。永久跳转在后端实际只发生一条请求log,为新地址。2)302表示临时跳转,旧的资源依然可以访问。临时跳转在后端发生了两条请求log,分别为旧地址和新地址。