中文字符和英文字符所占的字節長度是不一樣,一個是2個字節,一個是1個字節,這給我們用英文的web框架開發中文app帶來了麻煩。比如Django自帶過濾器truncatewords并不支持截取中文,另外模型中CharField中的max_length選項用于限制中英混合字符串的長度基本上是失真的,需要額外自定義表單驗證限制某些字段比如標題的長度。小編我今天就給大家分享幾個例子和解決方案。
我們同樣以博客的Article模型為例,我們試圖限制標題的最大長度為90 個字符。
class Article(models.Model):
"""Article Model"""
title = models.CharField('Title', max_length=90,db_index=True)
90個英文字符也就是10來個單詞,作為標題長度剛好合適。如果中文標題允許長達90個中文字符,這將是個恐怖的存在,也就意味著這里標題的長度限制對于中文或中英混雜字符串等于形同虛設。注意: Django CharField的max_length是按字符數來限制的,而不是字節數。同樣Django的length模板過濾器和python的len函數默認也是統計字符數,而不是字節數。
更好的解決方法?我們統計中英混合字符串的字節數,然后通過表單實現按字節數來限制,代碼如下所示。我們先通過python的encode方法將混合字符串轉化為二進制數據,再使用python的len方法統計字節長度。用該方法“我是a"會被統計成5,而不是3。該方法并不完美,但我們可以實現限制標題長度為90個英文字符或45個中文字符。
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
exclude = ['author', ]
def clean_title(self):
title = self.cleaned_data['title']
if len(title.encode('gb18030')) > 90: # 轉成二進制統計字節 b'\xce\xd2\xca\xc7ab\xd6\xed')
raise forms.ValidationError('The length of title must be shorter than 90 chars.')
return title
另一個例子是我們經常需要根據正文截取文中開頭部分作為摘要,使用Django自帶的模板過濾器truncatechars和truncatewords僅適用于羅馬及英文字符的,但對中文字符串完全不適用,如下所示:
{{ article.body|striptags|truncatewords:20 }}
這是我們需要自定義模板過濾器截取中英混合字符串,網上已有現成可用代碼,我在這里轉貼給大家備用。如果你不知道如何自定義Django模板過濾器及如何使用它們,請強烈閱讀本文Django基礎(16): 模板標簽(tags)的分類及如何自定義模板標簽
from django import template
from django.template.defaultfilters import stringfilter
register = template.Library()
@register.filter
@stringfilter
def cut_str(str, length=10):
"""
截取字符串,使得字符串長度等于length,并在字符串后加上省略號 """
is_encode = False
try:
str_encode = str.encode('gb18030') #b'\xce\xd2\xca\xc7ab\xd6\xed'
is_encode = True
except:
pass
if is_encode:
l = length*2
if l < len(str_encode):
l = l - 3
str_encode = str_encode[:l]
try:
str = str_encode.decode('gb18030') + '...'
except:
str_encode = str_encode[:-1]
try:
str = str_encode.decode('gb18030') + '...'
except:
is_encode = False
if not is_encode:
if length < len(str):
length = length - 2
return str[:length] + '...'
return str
使用時先載入自定義標簽,再按如下使用即可:
{% load your_tags %}
{{ article.body|striptags|cut_str:120 }}
相關閱讀Django基礎(15): 模板過濾器(filter)的工作原理及如何自定義模板過濾器
Django實戰: 利用自定義模板標簽實現仿CSDN博客月度歸檔
Django實戰專題: 開發專業博客(1)之內容管理后臺開發
大江狗 - 微信公眾號【Python Web與Django開發】
2020.2.15