day62---Django框架学习初阶(三)

day62---Django框架学习初阶(三)

有名分组与无名分组的反向解析

无名分组的反向解析

from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^index/(d+)/$',views.index,name='app01_index'),
    url(r'^home/(d+)/$',views.home,name='app01_home')
]

针对无名分组:

我们在浏览器中输入http://127.0.0.1:9090/index/1/
# 在试图函数views中反向解析路径
from django.shortcuts import reverse
from django.shortcuts import redirect

def home(request):
    return redirect(reverse('app01_index',args=(1,)))

# 在模板文件index.html中反向解析路径
<html>
	<head>
    	...
    </head>
	<body>
    	<form action="{% url 'app01_index' 1 %}" method="post">
        	{% crsf_token %}
            ...
        </form>
    </body>
</html>

有名分组的反向解析

from django.conf.urls import url
from app02 import views

urlpatterns = [
    url(r'^index/(?p<id>d+)/$',views.index,name='app02_index'),
    url(r'^home/(?p<id>d+)/$',views.home,name='app02_home')
]

针对有名分组:

我们在浏览器中输入http://127.0.0.1:9090/index/1/
# 在视图函数views中反向解析路径
from django.shortcuts import reverse
from django.shortcuts import redirect

def home(request):
    return redirect(reverse('app02_index',kwargs={'id':1}))

# 在模板文件中反向解析路径
<html>
	<head>
    	...
    </head>
	<body>
    	<form action="{% url 'app02_index' id=1 %}" method="post">
        	{% crsf_token %}
            ...
        </form>
    </body>
</html>

路由分发

"""
django的每一个应用都可以有自己的templates文件夹urls.py static文件夹,
正是基于上述的特点,django能够非常好的做到分组开发(每个人只写自己的app)。

当一个django项目中的url特别多的时候 总路由urls.py代码非常冗余不好维护
这个时候也可以利用路由分发来减轻总路由的压力。

利用路由分发之后,总路由不再干路由与视图函数的直接对应关系,而是做一个分发处理
"""

总路由

from django.conf.urls import url,include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 路由分发(终极写法),include函数就是做分发操作的
    url(r'^app01/',include('app01.urls')),
    url(r'^app02/',include('app02.urls')),
]

"""
注意:总路由里面的url千万不能以$结尾
"""

子路由app01/urls.py

from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^index/(d+)/$',views.index,name='app01_index'),
    url(r'^home/(d+)/$',views.home,name='app01_home')
]

子路由app02/urls.py

from django.conf.urls import url
from app02 import views

urlpatterns = [
    url(r'^index/(?p<id>d+)/$',views.index,name='app02_index'),
    url(r'^home/(?p<id>d+)/$',views.home,name='app02_home')
]

名称空间(了解)

# 当我们在项目下创建多个app,并且每个app都针对匹配的路径设置了别名,当别名存在重复,那么反向解析时会存在覆盖现象。

"""
解决上述的问题,主要有两种思路:
(1)规避这种出现别名重复的风险,我们可以在别名前面加上app名称加以区别,如app01_index;
(2)在总路由中通过名称空间加以区别。
"""

方法一:
from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^index/(d+)/$',views.index,name='app01_index'),
    url(r'^home/(d+)/$',views.home,name='app01_home')
]

方法二:
总路由
from django.conf.urls import url,include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 路由分发
    url(r'^app01/',include(('app01.urls','app01'))),
    url(r'^app02/',include(('app02.urls','app02'))),
]

"""
传给include功能一个元组,第一个参数是路由分发地址,第二参数是指名称空间NameSpace

也可以直接这样写:
url(r'^app01/',include('app01.urls',namespace='app01'))
"""

# 在视图函数views解析时
from django.shortcuts import redirect
from django.shortcuts import reverse
def home(request):
    return redirct(reverse('app01:index',args(1,)))

# 在模板文件app02_index.html解析时:
<html>
	<head>
    	...
    </head>
	<body>
    	<form action="{% url 'app02:index' id=1 %}" method="post">
        	{% crsf_token %}
            ...
        </form>
    </body>
</html>

总结+补充

1.在视图函数中基于名称空间的反向解析,其用法如下
reverse('名称空间的名字:待解析的别名')
2.在模板中的基于名称空间的反向解析,其用法如下:
<a href="{% url '名称空间的名字:待解析的别名' %}">妹子图</a>

伪静态(了解)

伪静态:就是将一个动态网页伪装成静态网页,如https://www.cnblogs.com/surpass123/p/12969043.html
    
"""
伪装的目的:
(1)在于增大本网站的seo查询力度;
(2)并且增加搜索引擎收藏本网上的概率
"""

实现:
from django.conf.urls import url
from app01 import views

urlpatterns = [
    url(r'^index.html',views.index,name='app01_index'),
    url(r'^home.html',views.home,name='app01_home')
]

虚拟环境(了解)

