西游之路——python全栈——通用模块(pager、check_code、form验证)

1、验证码

 1 import random
 2 from PIL import Image, ImageDraw, ImageFont, ImageFilter
 3 
 4 _letter_cases = "abcdefghjkmnpqrstuvwxy"  # 小写字母,去除可能干扰的i,l,o,z
 5 _upper_cases = _letter_cases.upper()  # 大写字母
 6 _numbers = ''.join(map(str, range(3, 10)))  # 数字
 7 init_chars = ''.join((_letter_cases, _upper_cases, _numbers))
 8 
 9 
10 def create_validate_code(size=(120, 30),
11                          chars=init_chars,
12                          img_type="GIF",
13                          mode="RGB",
14                          bg_color=(238, 99, 99),
15                          fg_color=(0, 0, 255),
16                          font_size=18,
17                          font_type="Monaco.ttf",
18                          length=4,
19                          draw_lines=True,
20                          n_line=(1, 2),
21                          draw_points=True,
22                          point_chance=2):
23     """
24     @todo: 生成验证码图片
25     @param size: 图片的大小,格式(宽,高),默认为(120, 30)
26     @param chars: 允许的字符集合,格式字符串
27     @param img_type: 图片保存的格式,默认为GIF,可选的为GIF,JPEG,TIFF,PNG
28     @param mode: 图片模式,默认为RGB
29     @param bg_color: 背景颜色,默认为白色
30     @param fg_color: 前景色,验证码字符颜色,默认为蓝色#0000FF
31     @param font_size: 验证码字体大小
32     @param font_type: 验证码字体,默认为 ae_AlArabiya.ttf
33     @param length: 验证码字符个数
34     @param draw_lines: 是否划干扰线
35     @param n_lines: 干扰线的条数范围,格式元组,默认为(1, 2),只有draw_lines为True时有效
36     @param draw_points: 是否画干扰点
37     @param point_chance: 干扰点出现的概率,大小范围[0, 100]
38     @return: [0]: PIL Image实例
39     @return: [1]: 验证码图片中的字符串
40     """
41     width, height = size  # 宽高
42     # 创建图形
43     img = Image.new(mode, size, bg_color)
44     draw = ImageDraw.Draw(img)  # 创建画笔
45 
46     def get_chars():
47         """生成给定长度的字符串,返回列表格式"""
48         return random.sample(chars, length)
49 
50     def create_lines():
51         """绘制干扰线"""
52         line_num = random.randint(*n_line)  # 干扰线条数
53         for i in range(line_num):
54             # 起始点
55             begin = (random.randint(0, size[0]), random.randint(0, size[1]))
56             # 结束点
57             end = (random.randint(0, size[0]), random.randint(0, size[1]))
58             draw.line([begin, end], fill=(0, 0, 0))
59 
60     def create_points():
61         """绘制干扰点"""
62         chance = min(100, max(0, int(point_chance)))  # 大小限制在[0, 100]
63         for w in range(width):
64             for h in range(height):
65                 tmp = random.randint(0, 100)
66                 if tmp > 100 - chance:
67                     draw.point((w, h), fill=(0, 0, 0))
68 
69     def create_strs():
70         """绘制验证码字符"""
71         c_chars = get_chars()
72         strs = ' %s ' % ' '.join(c_chars)  # 每个字符前后以空格隔开
73         font = ImageFont.truetype(font_type, font_size)
74         font_width, font_height = font.getsize(strs)
75 
76         draw.text(((width - font_width) / 3, (height - font_height) / 3),
77                   strs, font=font, fill=fg_color)
78 
79         return ''.join(c_chars)
80 
81     if draw_lines:
82         create_lines()
83     if draw_points:
84         create_points()
85     strs = create_strs()
86     # 图形扭曲参数
87     params = [1 - float(random.randint(1, 2)) / 100,
88               0,
89               0,
90               0,
91               1 - float(random.randint(1, 10)) / 100,
92               float(random.randint(1, 2)) / 500,
93               0.001,
94               float(random.randint(1, 2)) / 500
95               ]
96     img = img.transform(size, Image.PERSPECTIVE, params)  # 创建扭曲
97     img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)  # 滤镜,边界加强(阈值更大)
98     return img, strs
check_code.py

需要配合 MONACO.TTF 字体使用

2、分页

 1 class Pagination(object):
 2     def __init__(self,totalCount,currentPage,CurentUrl,perPageItemNum=3,maxPageNum=3,):
 3         # 所有数据总个数
 4         self.total_count = int(totalCount)
 5         # 当前页
 6         try:
 7             v = int(currentPage)
 8             if v <= 0:
 9                 v = 1
