zoukankan      html  css  js  c++  java
  • 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内部源码(******)
    """
    
  • 相关阅读:
    给Debian安装Xfce桌面
    【兄弟连ThinkPHP】1、介绍和安装
    CROSS JOIN,NATURAL JOIN
    表的连接查询
    多表查询在数据量非常大的时候性能不好,慎用!
    伪列:Oracle显示查询结果前几条记录用rownum<=。去掉重复记录,保留最早录入记录:取出最小ROWID
    其他函数:值为NULL时的默认值NVL,DECODE
    转换函数TO_CHAR,TO_DATE,TO_NUMBER
    50-用Python监听鼠标和键盘事件
    49-Python 安装pythoncom库和pyHook
  • 原文地址:https://www.cnblogs.com/surpass123/p/12977051.html
Copyright © 2011-2022 走看看