日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > python >内容正文

python

python实现矢量分级渲染_用 Python 撸一个 Web 服务器-第4章:动态渲染数据

發(fā)布時(shí)間:2025/3/20 python 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python实现矢量分级渲染_用 Python 撸一个 Web 服务器-第4章:动态渲染数据 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

上一章中為了盡快讓 Todo List 程序跑起來(lái),并沒(méi)有完全按照 MVC 模式編寫(xiě)程序。這一章就讓我們一起實(shí)現(xiàn)一個(gè)完整的 MVC 模式 Todo List 程序首頁(yè)。

使用模型操作數(shù)據(jù)

我們來(lái)分析下請(qǐng)求 Todo List 程序首頁(yè)時(shí),模型層需要做哪些事情。當(dāng)一個(gè)請(qǐng)求到達(dá)首頁(yè)視圖函數(shù) index 時(shí),它需要做兩件事情,首先調(diào)用模型層獲取全部的 todo 數(shù)據(jù),然后將 todo 數(shù)據(jù)動(dòng)態(tài)填充到 index.html 模板中。

調(diào)用模型層獲取全部的 todo 數(shù)據(jù),只需要在模型層編寫(xiě)讀取 todo/db/todo.json 文件數(shù)據(jù)的代碼即可。在這之前,我們需要先確定 todo 在文件中存儲(chǔ)的格式。

Todo List 程序中 todo 需要存儲(chǔ)的數(shù)據(jù)只有一個(gè),就是 todo 的內(nèi)容。所以我們可以將 todo 以如下格式存儲(chǔ)到 todo/db/todo.json 文件:// todo_list/todo/db/todo.json

[

{

"id": 1,

"content": "hello world"

},

{

"id": 2,

"content": "你好,世界!"

}

]

這是一個(gè)標(biāo)準(zhǔn)的 JSON 格式,每一個(gè)對(duì)象代表了一條 todo,content 字段即為 todo 內(nèi)容,id 作為每條數(shù)據(jù)的索引不會(huì)展示在頁(yè)面中,方便我們對(duì)數(shù)據(jù)進(jìn)行排序、快速查找等操作。

為了簡(jiǎn)化程序,我將數(shù)據(jù)存儲(chǔ)在 JSON 文件中而不是數(shù)據(jù)庫(kù)中。存儲(chǔ)到文件的格式多種多樣,但 JSON 格式是一種非常流行且友好的數(shù)據(jù)格式,在 Python 中也能夠很方便的對(duì) JSON 格式的文件進(jìn)行讀寫(xiě)操作。

注意:JSON 文件不支持注釋,所以如果你打算直接從上面示例中復(fù)制數(shù)據(jù)到 todo.json 文件時(shí),需要去掉頂部文件名注釋。

如果 todo/db/todo.json 文件內(nèi)容為空,使用 Python 讀取時(shí)會(huì)拋出 JSONDecodeError 異常,起碼要保證其內(nèi)部有一個(gè)空數(shù)組 [] 存在,才能正常讀取。

確定了 todo/db/todo.json 文件數(shù)據(jù)格式,就可以編寫(xiě)在模型層讀取 todo 數(shù)據(jù)的代碼了:# todo_list/todo/models.py

import os

import json

from todo.config import BASE_DIR

class Todo(object):

"""

Todo 模型類(lèi)

"""

def __init__(self, **kwargs):

self.id = kwargs.get('id')

self.content = kwargs.get('content', '')

@classmethod

def _db_path(cls):

"""獲取存儲(chǔ) todo 數(shù)據(jù)文件的絕對(duì)路徑"""

# 返回 'todo_list/todo/db/todo.json' 文件的絕對(duì)路徑

path = os.path.join(BASE_DIR, 'db/todo.json')

return path

@classmethod

def _load_db(cls):

"""加載 JSON 文件中所有 todo 數(shù)據(jù)"""

path = cls._db_path()

with open(path, 'r', encoding='utf-8') as f:

return json.load(f)

@classmethod

def all(cls, sort=False, reverse=False):

"""獲取全部 todo"""