10             self.current_page = v
11 
12         except Exception as e:
13             self.current_page = 1
14         # 每页显示行数
15         self.per_page_item_num = int(perPageItemNum)
16         # 最多页面数
17         self.max_page_num = int(maxPageNum)
18         self.current_url = CurentUrl
19 
20     def start(self):
21         return (self.current_page - 1)*self.per_page_item_num
22 
23     def end(self):
24         return self.current_page*self.per_page_item_num
25 
26     @property
27     def num_pages(self):
28         a,b = divmod(self.total_count,self.per_page_item_num)
29         if b == 0:
30             return a
31         return a+1
32 
33     @property
34     def pager_num_range(self):
35         # 当前页
36         # self.current_page
37         # 最多显示页码数 7
38         # self.per_page_num
39         # 总页数
40         # self.num_pages
41         # 判断最小极值
42         if self.max_page_num > self.num_pages:
43             # 根据当前页动态生成,设置页面显示数
44             return range(1, self.num_pages + 1)
45         # 总页数特别多时
46         part = int(self.max_page_num/2)
47 
48         if part >= self.current_page:
49             return range(1, self.max_page_num + 1)
50 
51         # 判断最大极值
52         if (self.current_page + part) >= self.num_pages:
53             return range(self.num_pages - self.max_page_num + 1, self.num_pages + 1)
54 
55         return range(self.current_page - part, self.current_page + part +1)
56 
57     def page_str(self):
58         page_list = []
59 
60         if self.current_page ==1:
61             prev = "<li><a href='#'>上一页</a></li>"
62         else:
63             prev = "<li><a href='%s?p=%s'>上一页</a></li>" %(self.current_url,self.current_page-1)
64         page_list.append(prev)
65 
66         first = "<li><a href='%s?p=1'>首页</a></li>" % self.current_url
67         page_list.append(first)
68 
69         for i in self.pager_num_range:
70             if self.current_page == i:
71                 tem = "<li class='active'><a href='%s?p=%s'>%s</a></li>" % (self.current_url, i,i)
72             else:
73                 tem = "<li><a href='%s?p=%s'>%s</a></li>" % (self.current_url, i, i)
74             page_list.append(tem)
75 
76         last = "<li><a href='%s?p=%s'>尾页</a></li>" % (self.current_url, self.num_pages)
77         page_list.append(last)
78 
79         if self.current_page == self.num_pages:
80             nex = "<li><a href='#'>下一页</a>"
81         else:
82             nex = "<li><a href='%s?p=%s'>下一页</a></li>" % (self.current_url, self.current_page + 1)
83         page_list.append(nex)
84 
85         return ''.join(page_list)
pager.py

使用时需要传入三个参数

  - 信息总数

  - 当前页

  - 当前url

1 obj_list = Pagination(article_list.count(), current_page, current_url)
2     data_list = article_list[obj_list.start():obj_list.end()]
视图函数中代码

3、form验证

  1 from django import forms
  2 from django.forms import fields,widgets
  3 from repository import models
  4 from django.core.exceptions import ValidationError
  5 
  6 class RegisterForm(forms.Form):
  7     username = fields.CharField(
  8         label='用户名',
  9         required=True,
 10         max_length=32,
 11         initial='请输入用户名',
 12         error_messages={
 13             'required': '用户名不能为空',
 14             'min_length': '用户名过短',
 15             'invalid': '格式错误',
 16         }
 17     )
 18     password = fields.CharField(
 19         label='密码',
 20         required=True,
 21         max_length=32,
 22         min_length=6,
 23         error_messages={
 24             'required': '密码不能为空',
 25             'min_length': '密码长度太短',
 26             'invalid': '格式有误',
 27         }
 28     )
 29     # fields.RegexField()   自定义正则
 30     confirm_pwd = fields.CharField(
 31         label='确认密码',
 32         required=True,
 33         max_length=32,
 34         min_length=6,
 35         error_messages={
 36             'required': '密码不能为空',
 37             'min_length': '密码长度太短',
 38             'invalid': '格式有误',
 39         }
 40     )
 41     email = fields.EmailField(
 42         widget=widgets.EmailInput(attrs={"class":"form-control","placeholder": "输入邮箱"}),
 43         label='邮箱',
 44         required=True,
 45         error_messages={
 46             'required': '不能为空',
 47         }
 48     )
 49     # 注意使用cleaned_data.get('username')取值
 50     def clean_username(self):
 51         ret = models.User.objects.filter(username=self.cleaned_data.get('username'))
 52         if not ret:
 53             return self.cleaned_data.get('username')
 54         else:
 55             raise ValidationError('用户名已存在')
 56 
 57     def clean_password(self):
 58         pwd = self.cleaned_data.get('password')
 59         if not pwd.isdigit():
 60             return self.cleaned_data.get('password')
 61         else:
 62             raise ValidationError('不能全是数字')
 63 
 64     # 验证前后密码是否一致
 65     def clean(self):
 66         password = self.cleaned_data.get('password')
 67         confirm_pwd = self.cleaned_data.get('confirm_pwd')
 68         if confirm_pwd == password:
 69             return self.cleaned_data
 70         else:
 71             raise ValidationError('密码输入不一致')
 72 
 73 class LoginForm(forms.Form):
 74     username = fields.CharField(
 75         label='用户名',
 76         required=True,
 77         max_length=32,
 78         initial='请输入用户名',
 79         error_messages={
 80             'required': '用户名不能为空',
 81         }
 82     )
 83     password = fields.CharField(
 84         label='密码',
 85         required=True,
 86         max_length=32,
 87         min_length=6,
 88         error_messages={
 89             'required': '密码不能为空',
 90         }
 91     )
 92     def clean(self):
 93         user_list = models.User.objects.all().values_list('username','password')
 94         user_list = list(user_list)
 95         login_list = (self.cleaned_data.get('username'),self.cleaned_data.get('password'))
 96         if login_list in user_list:
 97             return self.cleaned_data
 98         else:
 99             raise ValidationError('用户名或密码错误')
