项目关于Form组件与ModelForm组件的应用的地方
关于“继承”的说明
如果项目中有很多地方用到了ModelForm并且这些ModelForm有很多相同的设置。。。这样的话我们可以先定义一个父类Base,让他去继承forms.ModelForm,然后再让其他的类去继承这个Base父类~~这样的话那些相同的配置我们只要写在Base类中就好了~~~
form.py文件
这个项目的Form认证与ModelForm认证我都写在了一个utils包的form.py文件中,form.py文件的内容如下:
# -*- coding:utf-8 -*- import re from django import forms from django.forms import widgets from django.core.exceptions import ValidationError from customer import models #用户注册时用Form实现 class UserForm(forms.Form): # input name='username' 'username':'chao' errors.append('错误') username = forms.CharField(max_length=32, min_length=6, label='用户名', error_messages={'required': '这个字段必须填写!', 'max_length': '最大不能超过32位', 'min_length': '最小不能低于6位'}, ) password = forms.CharField(max_length=32, min_length=6, label='密码', error_messages={'required': '这个字段必须填写!', 'max_length': '最大不能超过32位', 'min_length': '最小不能低于6位'}, widget=widgets.PasswordInput(render_value=True),) r_password = forms.CharField(max_length=32, min_length=6, label='重复密码', error_messages={'required': '这个字段必须填写!', 'max_length': '最大不能超过32位', 'min_length': '最小不能低于6位'}, widget=widgets.PasswordInput(render_value=True),) email = forms.EmailField(max_length=32, label='邮箱', error_messages={'required': '这个字段必须填写!', 'max_length': '最大不能超过32位', 'invalid': '邮箱格式不对'}, ) # 局部钩子 def clean_username(self): val = self.cleaned_data.get('username') user_obj = models.UserInfo.objects.filter(username=val).first() if user_obj: raise ValidationError('该用户名已经存在,请换个名字!') else: return val def clean_password(self): val = self.cleaned_data.get('password') if val.isdecimal(): raise ValidationError('密码不能为纯数字') else: return val def clean_email(self): val = self.cleaned_data.get('email') if re.search('\w+@163.com$', val): return val else: raise ValidationError('必须是163网易邮箱!') # 全局钩子 密码与确认密码必须一致! def clean(self): password = self.cleaned_data.get('password') r_password = self.cleaned_data.get('r_password') if password != r_password: self.add_error('r_password', '两次密码不一致') else: return self.cleaned_data # 重写init方法~~给所有的标签定制统一的样式~ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) for field in self.fields: self.fields[field].widget.attrs.update({ 'class': 'form-control' }) #———————————————————————————————————————————————————————————————— # 添加编辑客户的时候用ModelForm做校验~~检验规则与提示设置的不全::: class CustomerModelForm(forms.ModelForm): class Meta: model = models.Customer fields = '__all__' ###其他的~错误信息、labels及字段定制都在Meta类中执行 #给出生日期加上type=date widgets = { 'birthday':forms.widgets.DateInput(attrs={'type':'date'}), } # 批量给各个标签加样式~注意得排除复选框对象,否则前端叠加起来了! def __init__(self,*args,**kwargs): super(CustomerModelForm, self).__init__(*args,**kwargs) #print(self.fields) #键是Customer对应的字段,值是对应属性xx类实例化出来的标签对象 from multiselectfield.forms.fields import MultiSelectFormField # 遍历这些实例化出来的标签对象 for field in self.fields.values(): # 不给复选框加样式!! if not isinstance(field,MultiSelectFormField): field.widget.attrs.update({ 'class':'form-control', }) #————————————————————————————————————————————————————————— # 添加编辑跟进记录——ModelForm class ConsultRecordForm(forms.ModelForm): class Meta: model = models.ConsultRecord fields = '__all__' # 排除删除状态字段 必须写在列表里 # 由于跟进人只能是当前用户,因此先排除他,我在前端自己构建标签 # 排除 删除状态字段 exclude = ['delete_status',] ###其他的~错误信息、labels及字段定制都在Meta类中执行
# 注意这里构造方法传一个request参数 def __init__(self,request,*args,**kwargs): super(ConsultRecordForm, self).__init__(*args,**kwargs) # 添加与编辑跟进记录的时候~仅显示当前用户的客户~~登陆用了auth组件~所以request有用户信息~~如果没用auth组件的话需要往request中加当前用户 self.fields['customer'].queryset = models.Customer.objects.filter(consultant=request.user) # 添加与编辑跟进记录的时候~跟进人只写当前的登陆的人 self.fields['consultant'].queryset = models.UserInfo.objects.filter(pk=request.user.pk) for field in self.fields: self.fields[field].widget.attrs.update({ 'class': 'form-control', })
由上面可知:注册用的是Form实现的,添加编辑公户以及添加编辑跟进记录用的是ModelForm实现的!
添加与编辑跟进记录的视图与模板 —— 复选框只展示当前登陆用户相关的信息
添加与编辑跟进记录的ModelForm里面的实现比较特殊,这里实现了models的choices属性依据特定条件筛选出特定值的效果,也就是说:只显示当前销售的客户,跟进人只显示当前销售。
注意这里构造方法要传入一个request参数!
添加与编辑跟进记录的视图函数
#添加跟进记录 class AddConsultRecord(View): def get(self,request): #将request参数传过去,实现单选框只出现跟自己有关的数据 form_obj = form.ConsultRecordForm(request) return render(request, 'add_edit_consultrecord.html', {'form_obj':form_obj}) def post(self,request): # print(request.POST) # 将request参数传过去,实现单选框只出现跟自己有关的数据 form_obj = form.ConsultRecordForm(request,request.POST) if form_obj.is_valid(): form_obj.save() return redirect('customer:consultrecord') else: return render(request, 'add_edit_consultrecord.html', {'form_obj':form_obj}) #删除跟进记录 def del_consult_record(request,pk): if request.method == 'GET': # 设置删除状态为True models.ConsultRecord.objects.filter(pk=pk).update(delete_status=True) return redirect('customer:consultrecord') #编辑跟进记录 class EditConsultRecord(View): def get(self,request,pk): # 注意这里是model对象! record_obj = models.ConsultRecord.objects.filter(pk=pk).first() form_obj = form.ConsultRecordForm(request,instance=record_obj) return render(request,'add_edit_consultrecord.html',{'form_obj':form_obj}) def post(self,request,pk): #注意这里是model对象 record_obj = models.ConsultRecord.objects.filter(pk=pk).first() # 这里要写instance~save才会翻译成update!否则会新增一条记录! form_obj = form.ConsultRecordForm(request,request.POST,instance=record_obj) if form_obj.is_valid(): form_obj.save() return redirect('customer:consultrecord') else: return render(request,'add_edit_consultrecord.html',{'form_obj':form_obj})
添加与编辑跟进记录的前端模板
注意:添加与跟进记录这里用的是同一个html页面!
{% extends 'base.html' %} {% block content %} <div class="content-wrapper"> <!-- Content Header (Page header) --> <section class="content-header"> <h1> <small>Optional description</small> </h1> <ol class="breadcrumb"> <li><a href="#"><i class="fa fa-dashboard"></i> Level</a></li> <li class="active">Here</li> </ol> </section> <!-- Main content --> <section class="content container-fluid"> <div class="container-fluid"> <div class="row"> <div class="col-md-8 col-md-offset-2"> {# 因为这个页面是添加与编辑共用的 因此这里的action在当前页面提交! #} <form action="" method="post" novalidate> {% csrf_token %} {% for field in form_obj %} <div class="form-group"> <label for="{{ field.id_for_label }}">{{ field.label }}</label> {{ field }} <span class="text-danger">{{ field.errors.0 }}</span> </div> {% endfor %} <button class="btn btn-primary pull-right">提交</button> </form> </div> </div> </div> </section> <!-- /.content --> </div> {% endblock content %}
添加与编辑公户的视图与模板
没有特殊处理了,直接上视图与模板的代码~
视图函数
#添加公户用户——用ModelForm实现~~ class AddCustomer(View): def get(self,request): # 实例化一个ModelForm对象 form_obj = form.CustomerModelForm() return render(request,'addcustomer.html',{'form_obj':form_obj}) def post(self,request): # 用request.POST实例化一个form_obj对象 form_obj = form.CustomerModelForm(request.POST) if form_obj.is_valid(): # save form_obj.save() return redirect('customer:customers') else: return render(request,'addcustomer.html',{'form_obj':form_obj}) #删除单条公户信息 class DeleteCustomer(View): def get(self,request,pk): models.Customer.objects.filter(pk=pk).delete() return redirect('customer:customers') #编辑单条公户信息 class EditCustomer(View): def get(self,request,pk): # 用model对象 customer_obj = models.Customer.objects.filter(pk=pk).first() # 利用model对象作为参数实例化ModelForm对象~要写instance= form_obj = form.CustomerModelForm(instance=customer_obj) return render(request,'editcustomer.html',{'form_obj':form_obj}) def post(self,request,pk): customer_obj = models.Customer.objects.filter(pk=pk).first() # 这里要写instance~save才会翻译成update!否则会新增一条记录! form_obj = form.CustomerModelForm(request.POST,instance=customer_obj) if form_obj.is_valid(): # save~ form_obj.save() return redirect('customer:customers') else: return render(request,'editcustomer.html',{'form_obj':form_obj})
添加与编辑公户的前端模板
{% extends 'base.html' %} {% block content %} <div class="content-wrapper"> <!-- Content Header (Page header) --> <section class="content-header"> <h1> 添加公户客户信息 <small>Optional description</small> </h1> <ol class="breadcrumb"> <li><a href="#"><i class="fa fa-dashboard"></i> Level</a></li> <li class="active">Here</li> </ol> </section> <!-- Main content --> <section class="content container-fluid"> <div class="container-fluid"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <form action="{% url 'customer:addcustomer' %}" method="post" novalidate> {% csrf_token %} {% for field in form_obj %} <div class="form-group"> <label for="{{ field.id_for_label }}">{{ field.label }}</label> {{ field }} <span class="text-danger">{{ field.errors.0 }}</span> </div> {% endfor %} <button class="btn btn-primary pull-right">提交</button> </form> </div> </div> </div> </section> <!-- /.content --> </div> {% endblock %}
{% extends 'base.html' %} {% block content %} <div class="container-fluid"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <form action="" method="post" novalidate> {% csrf_token %} {% for field in form_obj %} <div class="form-group"> <label for="{{ field.id_for_label }}">{{ field.label }}</label> {{ field }} <span class="text-danger">{{ field.errors.0 }}</span> </div> {% endfor %} <button class="btn btn-primary pull-right">提交</button> </form> </div> </div> </div> {% endblock %}
注册的视图与模板
注册用的是Form组件~
视图函数
这里注意认证装饰器的使用,需要在settings中配置一下认证失败后默认返回的路径!
另外这里利用了“字典打散”的方式传参!需要把没有用的键值对(r_password对应的)pop出去~~
# 注册——用Form实现~~ def register(request): if request.method == 'GET': form_obj = form.UserForm() return render(request,'register.html',{'form_obj':form_obj}) elif request.method == 'POST': #用post传过来的值实例化一个UserForm对象 form_obj = form.UserForm(request.POST) if form_obj.is_valid(): # 验证成功cleaned_data是一个验证字段为key,用户输入的值为value的字段 data = form_obj.cleaned_data # 但是记得先吧确认密码那个键值对pop出去 data.pop('r_password') # 特别注意:这样创建数据的前提是UserForm中的属性跟models对应的表中的属性一致! # 这里用 create_user 方法~~密码自动加密了! # 如果用 create_superuser 方法的话~创建的是超级用户 models.UserInfo.objects.create_user(**data) return redirect('customer:login') else: # 验证不成功的话还吧form_obj返回~这里既有错误信息也有之前输入的信息 return render(request,'register.html',{'form_obj':form_obj})
注册的前端渲染页面
{% extends 'base1.html' %} {% block title %}注册{% endblock title %} {% block main %} <form action="{% url 'customer:register' %}" method="post" novalidate> {% csrf_token %} {% for field in form_obj %} <div class="form-group"> <label for="{{ field.id_for_label }}">{{ field.label }}</label> {{ field }} <span class="text-danger">{{ field.errors.0 }}</span> </div> {% endfor %} <input type="submit" class="btn btn-success pull-right" value="注册"> </form> {% endblock main %}
~~~