"""
【拓展】:
    在正常开发中 我们会给每一个项目配备一个该项目独有的解释器环境,该环境内只有该项目用到的模块,其他用不到一概不装;
    通常会给每个项目配置一个requirements.txt文件,里面书写了该项目所有的模块即版本,我们只需要直接输入一条命令即可一键安装所有模块即版本
"""

Django版本之间的区别

  • 路由层的使用方法
"""
Django 1.x 使用的url()方法来正则匹配路径,Django 2.x和Django 3.x推荐使用path()方法来配置路径,同时也提供了re_path()方法来正则匹配路径。

url():第一个参数支持正则
path():第一个参数不支持正则,写什么就匹配什么

re_path就等价于Django 1.x 版本中的url


from django.urls import re_path # django2.0中的re_path
from django.conf.urls import url # 在django2.0中同样可以导入1.0中的url

urlpatterns = [
    # 用法完全一致
    url(r'^app01/', include(('app01.urls','app01'))),
    re_path(r'^app02/', include(('app02.urls','app02'))),
]
"""

重要:path功能主要是用来解决数据类型转换问题与正则表达式冗余问题。

from django.urls import re_path

from app01 import views

urlpatterns = [
    # 问题一:数据类型转换
    # 正则表达式会将请求路径中的年份匹配成功然后以str类型传递函数year_archive,在函数year_archive中如果想以int类型的格式处理年份,则必须进行数据类型转换
    re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),

    # 问题二:正则表达式冗余
    # 下述三个路由中匹配article_id采用了同样的正则表达式,重复编写了三遍,存在冗余问题,并且极不容易管理,因为一旦article_id规则需要改变,则必须同时修改三处代码
    
    re_path(r'^article/(?P<article_id>[a-zA-Z0-9]+)/detail/$', views.detail_view),
    re_path(r'^articles/(?P<article_id>[a-zA-Z0-9]+)/edit/$', views.edit_view),
    re_path(r'^articles/(?P<article_id>[a-zA-Z0-9]+)/delete/$', views.delete_view),
]

path解决上述问题的示例:

from django.urls import path,re_path

from app01 import views

urlpatterns = [
    # 问题一的解决方案:
    path('articles/<int:year>/', views.year_archive), # <int:year>相当于一个有名分组,其中int是django提供的转换器,相当于正则表达式,专门用于匹配数字类型,而year则是我们为有名分组命的名,并且int会将匹配成功的结果转换成整型后按照格式(year=整型值)传给函数year_archive


    # 问题二解决方法:用一个int转换器可以替代多处正则表达式
    path('articles/<int:article_id>/detail/', views.detail_view), 
    path('articles/<int:article_id>/edit/', views.edit_view),
    path('articles/<int:article_id>/delete/', views.delete_view),
]

强调:

#1、path与re_path或者1.0中的url的不同之处是,传给path的第一个参数不再是正则表达式,而是一个完全匹配的路径,相同之处是第一个参数中的匹配字符均无需加前导斜杠

#2、使用尖括号(<>)从url中捕获值,相当于有名分组

#3、<>中可以包含一个转化器类型(converter type),比如使用 <int:name> 使用了转换器int。若果没有转化器,将匹配任何字符串,当然也包括了 / 字符

path虽然不支持正则,但是它内部支持5中转换器

str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
int,匹配正整数,包含0。
slug,匹配字母、数字以及横杠、下划线组成的字符串。
uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)

除了原有默认的5个转换器外,还支持自定义的转换器

1.在app01下新建文件path_converters.py,文件名可以随意命名

class MonthConverter:
    regex='d{2}' # 属性名必须为regex

    def to_python(self, value):
        return int(value)

    def to_url(self, value):
        return value # 匹配的regex是两个数字,返回的结果也必须是两个数字

2.在urls.py中,使用register_converter将其注册到URL配置中:

from django.urls import path,register_converter
from app01.path_converts import MonthConverter

register_converter(MonthConverter,'mon')

from app01 import views


urlpatterns = [
    path('articles/<int:year>/<mon:month>/<slug:other>/', views.article_detail, name='aaa'),

]

3.views.py中的视图函数article_detail

from django.shortcuts import render,HttpResponse,reverse

def article_detail(request,year,month,other):
    print(year,type(year))
    print(month,type(month))
    print(other,type(other))
    print(reverse('xxx',args=(1988,12,'hello'))) # 反向解析结果/articles/1988/12/hello/
    return HttpResponse('xxxx')
在模型层里面1.X外键默认都是级联更新删除的,但是到了2.X和3.X中需要你自己手动配置参数。
1.x
models.ForeignKey(to='Publish')

2.x或3.x
models.ForeignKey(to='Publish',on_delete=models.CASCADE...)

视图层

三大神器

"""
HttpResponse:返回字符串类型

render: 返回html页面,并且在返回给浏览器之前,还可以html文件传值

redirect:重定向
"""
这三者返回的都是一个HttpResponse对象


