作用:
ModelForm,即可用于数据库操作(部分),也可用于用户请求的验证(部分)!
Models.py
class PrettyNum(models.Model):
""" 靓号表 """
mobile = models.CharField(max_length=11, verbose_name='手机号')
# 允许为空 null=True, blank=True
price = models.IntegerField(verbose_name='价格', default=0, null=True, blank=True)
level_choices = (
(1, '一级'),
(2, '二级'),
(3, '三级'),
(4, '四级'),
)
level = models.SmallIntegerField(choices=level_choices, default=1,
verbose_name='级别')
status_choices = (
(1, '末占用'),
(2, '已占用')
)
status = models.SmallIntegerField(choices=status_choices, default=1,
verbose_name='状态')
定义 ModelForm 类
views.py
""" 定义ModelForm类 """
# 导入forms
from django import forms
# 从验证器导入正则表达式验证器
from django.core.validators import RegexValidator
# 钩子方法
from django.core.validators import ValidationError
# 正则
import re
class PrettyNumModelForm(forms.ModelForm):
""" 验证方法一:"""
# 需要重新定义校验的字段,
# mobile = forms.CharField(
# # 重写 label
# label='手机号',
# # 正则表达式匹配
# # 验证手机号: 第一位以1做开头,第二位取[3-9],接9个数字 (正则表达式, 提示信息)
# validators=[RegexValidator(r'^1[3-9]\d{9}$', '号码格式错误')],
# )0
price = forms.IntegerField(
label='建议价格',
# forms.HiddenInput() 隐藏输入框
# widget=forms.HiddenInput(),
)
class Meta:
# 定义对哪张表,哪些字段
# 定义 元类 class meta, 获取 models.PrettyNum 类(表)的字段
model = models.PrettyNum
# 需要的字段
fields = '__all__' # 全部字段
# fields = ['mobile', 'price', 'level', 'status'] #选择部分字段
# exclude = ['level'] # 排除某个字段
# 该方法是通过修改内部代码,对样式进行修改,样式的值可根据需求随意修改
# 重新定义 init 方法
def __init__(self, *args, **kwargs):
# 重新定义 它 父类的 init 方法
super().__init__(*args, **kwargs)
# 循环ModelForm中的所有字段,给每个字段增加插件设置
for name, field in self.fields.items():
print(name, field)
# 判断,给某个字段不加插件
# if name == 'password':
# continue
# 字段的插件
field.widget.attrs = {
"class": "form-control",
# "placeholder": field.label,
}
""" 验证方法二:钩子方法 """
def clean_mobile(self):
# 用户传入的数据mobile
txt_mobile = self.cleaned_data['mobile']
print('用户传入的数据mobile:', txt_mobile)
# 多条件判断
# 条件一:判断是否存在,返回True、False
flag = models.PrettyNum.objects.filter(mobile=txt_mobile).exists()
if flag: # 如果是True 验证不通过,抛出错误信息
raise ValidationError('号码已经存在')
# 条件二:判断格式 利用正则
pattern = r'^1[3-9]\d{9}$'
# 正则匹配,返回 返回True、False
match = re.match(pattern, txt_mobile)
print('match:', match)
if not match: # 布尔值取反, not True = False, 将成功执行转变成失败执行
raise ValidationError('格式错误')
return txt_mobile
def num_add(request):
""" 增加靓号 """
if request.method == 'GET':
print("请求方式是:", request.method)
# form实例化, 自动生成html表单
form = PrettyNumModelForm()
print(form)
context = {
'form': form
}
return render(request, 'num_add.html', context=context)
else:
print("请求方式是:", request.method)
# form对象实例化,得到提交的所有字段,
# data=request.POST 当参数传入, 包含每个字段的值
form = PrettyNumModelForm(data=request.POST)
# is_valid() 验证数据
if form.is_valid():
# cleaned_data 验证通过的数据
print(form.cleaned_data)
# 保存 类似 models.PrettyNum.objects.create(xxx=xxx)
form.save()
return redirect('/prettynum/list/')
else:
# 没有通过验证的数据
print(form.errors)
context = {
'form': form
}
# 再传回到html, 遍历后,还包含了 errors 信息
return render(request, 'num_add.html',
context=context)
num_add.html
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">新增靓号</h3>
</div>
<div class="panel-body">
{# 同行显示 <form class="form-inline">#}
{# novalidate 关掉浏览器校验#}
<form method="post" novalidate >
{% csrf_token %}
{# form 是 views传过来的对象,也是ModelForm自动生成的html内容#}
{% for field in form %}
<div class="form-group">
{# field 是循环后得到的每一个字段的 input框#}
{# filed.label 就是 字段的 verbose_name#}
<label>{{ field.label }}:</label>
{# <input type="text" ....................>#}
{{ field }}
{# 错误信息#}
<span style="color: red">{{ field.errors }}</span>
</div>
{% endfor %}
<button type="reset" class="btn btn-primary btn-sm">
<span class="glyphicon glyphicon-remove-circle" aria-hidden="true"></span>
Reset
</button>
<button type="submit" class="btn btn-primary btn-sm">
<span class="glyphicon glyphicon-ok-circle" aria-hidden="true"></span>
Submit
</button>
</form>
</div>
</div>
</div>
{% endblock %}
修改号码:
新建修改靓号的 ModelForm
钩子方法验证数据
# 修改靓号的 ModelForm
class PrettyNumEditModelForm(forms.ModelForm):
# 重新定义字段
# mobile 只显示,不让修改 disabled=True
# mobile = forms.CharField(disabled=True, label='手机号')
class Meta:
model = models.PrettyNum
# 定义需要显示和修改的字段
fields = ['mobile', 'price', 'level', 'status']
# widgets = {
# 'mobile': forms.TextInput(attrs={'class': 'form-control'}),
# 'price': forms.TextInput(attrs={'class': 'form-control'}),
# 'level': forms.Select(attrs={'class': 'form-control'}),
# 'status': forms.Select(attrs={'class': 'form-control'})
#
# }
# 该方法是通过修改内部代码,对样式进行修改,样式的值可根据需求随意修改
# 重新定义 init 方法
def __init__(self, *args, **kwargs):
# 重新定义 它 父类的 init 方法
super().__init__(*args, **kwargs)
# 循环ModelForm中的所有字段,给每个字段增加插件设置
for name, field in self.fields.items():
print(name, field)
# 判断,给某个字段不加插件
# if name == 'password':
# continue
# 字段的插件
field.widget.attrs = {
"class": "form-control",
# "placeholder": field.label,
}
# 定义钩子方法,
def clean_mobile(self):
# 条件一:除了当前id后,号码不能重复
# pk 拿到主键当前id: self.instance.pk
primary = self.instance.pk
print("主键id:", primary)
# 用户传入的数据mobile
txt_mobile = self.cleaned_data['mobile']
# exclude 排除了当前id后,号码不能重复
flag = models.PrettyNum.objects.exclude(id=primary).filter(mobile=txt_mobile).exists()
if flag: # 如果是True 验证不通过,抛出错误信息
raise ValidationError('号码已经存在')
# 条件二:判断格式 利用正则
pattern = r'^1[3-9]\d{9}$'
# 正则匹配,返回 返回True、False
match = re.match(pattern, txt_mobile)
print('match:', match)
if not match: # 布尔值取反, not True = False, 将成功执行转变成失败执行
raise ValidationError('格式错误')
return txt_mobile
修改号码的函数
def num_edit(request, nid): # 从urls里传入nid
""" 编辑靓号 """
if request.method == 'GET':
print("请求方式是:", request.method)
# 根据 id 拿到现有数据
row_data = models.PrettyNum.objects.get(id=nid)
print(row_data, type(row_data))
# form实例化, 自动生成html表单,
# instance=row_data 将现有结果带入参数传入
form = PrettyNumEditModelForm(instance=row_data)
context = {
'form': form
}
return render(request, 'num_edit.html',
context=context)
# post 请求提交数据
else:
print("请求方式是:", request.method)
# 得到提交的数据
row_data = models.PrettyNum.objects.get(id=nid)
# form对象实例化,得到提交的所有字段,
# data=request.POST 当参数传入, 包含每个字段的值
# instance=row_data 将现有结果带入参数传入,如果不传入就是新增
form = PrettyNumEditModelForm(data=request.POST, instance=row_data)
# 进行数据校验
if form.is_valid():
print(form.cleaned_data)
form.save()
return redirect('/prettynum/list/')
else:
print(form.errors)
context = {
'form': form
}
return render(request, 'num_edit.html',
context=context)
num_edit.html
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">编辑靓号</h3>
</div>
<div class="panel-body">
{# 同行显示 <form class="form-inline">#}
{# novalidate 关掉浏览器校验#}
<form method="post" novalidate >
{% csrf_token %}
{# form 是 views传过来的对象,也是ModelForm自动生成的html内容#}
{% for field in form %}
<div class="form-group">
{# field 是循环后得到的每一个字段的 input框#}
{# filed.label 就是 字段的 verbose_name#}
<label>{{ field.label }}:</label>
{# <input type="text" ....................>#}
{{ field }}
{# 错误信息#}
<span style="color: red">{{ field.errors }}</span>
</div>
{% endfor %}
<button type="reset" class="btn btn-primary btn-sm">
<span class="glyphicon glyphicon-remove-circle" aria-hidden="true"></span>
Reset
</button>
<button type="submit" class="btn btn-primary btn-sm">
<span class="glyphicon glyphicon-ok-circle" aria-hidden="true"></span>
Submit
</button>
</form>
</div>
</div>
</div>
{% endblock %}
搜索靓号:
扩展语法:
# 多条件
models.PrettyNum.objects.filter(mobile=1999999999, id=123)
# 也支持字典 增加 两个** 支持字典 空字典是获取所有
data_dict = {'mobile': '1999999999', 'id' = 123}
models.PrettyNum.objects.filter(**data)
models.PrettyNum.objects.filter(id=12) # 等于12
models.PrettyNum.objects.filter(id__gt=12) # 大于12
models.PrettyNum.objects.filter(id__gte=12) # 大于等于12
models.PrettyNum.objects.filter(id__lt=12) # 小于12
models.PrettyNum.objects.filter(id__lte=12) # 小于等于12
data_dict = {"id__lte":12}
models.PrettyNum.objects.filter(**data_dict)
models.PrettyNum.objects.filter(mobile="999") # 等于
models.PrettyNum.objects.filter(mobile__startswith="1999") # 筛选出以1999开头
models.PrettyNum.objects.filter(mobile__endswith="999") # 筛选出以999结尾
models.PrettyNum.objects.filter(mobile__contains="999") # 筛选出包含999
data_dict = {"mobile__contains":"999"}
models.PrettyNum.objects.filter(**data_dict)
搜索
views.py
# views.py
def num_list(request):
""" 搜索 """
# q1 = models.PrettyNum.objects.filter(mobile=18981889158, id=5)
# print(q1) # <QuerySet [<PrettyNum: PrettyNum object (5)>]>
#
#
# # 支持字典 空字典是获取所有
# data_dict = {'mobile': '18180955800', 'id': 10}
# q2 = models.PrettyNum.objects.filter(**data_dict)
# print(q2) # <QuerySet [<PrettyNum: PrettyNum object (5)>]>
"""号码列表"""
if request.method == 'GET':
print('访问方式是 GET ')
""" 搜索 """
# 先定义一个空字典
data_dict = {}
# 通过浏览器url传值
# q有值,就拿q; 没值,就拿空字符串
search_data = request.GET.get('q', "")
if search_data: # 不为空时,为字典增加key和value
data_dict['mobile__contains'] = search_data
# 如果value是空,空字典就是获取所有
res = models.PrettyNum.objects.filter(**data_dict)
print("搜索结果:", res)
# order_by(排序字段) , 默认是升序, - 减号表示降序
# 搜索 filter(**data_dict),空字典就是获取所有
queryset = models.PrettyNum.objects.filter(**data_dict).order_by('-id')
print(queryset)
for obj in queryset:
print(obj.id, obj.mobile, obj.price,
# level 和 status 有 choices 选择字段, 显示选择字段
obj.get_level_display(), obj.get_status_display())
context = {
'queryset': queryset,
'count': queryset.count(),
# 搜索的字符串再传回html 显示
'search_data': search_data,
}
return render(request, 'num_list.html',
context=context)
num_list.html
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div style="margin-bottom: 5px" class="clearfix">
<a href="/prettynum/add/" class="btn btn-success">
<span class="glyphicon glyphicon-plus-sign" aria-hidden="true"></span>
添加靓号
</a>
<div style="float: right; width: 300px">
{# get方式传递出现在地址栏#}
<form method="get">
<div class="input-group">
{# value="{{ search_data }} 将输入的搜索值显示出来#}
<input type="text" name="q" class="form-control" placeholder="Search for..." value="{{ search_data }}">
<span class="input-group-btn">
<button class="btn btn-default" type="submit">
<span class="glyphicon glyphicon-search" aria-hidden="true">Search</span>
</button>
</span>
</div>
</form>
</div>
</div>
<div class="panel panel-default">
<!-- Default panel contents -->
<div class="panel-heading">
<span class="glyphicon glyphicon-list" aria-hidden="true"></span>
靓号列表
</div>
<!-- Table -->
<table class="table table-hover">
<thead>
<tr>
<th>ID</th>
<th>号 码</th>
<th>价 格</th>
<th>级 别</th>
<th>状 态</th>
<th>操 作</th>
</tr>
</thead>
<tbody>
{% for obj in queryset %}
<tr>
<th>{{ obj.id }}</th>
<td>{{ obj.mobile }}</td>
<td>{{ obj.price }}</td>
{# level 和 status 有 choices 选择字段, 显示选择字段#}
<td>{{ obj.get_level_display }}</td>
<td>{{ obj.get_status_display }}</td>
<td>
<a href="/prettynum/{{ obj.id }}/edit/" class="btn btn-primary btn-xs">
<span class="glyphicon glyphicon-edit" aria-hidden="true"></span>
Edit
</a>
<a href="/prettynum/{{ obj.id }}/delete/" class="btn btn-danger btn-xs">
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
Delete
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div><span style="float: right">合计:{{ count }}</span></div>
</div>
{% endblock %}