Django2.2.2
Python3.6.2

1.MVC与MTV模型

1.1MVC

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

2.django入场啦_第1张图片

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张图片

2.Django的下载

2.1下载django

pip3 install django

2.django入场啦_第3张图片

2.2查看django脚本路径

2.django入场啦_第4张图片

2.3创建一个django project

windows上
django-admin.exe startproject mysite

Linux上
django-admin.py startproject mysite

2.django入场啦_第5张图片
2.django入场啦_第6张图片

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

2.4在mysite下创建应用

python manage.py startapp blog

2.django入场啦_第7张图片

2.5启动django项目

python manage.py runserver 8080

2.django入场啦_第8张图片
2.django入场啦_第9张图片

2.6一个简易的Django示例

2.django入场啦_第10张图片

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

2.django入场啦_第11张图片

urls.py

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

views.py

from django.shortcuts import render
import 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




    
    Title


当前时间为:{{ ctime }}

2.django入场啦_第12张图片

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中




2.django入场啦_第13张图片
2.django入场啦_第14张图片
2.django入场啦_第15张图片

2.django入场啦_第16张图片
2.django入场啦_第17张图片
2.django入场啦_第18张图片
2.django入场啦_第19张图片
2.django入场啦_第20张图片

4.路由控制

URL配置就是用户请求的URL与views.py中函数的映射表。通过urls.py,找到对应的函数。

在Django-1.x版本中,使用url
from django.conf.urls import url
urlpatterns = [
    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_path
from django.urls import path, re_path
from app1 import views
urlpatterns = [
    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 admin
from django.urls import path, re_path
from app1 import views
urlpatterns = [
    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 HttpResponse

def 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))

2.django入场啦_第21张图片
2.django入场啦_第22张图片
2.django入场啦_第23张图片
2.django入场啦_第24张图片

说明:
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中匹配到的内容传递给函数。
可以按照参数的名称进行匹配,语法是:
(?Ppattern),其中name是参数名称,pattern是匹配的正则表达式。

注意:
urls.py中
re_path(r"^articles/(?P[0-9]{4})/$", views.year_archive)
函数定义中
def year_archive(request, year)
参数名称要保持相同,都要是year。

urls.py

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

views.py


from django.shortcuts import HttpResponse

def 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))


2.django入场啦_第25张图片
2.django入场啦_第26张图片

这个实现是捕获的值作为关键字参数而不是位置参数传递给视图函数。
/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')),
]

2.django入场啦_第27张图片
2.django入场啦_第28张图片
2.django入场啦_第29张图片
2.django入场啦_第30张图片

4.4登录验证实例

2.django入场啦_第31张图片
2.django入场啦_第32张图片
2.django入场啦_第33张图片
2.django入场啦_第34张图片
2.django入场啦_第35张图片
2.django入场啦_第36张图片
2.django入场啦_第37张图片
2.django入场啦_第38张图片

4.5反向解析

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

在html中:使用url模板标签
在python代码中:使用django.urls.reverse

urls.py

