中文字符和英文字符所占的字节长度是不一样,一个是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