Django 视图 & HTML模版

一、视图

1.1 FBV和CBV

1.1.1 FBV

FBV就是常用的function base views,基于函数的视图

# urls.py
urlpatterns = [
	path('login/',views.login)
]

#views.py
def login(request):
	return HttpResponse('登录')

诸如此类的函数和路由一一对应,匹配到路由,就去执行相应的函数。

1.1.2 CBV

CBV是指 class base views,基于类的视图

# urls.py
urlpatterns = [
	path('user/',views.UserView.as_view())
]

# views.py
form django.views import View

# 需要继承View这个类
class UserView(View):
	def get(self,request):
		return HttpResponse('Get')
	
	def post(self,request):
		return HttpResponse('Post')

稍微复杂点,在定义路由时,后面加入的自己定义的类.as_view()
然后在views定义的类中,定义get,post,put,delete等会用的方法,然后就会根据请求执行相应的函数

path('login/',views.login)
# 对象内部存储对应关系 ‘login/’ =====》 login函数

path('login/',views.UserView.as_view())
# 对象内部存储对应关系‘user/’ =====> as_view()的返回值

UserView里没有定义as_view(),那就去父类View中找,

class View:
    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
	...
	   @classonlymethod
    def as_view(cls, **initkwargs):
		...
        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            self.setup(request, *args, **kwargs)
            if not hasattr(self, 'request'):
                raise AttributeError(
                    "%s instance has no 'request' attribute. Did you override "
                    "setup() and forget to call super()?" % cls.__name__
                )
            return self.dispatch(request, *args, **kwargs)

        return view

as_view()函数最后返回了view这个函数

def setup(self, request, *args, **kwargs):
   """Initialize attributes shared by all view methods."""
   if hasattr(self, 'get') and not hasattr(self, 'head'):
       self.head = self.get
   self.request = request
   self.args = args
   self.kwargs = kwargs


def view(request, *args, **kwargs):
    self = cls(**initkwargs)
    # 1. self = UserView
    self.setup(request, *args, **kwargs)
    # 对request等内容进行封装
    if not hasattr(self, 'request'):
        raise AttributeError(
            "%s instance has no 'request' attribute. Did you override "
            "setup() and forget to call super()?" % cls.__name__
        )
    return self.dispatch(request, *args, **kwargs)


def dispatch(self, request, *args, **kwargs):
    if request.method.lower() in self.http_method_names:
    # 2. 判断request的方法是否在 定义的 http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] 里面
        # get请求为例
        # handler =  self.get 
        """
			class UserView(View):
				def get(self,request):
					return HttpResponse('Get')

		self.get 又是我们定义好的,所以handler就等于 get 函数
		""" 
		
        handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
    else:
        handler = self.http_method_not_allowed
    # 3. 最后 handler()相当于执行函数 get(request)
    return handler(request, *args, **kwargs)

	# 4. handler返回的get(),那么相当于前面dispatch返回的就是get(),也就相当于前面View.as_view()返回就是get()

1.1.3 FBV和CBV常见使用

FBV常用于前后端不分离
CBV常用于前后端分离的,如小程序API,后续常用django rest framekwork框架就是CBV模式

1.2 参数

  1. request参数

通过type(request)可以查看request的类型,再导入查看request具体内内容

type(reqeust) # <class 'django.core.handlers.wsgi.WSGIRequest'>
from django.core.handlers.wsgi import WSGIRequest
request.method

"""
    @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)
"""
# GET源码中会将原始参数转化成QueryDict类型
request.GET        ?name=123&age=123&age=199   =>  特殊的字典:{name:[123],"age":[123,1999]}
reqeust.POST
request.FILES
request.path_info
request.body
request.resover_math
request.session
#urls.py
from django import path,re_path

urlpatterns = [
	path('info/<int:v1>',views.info)
	path('mine/<int:v1>',views.MineView.as_view())
	# 参数为正则表达式
	re_path('repath/(\d+)', views.MineView.as_view())
]

#views.py
def info(request,v1):
	return HttpResponse('info')