# 這一步用來(lái)將所有從 JSON 文件中讀取的 todo 數(shù)據(jù)轉(zhuǎn)換為 Todo 實(shí)例化對(duì)象,方便后續(xù)操作

todo_list = [cls(**todo_dict) for todo_dict in cls._load_db()]

# 對(duì)數(shù)據(jù)按照 id 進(jìn)行排序

if sort:

todo_list = sorted(todo_list, key=lambda x: x.id, reverse=reverse)

return todo_list

定義 Todo 模型類(lèi)來(lái)操作 todo 數(shù)據(jù)。Todo 模型類(lèi)的 all 方法用來(lái)讀取全部的 todo 數(shù)據(jù),在其內(nèi)部將所有從 JSON 文件中讀取的 todo 數(shù)據(jù)轉(zhuǎn)換為 Todo 實(shí)例化對(duì)象并組裝成 list 返回。all 方法還可以對(duì)數(shù)據(jù)進(jìn)行排序,排序操作實(shí)際上轉(zhuǎn)發(fā)給了 Python 內(nèi)置的 sorted 函數(shù)來(lái)完成。

有了全部的 todo 數(shù)據(jù),下一步操作就是將 todo 數(shù)據(jù)動(dòng)態(tài)填充到 todo/templates/index.html 模板中。

使用模板引擎渲染 HTML

上一章實(shí)現(xiàn)的 Todo List 程序返回的首頁(yè)數(shù)據(jù)都是固定寫(xiě)死在 todo/templates/index.html 代碼中的。現(xiàn)在需要?jiǎng)討B(tài)填充 todo 內(nèi)容,我們需要學(xué)習(xí)一個(gè)新的概念叫作 模板渲染。

首先我們編寫(xiě)的 HTML 頁(yè)面不再是完全使用 HTML 的標(biāo)簽來(lái)編寫(xiě),而需要使用一些占位變量來(lái)替換需要?jiǎng)討B(tài)填充的部分,這樣編寫(xiě)出來(lái)的 HTML 頁(yè)面通常稱(chēng)為模板。將 HTML 模板讀取到內(nèi)存中,使用真實(shí)的 todo 數(shù)據(jù)來(lái)替換掉占位變量而獲得最終將要返回的字符串?dāng)?shù)據(jù),這個(gè)過(guò)程稱(chēng)為渲染。能夠?qū)崿F(xiàn)讀取 HTML 中的占位變量并正確替換為真實(shí)值的代碼稱(chēng)為模板引擎。

Todo List 程序首頁(yè)主體部分代碼如下:

Todo List

  • Hello World
  • 你好,世界!

其中每一個(gè) li 標(biāo)簽代表一條 todo,顯然 todo 的條數(shù)是不確定的,所以每一個(gè) li 標(biāo)簽都需要?jiǎng)討B(tài)生成。根據(jù)這段 HTML 代碼,可以編寫(xiě)出如下模板:

Todo List

{% for todo in todo_list %}

{{ todo.content }}

{% endfor %}

這段模板代碼中只保留了一對(duì) li 標(biāo)簽,它被嵌套在 for 循環(huán)中,for 語(yǔ)句塊從 {% for todo in todo_list %} 開(kāi)始,到 {% endfor %} 結(jié)束。todo_list 變量是在模板渲染階段傳進(jìn)來(lái)的由所有 todo 對(duì)象組成的 list,list 中有多少個(gè)元素就會(huì)渲染多少個(gè) li 標(biāo)簽。for 循環(huán)內(nèi)部使用了循環(huán)變量 todo,{{ todo.content }} 表示獲取 todo 變量的 content 屬性,這與 Python 中獲取對(duì)象的屬性語(yǔ)法相同。

了解了模板語(yǔ)法,我們還需要有一個(gè)能夠讀懂模板語(yǔ)法的模板引擎。Todo List 程序的 HTML 模板只會(huì)用到 for 循環(huán)和模板變量這兩種語(yǔ)法,所以我們將要實(shí)現(xiàn)的模板引擎只需要能夠解析這兩種語(yǔ)法即可。# todo_list/todo/utils.py

class Template(object):

"""模板引擎"""

def __init__(self, text, context):

# 保存最終結(jié)果

self.result = []