100 
101 class ArticleForm(forms.Form):
102     title = fields.CharField(
103         label='标题',
104         widget=widgets.TextInput(attrs={'class':'form-control', 'placeholder':'标题5-32个字符'}),
105         required=True,
106         min_length=5,
107         max_length=32,
108         error_messages={
109             'required':'不能为空',
110             'min_length':'标题最少为5个字节',
111             'max_length':'标题最少为32个字节',
112         }
113     )
114     summary = fields.CharField(
115         label='简介',
116         required=True,
117         widget=widgets.Textarea(attrs={'class':'form-control', 'placeholder':'简介10-64个字符'}),
118         max_length=32,
119         min_length=10,
120         error_messages={
121             'required':'不能为空',
122             'min_length':'简介最少为10个字节',
123             'max_length':'简介最多为64个字节',
124         }
125     )
126     detail = fields.CharField(
127         label='内容',
128         required=True,
129         widget=widgets.Textarea(attrs={'id':'detail','class': 'kind-content', 'placeholder': '简介32-256个字符'}),
130         max_length=256,
131         min_length=32,
132         error_messages={
133             'required': '不能为空',
134             'min_length': '简介最少为32个字节',
135             'max_length': '简介最少为256个字节',
136         }
137     )
138     user_choice = models.User.objects.all().values_list('id','username')
139     user = fields.IntegerField(
140         label='作者',
141         widget=widgets.Select(choices=user_choice, attrs={'class':'form-control'})
142     )
143     blog_choice = models.Blog.objects.all().values_list('id','title')
144     blog = fields.IntegerField(
145         label='博客',
146         widget=widgets.Select(choices=blog_choice, attrs={'class':'form-control'})
147     )
148     tags_choice = models.Tags.objects.all().values_list('id', 'caption')
149     tags = fields.IntegerField(
150         label='标签',
151         widget=widgets.Select(choices=tags_choice, attrs={'class':'form-control'})
152     )
153     category_choice = models.Category.objects.all().values_list('id','caption')
154     category = fields.IntegerField(
155         label='分类',
156         widget=widgets.Select(choices=category_choice, attrs={'class':'form-control'})
157     )
158 
159     def clean(self):
160         self.cleaned_data['user_id'] = self.cleaned_data.get('user')
161         self.cleaned_data['tags_id'] = self.cleaned_data.get('tags')
162         self.cleaned_data['blog_id'] = self.cleaned_data.get('blog')
163         self.cleaned_data['category_id'] = self.cleaned_data.get('category')
164         del self.cleaned_data['user']
165         del self.cleaned_data['tags']
166         del self.cleaned_data['blog']
167         del self.cleaned_data['category']
168         return self.cleaned_data
169 
170 class TroubleForm(forms.Form):
171     title = fields.CharField(
172         label='标题',
173         required=True,
174         widget=widgets.TextInput(attrs={'class':'form-control', 'placeholder':'输入标题5-32个字符'}),
175         max_length=32,
176         min_length=5,
177         error_messages={
178             'required':'标题不能为空',
179             'min_length':'标题最少为5个字节',
180             'invalid':'格式错误',
181         }
182     )
183     detail = fields.CharField(
184         label='内容',
185         required=True,
186         widget=widgets.Textarea(attrs={'id':'detail','class':'kind-content', 'placeholder':'输入内容5-256个字符'}),
187         max_length=256,
188         min_length=5,
189         error_messages={
190             'required': '内容不能为空',
191             'min_length': '内容最少为5个字节',
192             'invalid': '格式错误',
193         }
194     )
注册、登录、编辑、添加

有关form组件详情:稍后更新

 

原文地址:https://www.cnblogs.com/Lujun1028/p/9737857.html