class MineView(View):
	def get(self,request,v1):
		print(reques,v1)
		print(self.request,self.args,self.kwargs)
		return HttpResponse('mine')
  1. args和kwargs 参数
  • 当访问这个path('mine/<int:v1>',views.MineView.as_view())
    可以看到链接访问’mine/12’的时候,12这个参数是放在kwargs里传递
    在这里插入图片描述

  • 当访问re_path('repath/(\d+)', views.MineView.as_view())
    可以看到问带正则的re_path时,参数放在了args里面传递
    在这里插入图片描述

1.3业务处理(视图函数)

有了请求的数据,将数据拿到进行处理,如登录,注册,上传文件,数据库操作等等,处理完成得到结果返还给浏览器。

1.4 返回值

1.4.1 HttpResponse

from django.http import HttpResponse
# `构建` 响应体和响应头,
return HttpResponse("成都")

1.4.2 JsonResponse

from django.http import JsonResponse

result = {'staus': True, 'city': '成都'}
return JsonResponse(result)

在这里插入图片描述
而JsonResponse返回的数据是Unicode格式
在它内部,本质上是基于HttpResponse的一个类,进行json.dumps操作,然后结果返给HttpResponse返回

class JsonResponse(HttpResponse):
	...
	data = json.dumps(data, cls=encoder, **json_dumps_params)
	super().__init__(content=data, **kwargs)

需要加入参数"ensure_ascii": False

from django.http import JsonResponse

result = {'staus': True, 'city': '成都'}
return JsonResponse(result, json_dumps_params={'ensure_ascii':False})

在这里插入图片描述

1.4.3 render

from django.shortcuts import render

return render(request,'login.html')

查看render源码

def render(
    request, template_name, context=None, content_type=None, status=None, using=None
)
    content = loader.render_to_string(template_name, context, request, using=using)
    return HttpResponse(content, content_type, status)

content是已经将模板中特殊字符替换好的字符串。最后本质也是返回一个HttpResponse对象。

1.4.4 redirect

def redirect(to, *args, permanent=False, **kwargs):
    # Return an HttpResponseRedirect to the appropriate URL for the arguments
    ...


class HttpResponseRedirect(HttpResponseRedirectBase):
    status_code = 302 #临时重定向

class HttpResponsePermanentRedirect(HttpResponseRedirectBase):
    status_code = 301 #永久重定向

class HttpResponseRedirectBase(HttpResponse):
	....
	self["Location"] = iri_to_uri(redirect_to)
	...

返回HttpResponseRedirect ,从类的继承关系看出,最后还是一个HttpResponse对象
返回数据,响应头多了一个Location,响应体为空,状态码为301或者302,浏览器接收到301或302就重新向Location这个地址发起新的请求
在这里插入图片描述
ps:301和302对开发来说影响不大,只是影响搜索引擎收入网址。

二、HTML模板

2.1 模板的使用

def demo(request):
    return render(request,"demo.html")

2.1.1 读取模板的位置

  1. 首先读取‘demo.html’这个文件。读取位置就需要看settings里的设置
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.staticfiles',
    "apps.app01.apps.App01Config",
    "apps.www.apps.WwwConfig",
]

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
		...
    },
]

先从DIR定义的目录中去寻找templates文件,如果找不到再去已注册的APP的下去找。
注意:必须是已注册的APP
可能存在的问题,当多个app下都有同样的文件,那么python从上往下读取,就会出现bug,所以参考django源码进行相同编写
在这里插入图片描述
在templates下不直接写html文件,而是建立一个跟自己app同名的文件夹,将html文件放到文件夹里面,
在使用的时候,就可以return render(request,"admin/demo.html") 避免出现问题。

如果有需要复用的公共模板,一般在项目根目录下创建templates,这样优先级就会更高,即使其他App中存在相同的也不会使用。

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates'],
        'APP_DIRS': True,
		...
    },
]

2.1.2 读取文件内容

创建文件,创建为任意后缀都行,最后都是按字符串读取,只是.html后缀,在开发时比较方便,有提示而已。

  1. 如果模板中没有需要替换的文件,直接读取
  2. 如果有参数,那么就需要先进行模板渲染(替换),再读取返回的字符串