# 保存從 HTML 中解析出來(lái)的 for 語(yǔ)句代碼片段

self.for_snippet = []

# 上下文變量

self.context = context

# 使用正則匹配出所有的 for 語(yǔ)句、模板變量

self.snippets = re.split('({{.*?}}|{%.*?%})', text, flags=re.DOTALL)

# 標(biāo)記是否為 for 語(yǔ)句代碼段

is_for_snippet = False

# 遍歷所有匹配出來(lái)的代碼片段

for snippet in self.snippets:

# 解析模板變量

if snippet.startswith('{{'):

if is_for_snippet is False:

# 去掉花括號(hào)和空格,獲取變量名

var = snippet[2:-2].strip()

# 獲取變量的值

snippet = self._get_var_value(var)

# 解析 for 語(yǔ)句

elif snippet.startswith('{%'):

# for 語(yǔ)句開(kāi)始代碼片段 -> {% for todo in todo_list %}

if 'in' in snippet:

is_for_snippet = True

self.result.append('{}')

# for 語(yǔ)句結(jié)束代碼片段 -> {% endfor %}

else:

is_for_snippet = False

snippet = ''

if is_for_snippet:

# 如果是 for 語(yǔ)句代碼段,需要進(jìn)行二次處理,暫時(shí)保存到 for 語(yǔ)句片段列表中

self.for_snippet.append(snippet)

else:

# 如果是模板變量,直接將變量值追加到結(jié)果列表中

self.result.append(snippet)

def _get_var_value(self, var):

"""根據(jù)變量名獲取變量的值"""

# 如果 '.' 不在變量名中,直接在上下文變量中獲取變量的值

if '.' not in var:

value = self.context.get(var)

# '.' 在變量名中(對(duì)象.屬性),說(shuō)明是要獲取對(duì)象的屬性

else:

obj, attr = var.split('.')

value = getattr(self.context.get(obj), attr)

# 保證返回的變量值為字符串

if not isinstance(value, str):

value = str(value)

return value

def _parse_for_snippet(self):

"""解析 for 語(yǔ)句片段代碼"""

# 保存 for 語(yǔ)句片段解析結(jié)果

result = []

if self.for_snippet:

# 解析 for 語(yǔ)句開(kāi)始代碼片段

# '{% for todo in todo_list %}' -> ['for', 'todo', 'in', 'todo_list']

words = self.for_snippet[0][2:-2].strip().split()

# 從上下文變量中獲取 for 語(yǔ)句中的可迭代對(duì)象

iter_obj = self.context.get(words[-1])

# 遍歷可迭代對(duì)象

for i in iter_obj:

# 遍歷 for 語(yǔ)句片段的代碼塊

for snippet in self.for_snippet[1:]:

# 解析模板變量

if snippet.startswith('{{'):

# 去掉花括號(hào)和空格,獲取變量名

var = snippet[2:-2].strip()

# 如果 '.' 不在變量名中,直接將循環(huán)變量 i 賦值給 snippet

if '.' not in var:

snippet = i

# '.' 在變量名中(對(duì)象.屬性),說(shuō)明是要獲取對(duì)象的屬性

else:

obj, attr = var.split('.')

# 將對(duì)象的屬性值賦值給 snippet

snippet = getattr(i, attr)

# 保證變量值為字符串

if not isinstance(snippet, str):

snippet = str(snippet)

# 將解析出來(lái)的循環(huán)變量結(jié)果追加到 for 語(yǔ)句片段解析結(jié)果列表中

result.append(snippet)

return result

def render(self):

"""渲染"""

# 獲取 for 語(yǔ)句片段解析結(jié)果

for_result = self._parse_for_snippet()

# 將渲染結(jié)果組裝成字符串并返回

return ''.join(self.result).format(''.join(for_result))

def render_template(template, **context):

"""渲染模板"""

# 讀取 'todo_list/todo/templates' 目錄下的 HTML 文件內(nèi)容

template_dir = os.path.join(BASE_DIR, 'templates')

path = os.path.join(template_dir, template)

with open(path, 'r', encoding='utf-8') as f:

# 將從 HTML 中讀取的內(nèi)容傳遞給模板引擎

