zoukankan      html  css  js  c++  java
  • [Python自学] day-21 (1) (请求信息、html模板继承与导入、自定义模板函数、自定义分页)

    一、路由映射的参数

    1.映射的一般使用

    在app/urls.py中,我们定义URL与视图函数之间的映射:

    from django.contrib import admin
    from django.urls import path
    from django.urls import re_path
    
    from mgmt import views
    
    urlpatterns = [
        path('index', views.index),
        path('host/', views.host),
        re_path('test_ajax$', views.test_ajax),
    ]

    我们查看一下path()和re_path()的源码,可以看到参数信息:

    def _path(route, view, kwargs=None, name=None, Pattern=None):
        pass

    除了route表示url字符串,view表示视图函数的引用,name表示别名(可以在页面中用{% url name %}取url值)。

    2.在映射中传递参数

    还有一个形参是kwargs,这个参数接收一个字典,用来传递参数给视图函数:

    from django.contrib import admin
    from django.urls import path
    from django.urls import re_path
    
    from mgmt import views
    
    urlpatterns = [
        path('index', views.index),
        path('host/', views.host),
        re_path('test_ajax$', views.test_ajax),
        path('test', views.test, {'name': 'root'}),
    ]

    在视图函数的实现中,可以接受参数name:

    # test视图函数,接受参数name
    def test(request, name):
        print(name)
        return HttpResponse("OK")

    二、路由系统中的命名空间

    (暂不清楚有什么用,感觉就是第一层urls.py映射的name参数,然后和App urls.py中的映射的name参数一起来反射URL)

    1.未使用路由分发时

    当我们没有使用路由分发的时候,访问/index页面,直接使用http://127.0.0.0:8000/index就可以访问。

    如果我们为其映射传入一个name参数:(参考:[Python自学] day-19 (1) (Django框架) 第六节)

    path('test', views.test, {'name': 'root'}, name='tt')

    我们在视图函数中,可以使用reverse来利用name获取URL:

    # test视图函数,接受参数name
    def test(request, name):
        # 通过name='tt'来获取URL 'test'
        from django.urls import reverse
        url = reverse('tt')
        
        print(name)
        return HttpResponse("OK")

    2.使用路由分发时(分层)

    当我们使用了路由分发,urls.py分为两层。

    第一层是Django工程目录下的urls.py:

    from django.contrib import admin
    from django.urls import path
    from django.urls import include
    
    urlpatterns = [
        path('cmdb/', include("cmdb.urls")),
        path('mgmt/', include("mgmt.urls")),
    ]

    第二层才是App的urls.py(这里以mgmt为例):

    from django.contrib import admin
    from django.urls import path
    from django.urls import re_path
    
    from mgmt import views
    
    urlpatterns = [
        path('index', views.index),
        path('host/', views.host),
        re_path('test_ajax$', views.test_ajax),
        path('test', views.test, {'name': 'root'}, name='tt'),
    ]

    假设,第一层urls.py变为这样:

    from django.contrib import admin
    from django.urls import path
    from django.urls import include
    
    urlpatterns = [
        # path('cmdb/', include("cmdb.urls")),
        path('mgmt/', include("mgmt.urls")),
        path('mgmt2/', include("mgmt.urls")),
    ]

    这里的mgmt/ 和mgmt2/都指向mgmt App的urls.py。这就会导致我们用http://127.0.0.0:8000/mtmg/test和http://127.0.0.0:8000/mtmg2/test访问到的视图函数是同一个。

    在这种情况下,如果我们想要在视图函数中分别获取两个不同的url,例如mgmt/test和mgmt2/test。可以为每个第一层映射添加一个命名空间:

    from django.contrib import admin
    from django.urls import path
    from django.urls import include
    
    urlpatterns = [
        # path('cmdb/', include("cmdb.urls")),
        path('mgmt/', include("mgmt.urls"), namespace='m1'),
        path('mgmt2/', include("mgmt.urls"), namespace='m2'),
    ]

    使用命名空间的情况下,App urls.py还需要添加一个app_name变量:

    from django.contrib import admin
    from django.urls import path
    from django.urls import re_path
    
    from mgmt import views
    
    app_name = 'mgmt'
    urlpatterns = [
        path('index', views.index),
        path('host/', views.host),
        re_path('test_ajax$', views.test_ajax),
        path('test', views.test, {'name': 'root'}, name='tt'),
    ]

    然后,我们在视图函数中使用reverse来获取URL:

    # test视图函数,接受参数name
    def test(request, name):
        from django.urls import reverse
        # 通过'namespace:name',来获取url
        url = reverse('m1:tt')
        url2 = reverse('m2:tt')
        print(url)
        print(url2)
        return HttpResponse("OK")

    三、request获取请求信息

    1.在Views.py中看看request的类

    def test(request, name):
        # 打印type(request)
        print(type(request))
    
        return HttpResponse("OK")

    打印结果:<class 'django.core.handlers.wsgi.WSGIRequest'>

    2.查看WSGIRequest类的源码

    class WSGIRequest(HttpRequest):
        def __init__(self, environ):
            script_name = get_script_name(environ)
            # If PATH_INFO is empty (e.g. accessing the SCRIPT_NAME URL without a
            # trailing slash), operate as if '/' was requested.
            path_info = get_path_info(environ) or '/'
            self.environ = environ
            self.path_info = path_info
            # be careful to only replace the first slash in the path because of
            # http://test/something and http://test//something being different as
            # stated in https://www.ietf.org/rfc/rfc2396.txt
            self.path = '%s/%s' % (script_name.rstrip('/'),
                                   path_info.replace('/', '', 1))
            self.META = environ
            self.META['PATH_INFO'] = path_info
            self.META['SCRIPT_NAME'] = script_name
            self.method = environ['REQUEST_METHOD'].upper()
            # Set content_type, content_params, and encoding.
            self._set_content_type_params(environ)
            try:
                content_length = int(environ.get('CONTENT_LENGTH'))
            except (ValueError, TypeError):
                content_length = 0
            self._stream = LimitedStream(self.environ['wsgi.input'], content_length)
            self._read_started = False
            self.resolver_match = None
    
        def _get_scheme(self):
            return self.environ.get('wsgi.url_scheme')
    
        @cached_property
        def GET(self):
            # The WSGI spec says 'QUERY_STRING' may be absent.
            raw_query_string = get_bytes_from_wsgi(self.environ, 'QUERY_STRING', '')
            return QueryDict(raw_query_string, encoding=self._encoding)
    
        def _get_post(self):
            if not hasattr(self, '_post'):
                self._load_post_and_files()
            return self._post
    
        def _set_post(self, post):
            self._post = post
    
        @cached_property
        def COOKIES(self):
            raw_cookie = get_str_from_wsgi(self.environ, 'HTTP_COOKIE', '')
            return parse_cookie(raw_cookie)
    
        @property
        def FILES(self):
            if not hasattr(self, '_files'):
                self._load_post_and_files()
            return self._files
    
        POST = property(_get_post, _set_post)

    我们可以看到,在创建一个request对象时,构造函数有一个参数叫做environ。这个参数就包含了所有来自客户端的请求信息

    我们还可以看到GET()和POST是如何从environ中获取相应数据的,Django帮我们将常用的数据封装成了字典或列表形式,方便我们使用。

    除了Django为我们封装好的部分常用数据,如果我们还需要其他的数据,就需要我们自己从environ中获取,并处理。

    3.打印environ中的信息

    def test(request, name):
        dic = request.environ  # 使用request.META可以取到一样的数据
        for k, v in dic.items():
            print(k, '<=====>', v)
    
        return HttpResponse("OK")

    打印结果:

    ALLUSERSPROFILE <=====> C:ProgramData
    APPDATA <=====> C:UsersAdministratorAppDataRoaming
    COMMONPROGRAMFILES <=====> C:Program FilesCommon Files
    COMMONPROGRAMFILES(X86) <=====> C:Program Files (x86)Common Files
    COMMONPROGRAMW6432 <=====> C:Program FilesCommon Files
    COMPUTERNAME <=====> 8RBSKRQCXJM9LF7
    COMSPEC <=====> C:Windowssystem32cmd.exe
    DJANGO_SETTINGS_MODULE <=====> secondsite.settings
    HOMEDRIVE <=====> C:
    HOMEPATH <=====> UsersAdministrator
    IDEA_INITIAL_DIRECTORY <=====> C:UsersAdministratorDesktop
    LOCALAPPDATA <=====> C:UsersAdministratorAppDataLocal
    LOGONSERVER <=====> \8RBSKRQCXJM9LF7
    NUMBER_OF_PROCESSORS <=====> 4
    ONEDRIVE <=====> C:UsersAdministratorOneDrive
    OS <=====> Windows_NT
    PATH <=====> D:pycharm_workspacesecondsitevenvScripts;C:Windowssystem32;C:Windows;C:WindowsSystem32Wbem;C:WindowsSystem32WindowsPowerShellv1.0;C:Program Files (x86)NVIDIA CorporationPhysXCommon;C:Program FilesNVIDIA CorporationNVIDIA NvDLISR;D:AppsGitcmd;D:Dev_appsAnaconda5.3.0Scripts;D:Dev_appsAnaconda5.3.0;C:UsersAdministratorAppDataLocalMicrosoftWindowsApps;;D:AppsPyCharm 2019.3in;;D:Dev_appsAnaconda5.3.0libsite-packages
    umpy.libs;D:Dev_appsAnaconda5.3.0libsite-packages
    umpy.libs
    PATHEXT <=====> .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
    PROCESSOR_ARCHITECTURE <=====> AMD64
    PROCESSOR_IDENTIFIER <=====> Intel64 Family 6 Model 94 Stepping 3, GenuineIntel
    PROCESSOR_LEVEL <=====> 6
    PROCESSOR_REVISION <=====> 5e03
    PROGRAMDATA <=====> C:ProgramData
    PROGRAMFILES <=====> C:Program Files
    PROGRAMFILES(X86) <=====> C:Program Files (x86)
    PROGRAMW6432 <=====> C:Program Files
    PROMPT <=====> (venv) $P$G
    PSMODULEPATH <=====> C:Program FilesWindowsPowerShellModules;C:Windowssystem32WindowsPowerShellv1.0Modules;C:Program FilesIntelWired Networking
    PUBLIC <=====> C:UsersPublic
    PYCHARM <=====> D:AppsPyCharm 2019.3in;
    PYCHARM_DISPLAY_PORT <=====> 63342
    PYCHARM_HOSTED <=====> 1
    PYTHONIOENCODING <=====> UTF-8
    PYTHONPATH <=====> D:pycharm_workspacesecondsite;D:AppsPyCharm 2019.3pluginspythonhelperspycharm_matplotlib_backend;D:AppsPyCharm 2019.3pluginspythonhelperspycharm_display
    PYTHONUNBUFFERED <=====> 1
    SESSIONNAME <=====> Console
    SYSTEMDRIVE <=====> C:
    SYSTEMROOT <=====> C:Windows
    TEMP <=====> C:UsersADMINI~1AppDataLocalTemp
    TMP <=====> C:UsersADMINI~1AppDataLocalTemp
    USERDOMAIN <=====> 8RBSKRQCXJM9LF7
    USERDOMAIN_ROAMINGPROFILE <=====> 8RBSKRQCXJM9LF7
    USERNAME <=====> Administrator
    USERPROFILE <=====> C:UsersAdministrator
    VIRTUAL_ENV <=====> D:pycharm_workspacesecondsitevenv
    WINDIR <=====> C:Windows
    _OLD_VIRTUAL_PATH <=====> C:Windowssystem32;C:Windows;C:WindowsSystem32Wbem;C:WindowsSystem32WindowsPowerShellv1.0;C:Program Files (x86)NVIDIA CorporationPhysXCommon;C:Program FilesNVIDIA CorporationNVIDIA NvDLISR;D:AppsGitcmd;D:Dev_appsAnaconda5.3.0Scripts;D:Dev_appsAnaconda5.3.0;C:UsersAdministratorAppDataLocalMicrosoftWindowsApps;;D:AppsPyCharm 2019.3in;
    _OLD_VIRTUAL_PROMPT <=====> $P$G
    RUN_MAIN <=====> true
    SERVER_NAME <=====> 8RBSKRQCXJM9LF7
    GATEWAY_INTERFACE <=====> CGI/1.1
    SERVER_PORT <=====> 8000
    REMOTE_HOST <=====> 
    CONTENT_LENGTH <=====> 
    SCRIPT_NAME <=====> 
    SERVER_PROTOCOL <=====> HTTP/1.1
    SERVER_SOFTWARE <=====> WSGIServer/0.2
    REQUEST_METHOD <=====> GET
    PATH_INFO <=====> /mgmt/test
    QUERY_STRING <=====> 
    REMOTE_ADDR <=====> 127.0.0.1
    CONTENT_TYPE <=====> text/plain
    HTTP_HOST <=====> 127.0.0.1:8000
    HTTP_CONNECTION <=====> keep-alive
    HTTP_CACHE_CONTROL <=====> max-age=0
    HTTP_UPGRADE_INSECURE_REQUESTS <=====> 1
    HTTP_USER_AGENT <=====> Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36
    HTTP_SEC_FETCH_USER <=====> ?1
    HTTP_ACCEPT <=====> text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
    HTTP_SEC_FETCH_SITE <=====> none
    HTTP_SEC_FETCH_MODE <=====> navigate
    HTTP_ACCEPT_ENCODING <=====> gzip, deflate, br
    HTTP_ACCEPT_LANGUAGE <=====> zh-CN,zh;q=0.9
    wsgi.input <=====> <django.core.handlers.wsgi.LimitedStream object at 0x000001CDBF3FF5C0>
    wsgi.errors <=====> <_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>
    wsgi.version <=====> (1, 0)
    wsgi.run_once <=====> False
    wsgi.url_scheme <=====> http
    wsgi.multithread <=====> True
    wsgi.multiprocess <=====> False
    wsgi.file_wrapper <=====> <class 'wsgiref.util.FileWrapper'>

    我们可以从中看到很多熟悉的字段。

    如果我们在访问页面是使用GET提交数据(leo=111),则在environ中可以看到一个叫做 "QUERY_STRING" 的字段,他的值为"leo=111"。

    4.从environ中获取POST提交的数据

    当我们使用POST提交时,我们可以从environ中获取POST传递的数据:

    def test(request, name):
        # 获取请求体的长度
        request_body_size = int(request.environ.get('CONTENT_LENGTH', 0))
        print("CONTENT_LENGTH:", request_body_size)
        # 按请求体长度读取body
        request_body = request.environ['wsgi.input'].read(request_body_size)
        # 打印body的内容
        print("request_body:", request_body.decode('utf-8'))
    
        return HttpResponse("OK")

    打印结果为:

    CONTENT_LENGTH: 49
    request_body: hostname=host1&ip=192.168.1.1&port=1234&busi_id=4

    我们可以看到,POST提交的数据,只是将其放在了body中,格式和GET提交时一样。在浏览器F12中可以验证:

    5.请求头和请求体数据

    当django拿到请求后,会将请求头和请求体分开处理,我们可以通过以下方式获取:

    request.headers  #获取请求头
    request.body  #获取请求体,例如POST提交的数据
    request.META  #获取和environ一样的数据,包含服务器本地的信息和请求头信息

    四、HTML模板继承

    如果多个HTML页面,只有内容部分不一样,标题栏和导航栏都是一样的。我们无需要每个页面都重复写一样的部分。

    1.先写好标题栏和导航栏

    <!DOCTYPE html>
    <html lang="en">
        <meta charset="UTF-8">
        <title>Title</title>
        <link rel="stylesheet" href="/static/commons.css"/>
        <style>
            .pg-header{
                height: 48px;
                background-color: seagreen;
                color: lightgrey;
            }
            .left-navi{
                width: 200px;
                position: fixed;
                left: 0;
                top:48px;
                bottom: 0;
                background-color: cadetblue;
                color: lightgray;
            }
        </style>
    </head>
    <body>
        <div class="pg-header"><h3 style="display: inline">后台管理平台</h3></div>
        <div class="left-navi">
            <p><a href="#">主机管理</a></p>
            <p><a href="#">用户管理</a></p>
            <p><a href="#">应用管理</a></p>
        </div>
        <script src="/static/jquery-1.12.4.js"></script>
    </body>
    </html>

    效果如下:

    2.将master.html改写为可继承的模板

    <!DOCTYPE html>
    <html lang="en">
        <meta charset="UTF-8">
        <title>{% block title %}<!-- 这里用于替换title -->{% endblock %}</title>
        <link rel="stylesheet" href="/static/commons.css"/>
        <style>
            .pg-header{
                height: 48px;
                background-color: seagreen;
                color: lightgrey;
            }
            .left-navi{
                width: 200px;
                position: fixed;
                left: 0;
                top:48px;
                bottom: 0;
                background-color: cadetblue;
                color: lightgray;
            }
            .content{
                left:200px;
                top:48px;
                position: fixed;
                right: 0;
                bottom: 0;
                background-color: #dddddd;
            }
        </style>
    </head>
    <body>
        <div class="pg-header"><h3 style="display: inline">{% block h3 %}{% endblock %}</h3></div>
        <div class="left-navi">
            <p><a href="#">主机管理</a></p>
            <p><a href="#">用户管理</a></p>
            <p><a href="#">应用管理</a></p>
        </div>
        <div class="content">
            {% block content %}
            <!-- 这里用于替换content -->
            {% endblock %}
        </div>
        <script src="/static/jquery-1.12.4.js"></script>
    </body>
    </html>

    我们使用{%block block_name%}{%endblock%}来指定要替换的内容。这样这个模板就可以被其他html继承了。

    3.hostmanage.html继承master.html

    例如,我们有一个主机管理页面和应用界面,导航和标题栏样式都使用master.html。

    hostmanage.html:

    <!-- 继承master.html -->
    {% extends 'master.html' %}
    
    <!-- 填充内容 -->
    {% block title %}主机管理{% endblock %}
    {% block h3 %}主机管理{% endblock %}
    {% block content %}
        <ul>
            {% for i in host_list %}
                <li>{{ i.hostname }}</li>
            {% endfor %}
        </ul>
    {% endblock %}

    对应视图函数:

    def hostmanage(request):
        objs = models.Host.objects.all()
        return render(request, 'hostmanage.html', {'host_list': objs})

    效果如下:

    appmanage.html:

    <!-- 继承master.html -->
    {% extends 'master.html' %}
    
    <!-- 填充内容 -->
    {% block title %}应用管理{% endblock %}
    {% block h3 %}应用管理{% endblock %}
    {% block content %}
        <ul>
            {% for i in app_list %}
                <li>{{ i.appname }}</li>
            {% endfor %}
        </ul>
    {% endblock %}

    对应视图函数:

    def appmanage(request):
        objs = models.Application.objects.all()
        return render(request, 'appmanage.html', {'app_list': objs})

    效果如下:

    4.各页面内容使用自己的css和JS

    当主机管理页面和应用管理页面的内容都需要使用自己独有的css和js时,则需要在master.html再添加一个放置css和js的地方:

    <!DOCTYPE html>
    <html lang="en">
        <meta charset="UTF-8">
        <title>{% block title %}<!-- 这里用于替换title -->{% endblock %}</title>
        <link rel="stylesheet" href="/static/commons.css"/>
        <style>
            <!-- master.html中使用的css -->
        </style>
        {% block css %}{% endblock %}
    </head>
    <body>
        <!-- master.html中的固定内容 -->
        {% block content %}
            <!-- 这里用于替换content -->
        {% endblock %}
        <script src="/static/jquery-1.12.4.js"></script>
        {% block js %}{% endblock %}
    </body>
    </html>    

    当我们的hostmanage.html继承master.html的时候,可以将自己的css和js替换到相应的位置:

    <!-- 继承master.html -->
    {% extends 'master.html' %}
    
    <!-- 独有的CSS -->
    {% block css %}
        <link rel="stylesheet" href="/static/host_content.css"/>
    {% endblock %}
    
    <!-- 填充内容 -->
    {% block title %}主机管理{% endblock %}
    {% block h3 %}主机管理{% endblock %}
    {% block content %}
        <ul>
            {% for i in host_list %}
                <li>{{ i.hostname }}</li>
            {% endfor %}
        </ul>
    {% endblock %}
    <!-- 独有的JS -->
    {% block css %}
        <script src="/static/host_content.js"></script>
    {% endblock %}

    五、HTML导入小组件

    第四节中,html继承于一个模板,相当于该html的内容嵌入到被继承的模板中,最终形成一个完整的html模板。被继承模板 > 继承模板

    而在这节中,我们可以在html中导入其他html实现好的组件,从而完善本html页面。导入模板 > 被导入模板

    1.实现一个小组件(单独在一个html中实现)

    tag.html:

    <div>
        <form>
            <div>用户名:<input type="text"/></div>
            <div>密码:<input type="password"/></div>
            <div>Email:<input type="text"/></div>
            <div>Phone:<input type="text"/></div>
            <div><input type="submit" value="确定"/></div>
        </form>
    </div>

    2.在include.html导入tag.html组件

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Include</title>
        <style>
            .pg-header{
                height: 48px;
                background-color: seagreen;
                color: lightgrey;
            }
        </style>
    </head>
    <body>
        <div class="pg-header"><h3 style="display: inline">登录页面</h3></div>
        {% include 'tag.html' %}
    </body>
    </html>

    tag.html的内容就会被放到{%include 'tag.html'%}的位置。

    3.添加urls.py和视图函数,并运行观察效果

    访问http://127.0.0.1:8000/mgmt/include

    4.如果组件搭配有css和js文件,则记得在include.html中导入

    例如,我们为tag.html实现一个tag.css和tag.js文件:

    tag.css:

    #tag_form{
        background-color: aquamarine;
    }

    tag.js:

    $('#tag_form').find('input[type="button"]').click(function(){
        alert('这是测试按钮');
    });

    在include.html导入tag.html的时候,需要同时导入tag.css和tag.js:

    <!DOCTYPE html>
    <html lang="en" xmlns="http://www.w3.org/1999/html">
    <head>
        <meta charset="UTF-8">
        <title>Include</title>
        <style>
            .pg-header{
                height: 48px;
                background-color: seagreen;
                color: lightgrey;
            }
        </style>
        <link rel="stylesheet" href="/static/tag.css"/>
    </head>
    <body>
        <div class="pg-header"><h3 style="display: inline">登录页面</h3></div>
        {% include 'tag.html' %}
        <script src="/static/jquery-1.12.4.js"></script>
        <script src="/static/tag.js"></script>
    </body>
    </html>

    观察效果:

    六、Django提供的模板函数

    我们在前面的章节使用模板语言,只是将render传递的数据简单的展示出来。而Django为我们提供的模板语言,其实还可以对数据进行二次加工。

    Django为我们提供了一些模板语言函数

    列举一些简单函数:

    <!DOCTYPE html>
    <html lang="en" xmlns="http://www.w3.org/1999/html">
    <head>
        <meta charset="UTF-8">
        <title>Include</title>
        <style>
            .pg-header{
                height: 48px;
                background-color: seagreen;
                color: lightgrey;
            }
        </style>
    </head>
    <body>
        <div class="pg-header"><h3 style="display: inline">登录页面</h3></div>
        <div>
            <p>{{ test_string }}</p>
            <p>{{ test_string|truncatechars:'20' }}</p>
            <p>{{ test_string|lower }}</p>
            <p>{{ test_string|cut:' '|upper }}</p>
        </div>
        <script src="/static/jquery-1.12.4.js"></script>
    </body>
    </html>

    1)truncatechars:'10',截取字符串的前10个字符。

    2)lower,全部转换为小写

    3)cut:' ',按空格切割

    这些函数实际上都是Django提供的python函数,在render进行渲染的时候,这些函数会被调用。

    七、自定义模板函数(simple_tag)

    自定义simple_tag的步骤如下:

    1.在APP目录下创建templatetags目录

    (目录名不能变,Django只认识这个目录名)

    2.在templatetags目录中创建python模块文件

    (例如xxoo.py)

    3.在xxoo.py中写模板函数

    from django import template
    from django.utils.safestring import mark_safe
    
    register = template.Library()
    
    
    @register.simple_tag
    def myfunc():
        return ('My function')

    4.在需要使用该模板函数的html中导入并调用

    {% load xxoo %}
    <!DOCTYPE html>
    <html lang="en" xmlns="http://www.w3.org/1999/html">
    <head>
        <meta charset="UTF-8">
        <title>Include</title>
        <style>
            .pg-header{
                height: 48px;
                background-color: seagreen;
                color: lightgrey;
            }
        </style>
    </head>
    <body>
        <div class="pg-header"><h3 style="display: inline">登录页面</h3></div>
        <div>
            <p>{{ test_string }}</p>
            <p>{% myfunc %}</p>
        </div>
        <script src="/static/jquery-1.12.4.js"></script>
    </body>
    </html>

    导入模板函数,使用{% load ooxx %}。

    调用模板函数,使用{% myfunc %},Django会将myfunc函数的返回值放置在该位置。

    5.如果自定义模板函数带参数

    from django import template
    from django.utils.safestring import mark_safe
    
    register = template.Library()
    
    
    @register.simple_tag
    def myfunc(name, age):
        return 'My name is : %s , %s years old.' % (name, age)

    在html中调用myfunc时,需要传入对应的参数:

    {% myfunc 'leo' 32 %}

    当然也可以用render传递给模板的数据(test_string)作为参数:

    {% myfunc test_string 32 %}

    八、自定义模板函数(filter)

    Django还可以定义一种模板函数,调用形式和他自己提供的{% name|lower %}相似:

    @register.filter
    def myStringAdd(str1, str2):
        return str1 + str2

    该模板函数的装饰器是 @register.filter,实现的功能是将两个字符串拼接在一起。

    以以下方式在html中调用:

    {{ "My name"|my_string_add:" is leo" }}

    特别注意:这里的调用方式是{{}} 而不是{%%}。 这种filter形式的模板函数,最多只能传两个参数。而且,这种方式书写机制很固定,不能随便加空格,例如 {{ "My name"|my_string_add:空格" is leo" }} 就会报错。

    虽然filter形式有很多局限性,但是也有他独特的应用场景:

    {% if 3|int_add:5 > 7 %}
        <p>不成立</p>
    {% endif %}

    这种filter形式的模板函数,可以直接作为if的条件。而simple_tag不行

    {% if int_add 3 5 > 7 %}
        <p>不成立</p>
    {% endif %}

    九、XSS攻击介绍

    XSS:Cross Site Scripting 跨站点脚本攻击。

    XSS和SQL注入比较相似,就是以恶意脚本作为用户输入,起到控制用户浏览器的目的。

    例如我们在某个论坛的评论中发送一段JS代码,这段代码会被看成是字符串在评论区显示。但如果作为JS运行的话,就会对页面产生很大的影响。

    1.Django默认阻止XSS攻击

    在视图函数中,我们获取到评论<textarea>标签的数据后,我们要让其显示在评论区,会通过render将该数据替换到html模板中。

    def include(request):
        # 我们使用一个变量来模拟从<textarea>中获取的评论数据(用html举例)
        test_string = '<h1>HELLO world!My name is Leo</h1>'
        return render(request, 'include.html', {'test_string': test_string})

    Django默认会将test_string在html中以字符串的形式展示(避免XSS攻击)。

    效果:

    2.让其JS脚本或html生效

    from django.utils.safestring import mark_safe
    
    
    def include(request):
        test_string = '<h1>HELLO world!My name is Leo</h1>'
        # 使用mark_safe将test_string标记为安全的,让浏览器可以执行
        test_string = mark_safe(test_string)
        return render(request, 'include.html', {'test_string': test_string})

    效果:

    十、自定义分页(一)

    当我们从数据库中查询到很多数据时,例如100条。我们要将其在页面中展示,全部一起展示肯定是不合适的,所以我们要将他们分页展示。

    例如博客园首页效果:

    每页只显示20篇文章,下面提供一个页签进行跳转。

    我们来实现一个简单的分页效果:

    1.添加一个urls.py映射

    urlpatterns = [
        # ...这里是其他的映射关系,省略       
        re_path('paging/(?P<pnum>d*)', views.paging),
    ]

    2.添加一个对应的视图函数paging()

    # 模拟数据库的全部数据
    PAGES = []
    for content in range(100):
        PAGES.append("这是来自数据库的内容,第%s条" % content)
    
    # 视图函数,pnum为第几页
    def paging(request, pnum):
        # 从url中获取请求的页数,如果没有带页数,则默认为第1页
        if pnum == '':
            pnum = '1'
        pnum = int(pnum)
    
        # 每一页显示行数
        per_page = 10
    
        # 取对应页数的数据
        start = (pnum - 1) * per_page
        end = pnum * per_page
        contents = PAGES[start:end]
    
        # 当前处于第几页
        current_page = pnum
        # 获取总的条目数,select count(*) from tb;
        max_page = len(PAGES)
        # count为商(总页数),y为余数
        count, y = divmod(max_page, per_page)
        if y:
            count += 1
    
        # 从后台返回<a>标签,用于构建页签按钮
        from django.utils.safestring import mark_safe
        paginations = []
        for i in range(1, count + 1):
            # 当前页的页签按钮呈现不同的样式
            if i == current_page:
                temp = "<a class='page_num active' href='/mgmt/paging/%s'>%s</a>" % (i, i)
            else:
                temp = "<a class='page_num' href='/mgmt/paging/%s'>%s</a>" % (i, i)
            paginations.append(temp)
            
        # 让html返回到页面能够生效
        paginations = mark_safe("".join(paginations))
        return render(request, 'paging.html', {"contents": contents, "paginations": paginations})

    解释:

    1).先使用列表模拟从数据中获取的全部内容,一共100条。

    2).从url映射中获取用户请求的内容页数,http://127.0.0.1:8000/mgmt/paging/4,就获取到数字4。

    3).从PAGES中获取应该显示的10条数据

    4).计算我们要显示多少页的页签按钮,这里我们是计算的一共有多少页,如果内容非常多,一般只显示一部分。类似于:

    这个问题,在后面小节处理。

    5).在后台生成页签按钮对应的<a>标签,并使用mark_safe处理。

    6).返回所有的内容数据,和页签按钮<a>标签。

    3.实现对应html模板

    (按钮效果是从博客园参考的)

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Paging</title>
        <style>
            /*分别定义页签按钮的效果,以及当前页面的页签按钮效果*/
            .pagination{
                font-size: 12px;
                /*一般页签按钮都居中*/
                /*text-align: center;*/
            }
            .pagination .page_num{
                display: inline-block;
                padding: 2px 5px;
                border: 1px solid #9aafe5;
                color: #2e6ab1;
                margin: 0 2px;
                text-decoration: none;
            }
            .pagination .page_num.active{
                background-color: #2e6ab1;
                border: 1px solid #000080;
                color: #fff;
                font-weight: bold;
                margin: 0 2px;
                padding: 2px 5px;
            }
            a{
                outline: 0;
            }
        </style>
    </head>
    <body>
        <!-- 显示内容 -->
        <ul>
            {% for text in contents %}
            <li>
                {{ text }}
            </li>
            {% endfor %}
        </ul>
        <!-- 显示页签按钮 -->
        <div class="pagination">
            {{ paginations }}
        </div>
    </body>
    </html>

    4.最终实现效果

    十一、自定义分页(二)

    1.处理页签按钮的个数

    当我们的内容条数非常多时,例如1000条。我们会发现页签按钮特别多(因为是由总数计算而来)。如图:

    这种情况下,我们应该只显示当前页的前后几个页签按钮即可:

    # 模拟数据库的全部数据
    PAGES = []
    for content in range(1000):
        PAGES.append("这是来自数据库的内容,第%s条" % content)
    
    
    # 视图函数,pnum为第几页
    def paging(request, pnum):
        # 从url中获取请求的页数,如果没有带页数,则默认为第1页
        if pnum == '':
            pnum = '1'
        pnum = int(pnum)
    
        # 每一页显示行数
        per_page = 10
    
        # 当前处于第几页
        current_page = pnum
        # 获取总的条目数,select count(*) from tb;
        max_page = len(PAGES)
        # count为商(总页数),y为余数
        count, y = divmod(max_page, per_page)
        if y:
            count += 1
    
        # 检查URL传过来的页数是否处于正常范围,如果大于总页数,则置于最大值count
        if current_page > count:
            current_page = count
        # 取对应页数的数据
        start = (current_page - 1) * per_page
        end = current_page * per_page
        contents = PAGES[start:end]
    
        # 从后台返回<a>标签,用于构建页签按钮
        from django.utils.safestring import mark_safe
    
        bf_and_af = 5
        paginations = []
    
        if current_page - bf_and_af <= 0:
            p_start = 1
            p_end = 12
        elif current_page + bf_and_af > count:
            p_start = count - 10
            p_end = count + 1
        else:
            p_start = current_page - bf_and_af
            p_end = current_page + bf_and_af + 1
    
        if self.page_count <= self.pager_num * 2 + 1:
            p_start = 1
            p_end = self.page_count + 1
    
        # 前面增加"Prev"
        prev_page = current_page - 1 if current_page > 1 else 1
        temp = "<a class='page_num' href='/mgmt/paging/%s'>%s</a>" % (prev_page, "&lt;&lt;Prev")
        paginations.append(temp)
    
        for i in range(p_start, p_end):
            # 当前页的页签按钮呈现不同的样式
            if i == current_page:
                temp = "<a class='page_num active' href='/mgmt/paging/%s'>%s</a>" % (i, i)
            else:
                temp = "<a class='page_num' href='/mgmt/paging/%s'>%s</a>" % (i, i)
            paginations.append(temp)
    
        # 后面添加"Next"
        next_page = current_page + 1 if current_page < count else count
        temp = "<a class='page_num' href='/mgmt/paging/%s'>%s</a>" % (next_page, 'Next&gt;&gt;')
        paginations.append(temp)
    
        # 添加一个跳转到多少页
        temp = """
            <input style='30px;' type='text'/>
            <input id='jump_button' type='button' value="跳转"/>
            <script>
                var bt = document.getElementById('jump_button');
                bt.onclick=function(){
                    var val = this.previousElementSibling.value;
                    var r = /^+?[1-9][0-9]*$/;
                    var flag=r.test(val);
                    if(flag){
                        location.href = '/mgmt/paging/'+val;
                    }
    
                };
            </script>
        """
        paginations.append(temp)
        
        # 让html返回到页面能够生效
        paginations = mark_safe("".join(paginations))
    
        return render(request, 'paging.html', {"contents": contents, "paginations": paginations})

    解释:

    1)模拟数据库中1000条内容,计算得到需要100个页签按钮

    2)从url中获得用户请求的页数,判断是否超出总页数

    3)获取对应页数的内容

    3)计算应该显示哪几个页签按钮,前后显示多少个,处理位于两端时的异常问题

    4)在页签按钮前后添加"Prev"和"Next"按钮,点击可以上下页跳转

    5)在最后添加跳转按钮和输入框,使用这则表达式判断输入是否为整数

    2.实现效果

    十二、自定义分页(三)

    1.封装分页代码(代码重用)

    将分页代码封装成类:

    class Paging(object):
        def __init__(self, page_num, data_count, per_page_count=10, pager_num=5):
            self.page_num = page_num
            self.data_count = data_count
            self.per_page_count = per_page_count
            self.pager_num = pager_num
            self.current_page = self.proc_page_num()
    
        @property
        def page_count(self):
            # count为商(总页数),y为余数
            count, y = divmod(self.data_count, self.per_page_count)
            if y:
                count += 1
            return count
    
        def proc_page_num(self):
            # 从url中获取请求的页数,如果没有带页数,则默认为第1页
            if self.page_num == '':
                self.page_num = '1'
    
            self.page_num = int(self.page_num)
            if self.page_num > self.page_count:
                return self.page_count
            else:
                return self.page_num
    
        @property
        def start_idx(self):
            # 取对应页数的数据
            return (self.current_page - 1) * self.per_page_count
    
        @property
        def end_idx(self):
            return self.current_page * self.per_page_count
    
        def pager_str(self, base_url):
            # 从后台返回<a>标签,用于构建页签按钮
            from django.utils.safestring import mark_safe
    
            paginations = []
    
            if self.current_page - self.pager_num <= 0:
                p_start = 1
                p_end = 12
            elif self.current_page + self.pager_num > self.page_count:
                p_start = self.page_count - 10
                p_end = self.page_count + 1
            else:
                p_start = self.current_page - self.pager_num
                p_end = self.current_page + self.pager_num + 1
    
            if self.page_count <= self.pager_num * 2 + 1:
                p_start = 1
                p_end = self.page_count + 1
    
            # 前面增加"Prev"
            prev_page = self.current_page - 1 if self.current_page > 1 else 1
            temp = "<a class='page_num' href='%s%s'>%s</a>" % (base_url, prev_page, "&lt;&lt;Prev")
            paginations.append(temp)
    
            for i in range(p_start, p_end):
                # 当前页的页签按钮呈现不同的样式
                if i == self.current_page:
                    temp = "<a class='page_num active' href='%s%s'>%s</a>" % (base_url, i, i)
                else:
                    temp = "<a class='page_num' href='%s%s'>%s</a>" % (base_url, i, i)
                paginations.append(temp)
    
            # 后面添加"Next"
            next_page = self.current_page + 1 if self.current_page < self.page_count else self.page_count
            temp = "<a class='page_num' href='%s%s'>%s</a>" % (base_url, next_page, 'Next&gt;&gt;')
            paginations.append(temp)
    
            # 添加一个跳转到多少页
            temp = """
                    <input style='30px;' type='text'/>
                    <input id='jump_button' type='button' value="跳转"/>
                    <script>
                        var bt = document.getElementById('jump_button');
                        bt.onclick=function(){
                            var val = this.previousElementSibling.value;
                            var r = /^+?[1-9][0-9]*$/;
                            var flag=r.test(val);
                            if(flag){
                                location.href = '%s'+val;
                            }
                        };
                    </script>
                """ % base_url
            paginations.append(temp)
            pager_str = mark_safe("".join(paginations))
    
            return pager_str

    2.使用封装好的分页类

    # 模拟数据库的全部数据
    PAGES = []
    for content in range(1000):
        PAGES.append("这是来自数据库的内容,第%s条" % content)
    
    
    def paging(request, pnum):
        # 获取一个分页对象
        pager_obj = Paging(pnum, len(PAGES))
        # 获取页签按钮的list
        paginations = pager_obj.pager_str('/mgmt/paging/')
        # 根据start和end来获取数据,对应数据库的行数
        contents = PAGES[pager_obj.start_idx: pager_obj.end_idx]
    
        return render(request, 'paging.html', {"contents": contents, "paginations": paginations})
  • 相关阅读:
    数据结构之 内部排序---交叉排序(没啥特别的!!!)
    数据结构之 图论---基于邻接矩阵的广度优先搜索遍历(输出bfs遍历序列)
    数据结构之 图论---图的深度遍历( 输出dfs的先后遍历序列 )
    数据结构之 排序---折半插入排序(时间复杂度 O(nlog2 n) )
    HDU 1022 之 Train Problem I
    Bestcoder round 18---A题(素数筛+素数打表+找三个素数其和==n)
    Bestcoder round 18----B题(一元三次方程确定区间的最大值(包含极值比较))
    操作字典
    在线压缩图片
    JSON转C#实体类
  • 原文地址:https://www.cnblogs.com/leokale-zz/p/12070009.html
Copyright © 2011-2022 走看看