# render简单内部实现原理
from django.template import Template,Context
    res = Template('<h1>{{ user }}</h1>')
    con = Context({'user':{'username':'jason','password':123}})
    ret = res.render(con)
    print(ret)
    return HttpResponse(ret)

JsonResponse类

json格式的数据有什么用?

前后端数据的交互要使用json作为过渡,实现跨语言的传输数据

# 序列化与反序列化
前端                               后端
JSON.stringify()                   json.dumps()
JSON.parse()                       json.loads()

示例:

import json

def test(request):
    user_dict = {'username': '天王盖地虎', 'password': 'jason一米五'}
    user_str = json.dumps(user_dict)
    return HttpResponse(user_str)

可以看到,在页面上显示的是Unicode,我们可以通过设置dumps的ensure_ascii参数,设置其为False

user_str = json.dumps(user_dict,ensure_ascii=False)

# 刷新一下页面

django中提供了一种JsonResponse类帮我们实现上述的流程

from django.http import JsonResponse


def test(request):
    user_dict = {'username': '天王盖地虎', 'password': 'jason一米五'}
    return JsonResponse(user_dict)

发现又显示的unicode,这时候我们看JsonResponse源码

class JsonResponse(HttpResponse):
    """
    An HTTP response class that consumes data to be serialized to JSON.

    :param data: Data to be dumped into json. By default only ``dict`` objects
      are allowed to be passed due to a security flaw before EcmaScript 5. See
      the ``safe`` parameter for more information.
    :param encoder: Should be an json encoder class. Defaults to
      ``django.core.serializers.json.DjangoJSONEncoder``.
    :param safe: Controls if only ``dict`` objects may be serialized. Defaults
      to ``True``.
    :param json_dumps_params: A dictionary of kwargs passed to json.dumps().
    """

    def __init__(self, data, encoder=DjangoJSONEncoder, safe=True,
                 json_dumps_params=None, **kwargs):
        if safe and not isinstance(data, dict):
            raise TypeError(
                'In order to allow non-dict objects to be serialized set the '
                'safe parameter to False.'
            )
        if json_dumps_params is None:
            json_dumps_params = {}
        kwargs.setdefault('content_type', 'application/json')
        data = json.dumps(data, cls=encoder, **json_dumps_params)
        super(JsonResponse, self).__init__(content=data, **kwargs)

其中,

"""
param json_dumps_params: A dictionary of kwargs passed to json.dumps().
"""
data = json.dumps(data, cls=encoder, **json_dumps_params)

可以发现JsonResponse是内部是调用了json的dumps方法,它通过json_dumps_params来控制dunps内的传入参数,到这步,问题基本可以得到解决了。

return JsonResponse(user_dict,json_dumps_params={'ensure_ascii':False})
**json_dumps_params ==> ensure_ascii=False

那么,能否将列表序列化了?

如下列表,beast = ['姜春',' 李乾新','王林']

def test(request):
    beast = ['姜春','李乾新','王林']
    return JsonResponse(beast)

运行后报错了,提示如下:

TypeError: In order to allow non-dict objects to be serialized set the safe parameter to False.
    
为了使非字典对象(默认是字典对象)可以被序列化,请将safe参数设置为False

def test(request):
    beast = ['姜春','李乾新','王林']
    return JsonResponse(beast,safe=False,json_dumps_params={'ensure_ascii':False})

form表单上传文件

form表单上传文件
"""
1.method必须指定成post
2.enctype必须换成formdata
"""

def upload_files(request):
    if request.method == 'POST':
        file_obj = request.FILES.get('file') # 获取文件对象
        with open(file_obj.name, mode='wb') as f:
            for line in file_obj.chunks():  # 推荐加上chunks方法 
                f.write(line)
        return HttpResponse(f'<h3>upload file {file_obj.name} success!</h3>')
    return render(request, 'upload_files.html')

request对象方法

"""
request.method
request.POST
request.GET
request.FILES
request.body  # 原生的浏览器发过来的二进制数据  后面详细的讲
request.path 
request.path_info
request.get_full_path()  能过获取完整的url及问号后面的参数 
"""
    print(request.path)  # /app01/ab_file/
    print(request.path_info)  # /app01/ab_file/
    print(request.get_full_path())  # /app01/ab_file/?username=jason

FBV与CBV

# 视图函数既可以是函数也可以是类
def index(request):
  return HttpResponse('index')

# CBV
# CBV路由
    url(r'^login/$', views.MyLogin.as_view(),name='login')

from django.views import View

class MyLogin(View):
    def get(self, request):
        return render(request, 'login.html')

    def post(self, request):
        return HttpResponse('post方法')

      
"""
FBV和CBV各有千秋
CBV特点
	能够直接根据请求方式的不同直接匹配到对应的方法执行
	内部到底是怎么实现的?
		CBV内部源码(******)
"""
原文地址:https://www.cnblogs.com/surpass123/p/12977051.html