t = Template(f.read(), context)

# 調(diào)用模板引擎的渲染方法,實(shí)現(xiàn)模板渲染

return t.render()

Template 類(lèi)就是我們?yōu)?Todo List 程序?qū)崿F(xiàn)的模板引擎。模板引擎的代碼有些復(fù)雜,我寫(xiě)了比較詳細(xì)的注釋來(lái)幫助你理解。模板渲染的大概過(guò)程如下:

首先實(shí)例化 Template 對(duì)象,Template 對(duì)象的初始化方法 __init__ 需要傳遞兩個(gè)參數(shù),分別是 HTML 字符串和保存了模板所需變量的 dict,在初始化時(shí)會(huì)解析出 HTML 中所有的 for 語(yǔ)句和模板變量,模板變量直接被替換為對(duì)應(yīng)的值,for 語(yǔ)句代碼段則被暫存起來(lái),等到需要真正渲染模板時(shí),調(diào)用模板引擎實(shí)例對(duì)象的 render 方法,完成 for 語(yǔ)句的解析和值替換,最終將渲染結(jié)果組裝成字符串并返回。

render_template 函數(shù)的代碼也做了相應(yīng)的調(diào)整,它的功能不再只是讀取 HTML 內(nèi)容,而是需要在內(nèi)部調(diào)用模板引擎獲取渲染結(jié)果。

對(duì)于基礎(chǔ)薄弱的讀者來(lái)說(shuō)可能模板引擎部分的代碼不太好理解,那么暫時(shí)先不必深究,你只需要知道模板引擎干了什么,明白它的原理無(wú)非是將 HTML 字符串中的模板語(yǔ)法全部找出來(lái),然后根據(jù)語(yǔ)法規(guī)則將其替換成真正的變量值,最后渲染成正確的 HTML。本質(zhì)上還是字符串的拼接,就像 Python 字符串的 format 方法一樣,它能夠找到字符串中的花括號(hào) {},然后替換成傳遞給它的參數(shù)值。

MVC 模式的 Todo List 程序首頁(yè)

我們已經(jīng)介紹了使用模型操作數(shù)據(jù)和使用模板引擎渲染 HTML,現(xiàn)在就可以用動(dòng)態(tài)渲染的 HTML 首頁(yè)替換之前的靜態(tài)首頁(yè)了。

修改首頁(yè) todo/templates/index.html 的 HTML 代碼為一個(gè)模板:

Todo List

Todo List

{% for todo in todo_list %}

{{ todo.content }}

{% endfor %}

這里我暫時(shí)去掉了 HTML 頂部的 CSS 樣式,因?yàn)槲覀兊哪0逡娌恢С诌@種直接將 CSS 嵌入在 HTML 中的寫(xiě)法,之后我會(huì)介紹如何通過(guò) link 標(biāo)簽來(lái)引入外部樣式。

我們還要對(duì) index 視圖函數(shù)做些修改,在視圖函數(shù)內(nèi)部調(diào)用 Todo 模型的 all 方法來(lái)獲取所有 todo,然后傳遞給模板引擎對(duì) HTML 進(jìn)行渲染,得到最終結(jié)果。修改后的代碼如下:# todo_list/todo/controllers.py

from todo.utils import render_template

from todo.models import Todo

def index():

"""首頁(yè)視圖函數(shù)"""

# 倒序排序,最近添加的 todo 排在前面

todo_list = Todo.all(sort=True, reverse=True)

context = {

'todo_list': todo_list,

}

return render_template('index.html', **context)

在終端中進(jìn)入項(xiàng)目根目錄 todo_list/ 下,使用 Python 運(yùn)行 server.py 文件,將得到經(jīng)過(guò)動(dòng)態(tài)渲染的 Todo List 程序首頁(yè):

現(xiàn)在 Todo List 程序首頁(yè)已經(jīng)是動(dòng)態(tài)渲染的了,下一章我們就來(lái)解決樣式問(wèn)題。

聯(lián)系我:

總結(jié)

以上是生活随笔為你收集整理的python实现矢量分级渲染_用 Python 撸一个 Web 服务器-第4章:动态渲染数据的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。