项目关于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 %}

~~~

 

posted on 2019-06-17 09:51  江湖乄夜雨  阅读(324)  评论(0编辑  收藏  举报