from django.contrib import admin
from django.urls import path, re_path, include
from app1 import views
urlpatterns = [
    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,HttpResponseRedirect

from django.urls import reverse

def 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




    
    Title


2013

{% for yearvar in year_list %}
    {{ yearvar }}
{% endfor %}

2.django入场啦_第39张图片

4.6名称空间

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

project的urls.py

from django.contrib import admin
from django.urls import path, re_path, include
urlpatterns = [
    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: vita
from django.urls import path, re_path, include
from app1 import views
app_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,HttpResponseRedirect

from django.urls import reverse

def 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: vita
from django.urls import path, re_path, include
from app2 import views
app_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,HttpResponseRedirect

from django.urls import reverse

def year_archive(request, year):
    url = reverse('app2:year_archive', args=(year,))
    return HttpResponse("app2 %s" % url)

2.django入场啦_第40张图片
2.django入场啦_第41张图片

4.7django2.0版的path

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r"^articles/2003/$", views.special_case_2003),
    re_path(r"^articles/(?P[0-9]{4})/$", views.year_archive),
    re_path(r"^articles/(?P[0-9]{4})/(?P[0-9]{2})/$", views.month_archive),
    re_path(r"^articles/(?P[0-9]{4})/(?P[0-9]{2})/(?P[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 Configuration

The `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 admin
from django.urls import path, re_path, include
from app1 import views
urlpatterns = [
    path('admin/', admin.site.urls),
    path("articles/2003/", views.special_case_2003),
    path("articles//", views.year_archive),
    path("articles///", views.month_archive),
    path("articles////", views.article_detail),
]

views.py

from django.shortcuts import HttpResponse

def special_case_2003(request):
    return HttpResponse("articles/2003/")

def year_archive(request, year):
    print(type(year))#
    return HttpResponse("articles//   year:%s" % (year))

def month_archive(request, month, year):
    return HttpResponse("articles///   year:%s ,month:%s " % (year, month))

def article_detail(request, article_id, year, month):
    return HttpResponse("articles////  year:%s ,month:%s ,article_id:%s " % (year, month, article_id))

2.django入场啦_第42张图片
2.django入场啦_第43张图片
2.django入场啦_第44张图片

2.django入场啦_第45张图片

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: vita
class 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 admin
from django.urls import path, register_converter
from app1 import views,converters
register_converter(converters.YearConverter,"int_con")
urlpatterns = [
    path('admin/', admin.site.urls),
    path("articles/2003/", views.special_case_2003),
    path("articles///", views.month_archive,name="month_archive"),
]

views.py

from django.shortcuts import HttpResponse
from django.urls import reverse

def special_case_2003(request):
    return HttpResponse("articles/2003/")

def month_archive(request, year, month):
    print(reverse(month_archive,args=(121,234)))#
    return HttpResponse("articles//   year:%s" % (year))

2.django入场啦_第46张图片
2.django入场啦_第47张图片

从上面结果可以看出,to_python调用了两次,to_url调用了两次
在to_url中,是把args=(121,234)中的参数,每一个参数调用一次to_url。

5.视图层--views.py

5.1视图函数

from django.shortcuts import render, HttpResponse, HttpResponseRedirect, redirect
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    html = "It is now %s." % now
    return HttpResponse(html)

解读代码:
1.首先导入模块:from django.shortcuts import render, HttpResponse, HttpResponseRedirect, redirect
2.定义视图函数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
    
    不手动设置参数http://127.0.0.1:8000/login/
    

2.HttpRequest.POST
    post请求时,
    form表单提交
    

    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=123

4.HttpRequest.get_full_path()
    返回path,如果有查询字符串,查询字符串也在路径中。

5.HttpRequest.method
    用于判断是GET请求还是POST请求

6.HttpRequest.FILES
    print(request.FILES)#]}>
    print(type(request.FILES.get("myfile")))# 
    一个类似于字典的对象,包含所有的上传文件信息。
    FILES总的每个键为 中的name,值则为对应的数据。
    注意:FILES只有在请求的方法为POST且提交的
表单中带有属性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.encoding print(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)传递要重定向的一个硬编码URL
def my_view(request):
    ...
    return redirect('/some/url/')def my_view(request):
    ...
    return redirect('http://example.com/')

2)也可以是一个完整的URL
def 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 4

4)return http.HttpResponsePermanentRedirect ('http://www.baidu.com/'),返回码是301,发送了一次请求
[23/Jun/2019 10:32:50] "GET /login/ HTTP/1.1" 301 0

5)302与301的区别:
django实现302临时重定向的代码,也可以使用上面的redirect
from django import http
def view(request):
  return http.HttpResponseRedirect('http://www.baidu.com/')

django实现301永久重定向
from django import http

def view(request):
  return http.HttpResponsePermanentRedirect ('http://www.baidu.com/')

相同点:
1)都表示重定向,浏览器自动跳转到一个新地址,用户看到的效果是输入的A地址瞬间变为了B地址。

不同点:
1)301表示永久跳转,旧地址被永久的移除了(这个资源不可访问了),搜索引擎在抓取新内容的同时,也将旧地址转换为新地址。
永久跳转在后端实际只发生一条请求log,为新地址。
2)302表示临时跳转,旧的资源依然可以访问。
临时跳转在后端发生了两条请求log,分别为旧地址和新地址。