2.1.3 封装到HttpResponse

封装返回数据,返回给浏览器。

2.2 底层处理机制*

从其他地方看到的,底层处理机制。现目前也不是很了解
在这里插入图片描述

namespace = {'city': '成都', 'data': ['2021', '2022', '2023']}

info="""
def _execute():
    _buffer = []
    _buffer.append("<h1>")
    _buffer.append(city)
    _buffer.append(data[2])
    _buffer.append("  大运会")
    _buffer.append("</h1>")
    return "".join(_buffer)
"""
func = compile(info, '<string>', "exec")
exec(func, namespace)
result = namespace['_execute']()
print(result)

在这里插入图片描述

2.3 模板的继承

当有一些基础页面需要重复使用很多次,就会使用继承。将基础框架搭建好,再往里面塞不同的东西
继承只能继承一个

{% extends 'xxx.html'%}

在这里插入图片描述

2.4 导入

导入可以导入多次

{% include 'xxx.html'%}

在这里插入图片描述

2.5 常见易错的模版导入

1.返回的值是数值

    return render(request, "demo.html", {'info': 999}) 
	...
	<script>
	alert({{info}}</script>
	...

此时浏览器会弹出警告窗,且文案为999

2. 返回的值是字符串

    return render(request, "demo.html", {'info': '成都'}) 
	...
	<script>
	alert({{info}}</script>
	...

此时,在进行模板渲染的时候,会替换成alert(成都),再返回给浏览器。就会报错。
需要在django项目的html模板中,修改加引号,就不会报错了。

	...
	<script>
	alert('{{info}}'</script>
	...

3. script引入

...
<script src='xxxx.js'> </script>
...

如果在xxx.js中引入了{{info}},那么在进行模板渲染的时候,就不会进行替换。

2.6 自定义模板函数

  • app必须注册
  • 创建templatetags的文件夹
  • 任意创建文件,必须导入Library和实例化对象
from django.template.library import Library

register = Library()

2.6.1 simple_tag

# xxx.py

from django.template.library import Library

register = Library()

@register.simple_tag()
def my_func(v1, v2):
    return "成都" + v1 + v2 

需要在使用的模板,引用{% load xxx %}

# demo.html 
{% load xxx %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<div>{% my_func "2023" "大运会" %}</div>
</body>
</html>
...

自定义的函数会将直接执行,空格后面填写参数。
在这里插入图片描述

2.6.2 inclusion_tag(‘xxx.html’)

# xxx.py

@register.inclusion_tag("select.html")
def my_select(num):
    return {"x": [item for item in num if item > 22]}

inclusion_tag 会将得到的返回值,返回给xxx.html,然后再返回给引用的对象。适用于显示页面小片段

例如

# select.html

<ul>
    {% for item in x %}
        <li>{{ item }}</li>
    {% endfor %}
</ul>
# views.py
def demo(request):
    return render(
        request,
        "demo.html",
        {
            'num': [11, 22, 33, 44],
            'ctime': datetime.datetime.now()
        }
    )

# demo.html
...
<div>{% my_select num %}</div>
...

首先views.py 传参num给demo.html 然后 my_select 检测到时inclusion_tag,调用自定义函数my_select(num),得到结果返回给select.html,在select.html 中,又执行一次循环展示
最终得到实际结果
在这里插入图片描述

2.6.3 filter

filter最多支持2个参数。

#xxx.py 

@register.filter
def my_f1(a1, a2):
    return "Hello" + a1 + a2
# demo.html
...
<div>{{ "成都"|my_f1:"下雨" }}</div>
...

在这里插入图片描述

2.6.4 区别

filtersimple_taginclusion_tag 使用不同,
使用{{xxx|filter}} 双括号使用,必须带至少一个参数,且只能传2个参数

simple_taginclusion_tag 则是使用{% my_select num %},但括号+%使用,且参数不限制。

filter虽然使用没有其他两个方便,但是当模板中存在if判断条件时,只有filter可以当做判断条件,其他两个则不行

{% if "成都"|my_tt:"下雨" %}
    <h1></h1>
{% else %}
    <h1></h1>
{% endif %}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值