Django之Form、ModelForm 组件

Django之Form、ModelForm 组件

一、Form组件:

django框架提供了一个form类,来处理web开发中的表单相关事项。众所周知,form最常做的是对用户输入的内容进行验证,为此django的forms类提供了全面的内容验证和保留用户上次输入数据的支持。

 form组件有2大大功能

对用户提交的内容进行验证(from表单/Ajax)

保留用户上次输入的内容

1、对用户提交的数据进行验证

form组件验证的原理

1.obj=Form()form组件类实例化时找到类中所有的字段 把这些字段 变成组合成字典;

self.fields={‘user’:正则表达式1,‘pwd’:正则表达式2}

2.循环self.fields字典(自己写的字段)

for  k,v  in self.fields.items():

    K是user,pwd

   v是正则表达式

3.每次循环通过self.fields字典的键, 一个一个的去get前端POST提交的数据 得到用户输入数据;

input_value= request.post.get(‘k’)(所以form字段的名称,要和前端的name属性匹配)

4.每次拿到用户输入的数据 (input_value)和进行正则表达式匹配;

5.匹配成功flag=True 匹配失败flag=falsh,最后 return flag  obj.is_valid=flag。

如果For自带的规则和正则满足不了验证需求,可在Form类中自定义方法,做扩展。

6.每个字段验证通过后,每个字段执执行self.clean_filelds函数(自定义 对Form类中的字段做单独验证,比如去数据库查询判断一下用户提交的数据是否存在?)

7. 执行Form组件的clean_form方法进行整体验证!(既然每个字段都验证了,就可以对用户提交的数据做整体验证了!比如进行联合唯一的验证)

 8.最后执行类似 clean_form的post_clean方法结束验证。(一般不使用post_clean做自定义过滤,clean_form方法完全可以解决)

form表单提交验证(form表单(会发起 get)提交刷新失去上次内容)

from django.shortcuts import render,HttpResponse,redirect
from django.forms import Form
from django.forms import fields

class Login(Form):
                              #from验证规则 用户名 6-10字符  required不能为空
    name=fields.CharField(max_length=10,
                          min_length=6,
                          required=True,
                           error_messages={
                               'required':'用户名不能为空',  #error_messages参数 自定义错误信息
                               'min_length':'太短了',
                                'max_length': "太长了",
                                           }

                          )
                                                                    # z注意name 必须和 from表单提交的一致,要么二则怎么对比校验呢
    pwd= fields.CharField(min_length=3,
                          required=True,
                          error_messages={
                              'required': '密码不能为空',  # error_messages参数 自定义错误信息
                              'min_length': '太短了',
                              'max_length': "太长了",
                          }



                          )


def index(request):
    if request.method=='GET':
        return render(request,'login.html')
    else:
        obj=Login(request.POST)  #把客户端提交来的form表单和 和匹配规则放在一起
        res=obj.is_valid()         #自动校验  给出结果 True 或者 False
        if res:                    #验证成功后obj.cleaned_data获取成功的数据,字典类型正好对应数据 的批量操作
            print(obj.cleaned_data)
            return redirect('http://www.baidu.com')                 #obj.errors获取错误信息(对象类型)就可以传到前端显示了!
        else:
           return  render(request,'login.html',{'obj':obj})
View Code

Aja提交验证(不会刷新,上次输入内容自动保留)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ajx提交</title>
</head>
<body>
<form method="post" action="/aja_login/" id="f1">
  {%csrf_token%}
<p>用户:<input type="text" name="name"></p>
<p>密码:<input type="password" name="pwd"></p>
<p><input type="button" onclick="Ajxform()" value="aja提交"></p>
</form>
</body>
<script src="/static/zhanggen.js"></script>
<script> function Ajxform(){
    $('.c1').remove()
    $.ajax({
        url:'/alogin/',
        type:'POST',
        dataType:'JSON',
        data:$('#f1').serialize(),
        success:function (args) {
            if (args.status){ }
            else{
{#                {status: false, msg: Object}#}
{#                console.log(args);#}
{#                Jquery循环服务端 传过来的 错误信息对象#}
                $.each(args.msg,function (index,value) {
                    console.log(index,value);
{#                 index----> name ["太短了"]#}
{#                 value-----pwd["密码不能为空"]#}
                    var tag=document.createElement('span');
                    tag.innerHTML= value[0];
                    tag.className='c1';
                    console.log(index);
{#                    寻找input下 属性为 name 和pwd的标签(字符串拼接) 在他们后半加 上tag标签也就是错误 信息 #}
                    $('#f1').find('input[name="'+ index +'"]').after(tag)

                })
            }

               }})}
</script>
</html>
View Code

Views

from django.shortcuts import render,HttpResponse,redirect
from django.forms import Form
from django.forms import fields
import json
class Login(Form):
                              #from验证规则 用户名 6-10字符  required不能为空
    name=fields.CharField(max_length=10,
                          min_length=6,
                          required=True,
                         error_messages={
                               'required':'用户名不能为空',  #error_messages参数 自定义错误信息
                               'min_length':'太短了',
                                'max_length': "太长了",
                                           }

                          )
                                                                    # z注意name 必须和 from表单提交的一致,要么二则怎么对比校验呢
    pwd= fields.CharField(min_length=3,
                          required=True,
                          error_messages={
                              'required': '密码不能为空',  # error_messages参数 自定义错误信息
                              'min_length': '太短了',
                              'max_length': "太长了",})



def agx_login(request):
    ret={'status':True,'msg':None}
    if request.method=='GET':
       return render(request,'ajalogin.html')
    else:
        obj=Login(request.POST)
        ret['status']=False
        ret['msg']=obj.errors
        return HttpResponse(json.dumps(ret))
View Code

2、动态生成HTML标签,保留用户上次输入的内容。

如何保留用户上次输入的内容?

由于form表单submit之后(发送post请求) 数据提交到 后端,不管前端输入的数据是否正确,服务端也要响应,所以页面会刷新;

所以无法保留用户上次输入的内容;如何解决呢?

1、把定义的定义的Form类,实例化(obj=Login() )内部调用一个__str__的方法,如果没有传值 返回<input type="text" name=“字段”>name='字段名空的input标签

2、把这个实例化之后的对象传到前端显示,让用户输入值;用户输入值通过post方法提交到后台。

3、如果后台实例化一个对象 obj=Login(request.POST)传入了值, <input type="text" name=“字段” value='request.post的数据'>然后后端再返回客户端就可以看到用户输入的值了!

保留用户上次输入的内容 是利用了 obj=Login(request.POST)接收了用户输入的值

视图

from django import  forms

class Myform(forms.Form):            #1、写1个继承forms.Form的类,定制form表单的数据类型;
    user=forms.CharField(max_length=32,min_length=3,label='用户名',
                         error_messages={'required':'不能为空'},
                         widget=forms.TextInput(attrs={'class':'sb','placeholder':'用户名'},)
                         )
    age=forms.IntegerField(label='年龄',error_messages={'required':'不能为空'},)
    email=forms.EmailField(label='邮箱',error_messages={'required':'不能为空'},)

def register2(request):
    if request.method=='GET':
        forms_obj=Myform()               #2、实例化类,把对象渲染到模板
        return render(request,'form——register.html',{'forms_obj':forms_obj})
    else:
        forms_obj=Myform(request.POST)   #3、把提交的数据封装成form对象
        if forms_obj.is_valid():         #4、使用 form对象的.is_valid()方法,校验提交过来的数据是否符合验证规则
            data=forms_obj.cleaned_data  #5、获取验证通过的数据(字典类型,可直接 **dict插入数据库)
            # User.objects.create_user(**data)
            return HttpResponse('OK')
        else:
            print(forms_obj.cleaned_data) #6、由于用户在form表单提交了值,利用这一点,
                                            # 把forms_obj=Myform(request.POST)渲染到前端就可以保存用户输入的值
        return render(request, 'form——register.html', {'forms_obj':forms_obj})
View Code

前台

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Django_form验证</title>
</head>
<body>
<form action="/form/" method="post" novalidate>
    {% csrf_token %}
    <p>用户:{{ forms_obj.user }}{{ forms_obj.errors.user.0 }}</p>
      <p>年龄:{{ forms_obj.age }}{{ forms_obj.errors.age.0}}</p>
    <p>邮箱:{{ forms_obj.email }}{{ forms_obj.errors.email.0}}</p>
    <button>提交</button>
</form>
</body>
</html>
View Code
from django.shortcuts import render,HttpResponse,redirect
from django.forms import Form
from django.forms import fields
import json
class Login(Form):
                              #from验证规则 用户名 6-10字符  required不能为空
    name=fields.CharField(max_length=10,
                          min_length=6,
                          required=True,
                         error_messages={
                               'required':'用户名不能为空',  #error_messages参数 自定义错误信息
                               'min_length':'太短了',
                                'max_length': "太长了",
                                           }

                          )
                                                                    # z注意name 必须和 from表单提交的一致,要么二则怎么对比校验呢
    pwd= fields.CharField(min_length=3,
                          required=True,
                          error_messages={
                              'required': '密码不能为空',  # error_messages参数 自定义错误信息
                              'min_length': '太短了',
                              'max_length': "太长了",})


def index(request):
    ret={'status':True,'msg':None}
    if request.method=='GET':
        obj=Login()                 #自动生成空白的input标签 发送给客户端)
        return render(request,'login.html',{'obj':obj})
    else:
        obj=Login(request.POST)  #把客户端提交来的form表单和 和匹配规则放在一
        res=obj.is_valid()         #自动生成空白的input标签 发送
        if res:                    #验证成功后obj.cleaned_data获取成功的数据,字典类型正好对应数据 的批量操作
            return HttpResponse('OK') #obj.errors获取错误信息(对象类型)就可以传到前端显示了!
        else:
           return render(request,'login.html',{'obj':obj})
View Code
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>
<form method="post" action="/login/" id="f1" novalidate >
     {%csrf_token%}
       <h1>用户登录</h1>
        <p>用户名 {{obj.name}}{{ obj.name.errors.0}}</p>
        <p>密码:{{ obj.pwd}}{{ obj.pwd.errors.0}}</p>
        <p><input type="submit" value="登录"></p>
</form>
</body>
</html>
View Code

 3、承上启下 form组件的套路(执行流程):

(1)在后端定义类和字段,实例化Form类;

(2)到用户 发送get请求时,服务端渲染到模板(空标签/默认值)发送到客户端显示

(3)客户端填数据,POST提交到后端;

(4)后端验证,返回结果给前端;(切记Form组件是在后端生成,发送给客户端显示,客户端填完数据在发回服务端!)

 

二、form组件使用:

1、导入Form插件

from django.forms import Form,fields
from django.forms import widgets
class Class_form(Form):
    title=fields.RegexField('全栈\d+',
                            # initial='全栈', #设置input标签中的默认值
                            min_length=2,
                            required=True,
                            error_messages={'invalid':"必须以全栈开头",
                                            'min_length':'太短了',
                                            'required':"不能为空",
                                            }
                            )
class Students(Form):
    name=fields.CharField(required=True,
    widget=widgets.TextInput(attrs={'class':'form-control'}),
    error_messages={'required':'姓名不能为空'})
    sex=fields.CharField(required=True,error_messages={'required':'不能为空'},
    widget = widgets.TextInput(attrs={'class': 'form-control'})

                         )
    cls_id=fields.IntegerField(widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'),
    attrs = {'class': 'form-control'}

                                                     ),

                                )
class teacher_form(Form):
    tname=fields.CharField(required=True,error_messages={'required':"姓名不能为空"} )
   #  classes=fields.CharField( #多选 不能用这个插件不使用request.post.getlist()取值
   #      widget=widgets.Select(choices=models.Classes.objects.values_list(),
   #      attrs={'multiple':'multiple'})
   #                           )
   #  classes=fields.CharField(widget=widgets.SelectMultiple(
   # # 如果在fields.CharField字段该插件,得到会是{'tname': 'ww', 'classes': "['2', '3']"}字符串
   #   choices= models.Classes.objects.values_list('id','title')) )
    classes=fields.MultipleChoiceField(
        choices=models.Classes.objects.values_list('id','title'),
        widget=widgets.SelectMultiple,error_messages={'required':'选择不能为空'})
    def __int__(self,*args,**kwargs):  #解决数据不同步的bug,每次form组件实例化时 都重新去数据库拿数据
        super(teacher_form,self).__init__(*args,**kwargs)
        self.fields['classes'].widget.choices=models.Classes.objects.values_list('id','title')



class Test_form(Form):
    name=fields.CharField()  #动态生成 text类型的input标签
    text=fields.CharField(widget=widgets.Textarea,) #动态生成文本框
    age=fields.CharField(widget=widgets.CheckboxInput) #动态生成单选框
    holby=fields.MultipleChoiceField(                  #MultipleChoiceField动态生成复选框
        choices=[(1,'篮球'),(2,"足球"),(3,"高俅")],
        widget=widgets.CheckboxSelectMultiple)
    sex=fields.MultipleChoiceField(
        choices=[(1,''),(2,'')],
        widget=widgets.RadioSelect
    )

    select=fields.ChoiceField(choices=[(2, '北京'), (3, '唐县')]) #通过ChoiceField字段动态生成单选框
                                                                   # 通过form组件的MultipleChoiceField字段
    mselect=fields.MultipleChoiceField(choices=[(1,'管家佐'),(2,'万宝利') ],
                                 widget=widgets.SelectMultiple)
    file=fields.FileField()  #通过form组件的.FileField动态生成 文件上传input标签,注意在提交到后台是对象
View Code


2、定义类和字段(验证规则) 扩展方法

class Form_login(Form):

                 字段                               参数

  user=fields.RegexField正则表达式,验证规则,error_messages={错误信息},widget=html标签插件attrs = {'标签插件的属性'})

四、form的钩子函数

Django的form在obj.is_valid()方法内提供2个钩子函数,以便我们随时调用他自定制一些复杂的验证规则;

 局部钩子函数 

class Class_form(Form):
    title=fields.RegexField('全栈\d+',
                            # initial='全栈', #设置input标签中的默认值
                            min_length=2,
                            required=True,
                            error_messages={'invalid':"必须以全栈开头",
                                            'min_length':'太短了',
                                            'required':"不能为空",
                                            }
                            )
class Students(Form):
    name=fields.CharField(required=True,
    widget=widgets.TextInput(attrs={'class':'form-control'}),
    error_messages={'required':'姓名不能为空'})
    sex=fields.CharField(required=True,error_messages={'required':'不能为空'},
    widget = widgets.TextInput(attrs={'class': 'form-control'})

                         )
    cls_id=fields.IntegerField(widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'),
    attrs = {'class': 'form-control'}

                                                     ),

                                )
class teacher_form(Form):
    tname=fields.CharField(required=True,error_messages={'required':"姓名不能为空"} )
   #  classes=fields.CharField( #多选 不能用这个插件不使用request.post.getlist()取值
   #      widget=widgets.Select(choices=models.Classes.objects.values_list(),
   #      attrs={'multiple':'multiple'})
   #                           )
   #  classes=fields.CharField(widget=widgets.SelectMultiple(
   # # 如果在fields.CharField字段该插件,得到会是{'tname': 'ww', 'classes': "['2', '3']"}字符串
   #   choices= models.Classes.objects.values_list('id','title')) )
    classes=fields.MultipleChoiceField(
        choices=models.Classes.objects.values_list('id','title'),
        widget=widgets.SelectMultiple,error_messages={'required':'选择不能为空'})
    def __int__(self,*args,**kwargs):  #解决数据不同步的bug,每次form组件实例化时 都重新去数据库拿数据
        super(teacher_form,self).__init__(*args,**kwargs)
        self.fields['classes'].widget.choices=models.Classes.objects.values_list('id','title')



class Test_form(Form):
    name=fields.CharField()  #动态生成 text类型的input标签
    text=fields.CharField(widget=widgets.Textarea,) #动态生成文本框
    age=fields.CharField(widget=widgets.CheckboxInput) #动态生成单选框
    holby=fields.MultipleChoiceField(                  #MultipleChoiceField动态生成复选框
        choices=[(1,'篮球'),(2,"足球"),(3,"高俅")],
        widget=widgets.CheckboxSelectMultiple)
    sex=fields.MultipleChoiceField(
        choices=[(1,''),(2,'')],
        widget=widgets.RadioSelect
    )

    select=fields.ChoiceField(choices=[(2, '北京'), (3, '唐县')]) #通过ChoiceField字段动态生成单选框
                                                                   # 通过form组件的MultipleChoiceField字段
    mselect=fields.MultipleChoiceField(choices=[(1,'管家佐'),(2,'万宝利') ],
                                 widget=widgets.SelectMultiple)
    file=fields.FileField()  #通过form组件的.FileField动态生成 文件上传input标签,注意在提交到后台是对象
View Code

 全局钩子函数

如果要想要同时对2个form字段进行验证,就需要全局钩子函数(应用 验证2次输入的密码是否一致),可以调用他们自定制复杂的form验证规则,

问题1:  注册页面输入为空,报错:keyError:找不到password

def clean(self):
        print("---",self.cleaned_data)
        #  if self.cleaned_data["password"]==self.cleaned_data["repeat_password"]:        
        #  报错原因:self.cleaned_data是干净数据,如果页面没有输入内容,则self.cleaned_data没有password。
        改如下:
        if self.cleaned_data.get("password")==self.cleaned_data.get("repeat_password"):
            return self.cleaned_data
        else:
            raise ValidationError("两次密码不一致")
View Code

  

四、渲染到模板

1.简单粗暴型

注意:模板语言{{form_obj.as_p}},一定要在设置lable参数

class UserInfoForm(Form):
    name=fields.CharField(required=True,error_messages={'reqired':'用户名不能为空'},label='姓名') 
    email=fields.EmailField(required=True,error_messages={'reqired':'用户名不能为空'},label='邮箱')
    pary=fields.ChoiceField(choices=[(1,"技术部"),(2,'销售部'),(3,'市场部'),],label='部门')
{{obj.as_p}}以P标签的形式全部显示

<table> 注意加table标签形式全部显示
{{obj.as_table}}
</table>


<ul>注意加ul标签形式全部显示
{{obj.as_table}}
</ul>
View Code

2.灵活定制型

<p>姓名:{{ obj.name }}</p>
<p>性别:{{ obj.sex }}</p>
<p>爱好: {{ obj.holby }}</p>
<p>婚姻状况:{{ obj.age }}</p>
<p>个人简介 {{ obj.text }}</p>
<p>工作地点: {{ obj.select }}</p>
<p>居住地点:{{ obj.mselect }}</p>
<p>资料上传:{{obj.file}}</p>
View Code

五、页面显示用户填完数据提交回来后台验证

数据校验

obj=classForm_login(request.POST )

默认校验:obj=classForm_login(data={} ) 含有错误信息: obj=Class_form(initial={'title':class_obj.title})只有html标签

obj.is_valid()  获取校验结果

obj.errors获取错误信息

obj.cleand_data 获取正确的数据

六、基于Form组件的学生管理系统(项目)

路由

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^classes/',views.classes),
    url(r'^classes_add(\d+)/',views.classes_add),
    url(r'^classes_edit(\d+)/',views.classes_edit),
    url(r'^students/',views.students),
    url(r'^students_add(\d+)/',views.students_add),
    url(r'^students.edit(\d+)/',views.students_edit),
    url(r'^teachers/',views.teacher),
    url(r'^teacher_add/',views.teacher_add),
    url(r'^teacher_edit(\d+)/',views.teacher_edit),
    url(r'^test/',views.test),
]
View Code

视图

def classes(request):
    if request.method=='GET':
        c_list=models.Classes.objects.all()
        return render(request,'class_list.html',{'clist':c_list})

def classes_add(request,args):
    if request.method=='GET':
        obj=Class_form()
        return render(request,'class_add.html',{'obj':obj})
    else:
        obj=Class_form(request.POST)
        if obj.is_valid():
            models.Classes.objects.create(**obj.cleaned_data )
            return redirect('/classes/')
        else:
            print('NI')
            return render(request,'class_add.html',{'obj':obj})

def classes_edit(request,args):
    if request.method=='GET':
        class_obj=models.Classes.objects.filter(id=args).first()
        obj=Class_form(initial={'title':class_obj.title})
        return render(request,'class_edit.html',{'obj':obj,'nid':args})
    else:
        obj = Class_form(request.POST)
        if obj.is_valid():
            models.Classes.objects.filter(id=args).update(**obj.cleaned_data)
            return redirect('/classes/')
        else:
            return render(request, 'class_edit.html', {'obj': obj, 'nid': args})


def students(request):
    if request.method=='GET':
        students=models.Student.objects.all()
        return render(request,'students.html',{'student':students})

def students_add(request,nid):
    if request.method=='GET':
        obj=Students()
        return render(request,'student_add.html',{'obj':obj,'nid':nid})
    else:
        obj=Students(request.POST)
        if obj.is_valid():
            models.Student.objects.create(**obj.cleaned_data)
            return redirect('/students/')
        else:
            return render(request,'student_add.html',{'obj':obj,'nid':nid})


def students_edit(request,nid):
    if request.method=='GET':
        # print(models.Student.objects.filter(id=nid).values('name','sex','cls_id').first())
        obj=Students(initial=models.Student.objects.filter(id=nid).values('name','sex','cls_id').first())
        return render(request,'student_edit.html',{'obj':obj,'nid':nid})
    else:
        obj1=Students(request.POST)
        if obj1.is_valid():
            models.Student.objects.filter(id=nid).update(**obj1.cleaned_data)
            return redirect('/students/')
        else:
            return render(request, 'student_edit.html', {'obj': obj1, 'nid': nid})

def teacher(request):
    if request.method=='GET':
        teacher_list=models.teacher.objects.all()
        return render(request,'teacher_list.html',{'tlist':teacher_list})

def teacher_add(request):
    if request.method=='GET':
        print(request.method)
        obj=teacher_form()
        return render(request,'teacher_add.html',{'obj':obj})
    else:
        obj=teacher_form(request.POST)
        if obj.is_valid():
            classe_ids=obj.cleaned_data.pop('classes') #老师任教的班级
            tname=obj.cleaned_data   #老师的姓名
            create_teacher=models.teacher.objects.create(**tname)
            create_teacher.c2t.add(*classe_ids)
            return redirect('/teachers/')
        else:
            return render(request,'teacher_add.html',{'obj':obj})









def teacher_edit(request,arg):
    if request.method=='GET':
        teacher_obj=models.teacher.objects.filter(id=arg).first()
        classes_ids=teacher_obj.c2t.values_list('id')
        cid_list=list(zip(*classes_ids))[0] if list(zip(*classes_ids)) else[]
        # print(cid_list) 元组
        obj=teacher_form(initial={'tname':teacher_obj.tname,'classes':cid_list})
        return render(request,'teacher_edit.html',{'obj':obj,'nid':arg} )
    else:
        obj=teacher_form(request.POST)
        if obj.is_valid():
            teacher_name=obj.cleaned_data.pop('tname')
            models.teacher.objects.filter(id=arg).update(tname=teacher_name)
            obj1=models.teacher.objects.filter(id=arg).first()
            classes=obj.cleaned_data.pop('classes')
            obj1.c2t.set(classes)
            return redirect('/teachers/')
        else:
            return render(request,'teacher_add.html',{'obj':obj})








def test(request):
    if request.method=='GET':
        obj=Test_form(initial={'holby':[ 1,2],'name':'张根','text':'我来自太行山','sex':1})
        # # print(models.Student.cls.objects.values_list())
        # obj=models.Student.objects.filter(id=1).first()
        # print(obj.cls.title)
        return render(request,'test.html',{'obj':obj})
View Code

form组件

from django.shortcuts import render,HttpResponse,redirect
from  app01 import models
from django.forms import Form,fields
from django.forms import widgets

class Class_form(Form):
    title=fields.RegexField('全栈\d+',
                            # initial='全栈', #设置input标签中的默认值
                            min_length=2,
                            required=True,
                            error_messages={'invalid':"必须以全栈开头",
                                            'min_length':'太短了',
                                            'required':"不能为空",
                                            }
                            )
class Students(Form):
    name=fields.CharField(required=True,
    widget=widgets.TextInput(attrs={'class':'form-control'}),
    error_messages={'required':'姓名不能为空'})
    sex=fields.CharField(required=True,error_messages={'required':'不能为空'},
    widget = widgets.TextInput(attrs={'class': 'form-control'})

                         )
    cls_id=fields.IntegerField(widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'),
    attrs = {'class': 'form-control'}

                                                     ),

                                )
class teacher_form(Form):
    tname=fields.CharField(required=True,error_messages={'required':"姓名不能为空"} )
   #  classes=fields.CharField( #多选 不能用这个插件不使用request.post.getlist()取值
   #      widget=widgets.Select(choices=models.Classes.objects.values_list(),
   #      attrs={'multiple':'multiple'})
   #                           )
   #  classes=fields.CharField(widget=widgets.SelectMultiple(
   # # 如果在fields.CharField字段该插件,得到会是{'tname': 'ww', 'classes': "['2', '3']"}字符串
   #   choices= models.Classes.objects.values_list('id','title')) )
    classes=fields.MultipleChoiceField(
        choices=models.Classes.objects.values_list('id','title'),
        widget=widgets.SelectMultiple,error_messages={'required':'选择不能为空'})
    def __int__(self,*args,**kwargs):  #解决数据不同步的bug,每次form组件实例化时 都重新去数据库拿数据
        super(teacher_form,self).__init__(*args,**kwargs)
        self.fields['classes'].choices=models.Classes.objects.values_list('id','title')



class Test_form(Form):
    name=fields.CharField()  #动态生成 text类型的input标签
    text=fields.CharField(widget=widgets.Textarea,) #动态生成文本框
    age=fields.CharField(widget=widgets.CheckboxInput) #动态生成单选框
    holby=fields.MultipleChoiceField(                  #MultipleChoiceField动态生成复选框
        choices=[(1,'篮球'),(2,"足球"),(3,"高俅")],
        widget=widgets.CheckboxSelectMultiple)
    sex=fields.MultipleChoiceField(
        choices=[(1,'男'),(2,'女')],
        widget=widgets.RadioSelect
    )

    select=fields.ChoiceField(choices=[(2, '北京'), (3, '唐县')]) #通过ChoiceField字段动态生成单选框
                                                                   # 通过form组件的MultipleChoiceField字段
    mselect=fields.MultipleChoiceField(choices=[(1,'管家佐'),(2,'万宝利') ],
                                 widget=widgets.SelectMultiple)
    file=fields.FileField()  #通过form组件的.FileField动态生成 文件上传input标签,注意在提交到后台是对象
View Code

模板

班级管理(1对1)

班级显示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>班级列表</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.5-dist/css/bootstrap.css">
</head>
<body>
<div style="width: 500px;margin: 0 auto">
<h1>班级列表</h1>
<ul>
    {% for obj in clist %}
        <li>{{ obj.title }}
            <a href="/classes_add{{ obj.id }}/">添加</a>
            <a href="/classes_edit{{ obj.id }}/">编辑</a>
        </li>
    {% endfor %}
</ul>
</div>
</body>
</html>
View Code

添加班级

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>添加班级</title>
</head>
<body>
<h1>添加班级</h1>
<form action='/classes_add{{2}}/' method="post" novalidate>
    {% csrf_token %}
  <p>班级添加:{{ obj.title}} {{ obj.errors.title.0}}</p>
   <p><input type="submit" value="提交"></p>
</form>

</body>
</html>
View Code

编辑班级

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>编辑班级</title>
</head>
<body>
<form action="/classes_edit{{nid}}/" method="post">
    {% csrf_token %}
   <p>编辑班级:{{ obj.title }}{{obj.title.errors.0}} </p>
    <input type="submit" value="提交">
</form>
</body>
</html>
View Code

学生管理1对多

学生显示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>学生管理</title>
</head>
<body>
<h1>学生管理</h1>
<ul>
    {% for row in student %}
        <li>{{row.name}}
            {{row.sex}}
            <a href="/students_add{{row.id}}/">添加</a>
            <a href="/students.edit{{row.id}}/">编辑</a>
        </li>
    {% endfor %}
</ul>
</body>
</html>
View Code

学生添加

<!DOCTYPE html>
<html lang="en">
<head>
    <h1>添加学生</h1>
    <meta charset="UTF-8">
    <title>添加学生</title>
</head>
<body>
<form action="/students_add{{ nid}}/" method="post" novalidate>
     {% csrf_token%}
<p>姓名:{{ obj.name}}{{obj.name.errors.0}}</p>
<p>性别:{{ obj.sex}}{{obj.sex.errors.0}} </p>
<p>班级:{{ obj.cls_id }}{{ obj.errors.0}}  </p>
<p><input type="submit" value="提交"></p>
 </form>
</body>
</html>
View Code

学生编辑

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>老师编辑</title>
</head>
<body>
<form action="/teacher_edit{{ nid }}/" method="post">
    {% csrf_token %}
<p>老师姓名:{{obj.tname }}</p>
<p>班级:{{ obj.classes }}</p>
<input type="submit" value="提交">
</form>
</body>
</html>
View Code

 老师管理多对多

老师显示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>老师列表</title>
</head>
<body>
<table border="1">
<thead>
<tr><td>ID</td><td>老师</td><td>任教班级</td><td colspan="3">操作</td></tr>
</thead>
<tbody>
     {% for teacher in tlist %}
    <tr>
        <td>{{ teacher.id }}</td>
        <td>{{teacher.tname}}</td>
        <td>
            {% for row in teacher.c2t.values %}
            {{ row.title }}
            {% endfor %}
         </td>
        <td><a href="/teacher_add/">添加</a></td>
        <td><a href="/teacher_edit{{ teacher.id}}/">编辑</a></td>
    </tr>
    {% endfor %}
</tbody>
</table>



</body>
</html>
View Code

老师添加

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>添加老师</title>
</head>
<body>
<form action="/teacher_add/" method="post" novalidate>
{% csrf_token %}
<p>老师姓名:{{ obj.tname }}{{obj.tname.errors.0}}</p>
<p>任教班级:{{ obj.classes }}{{obj.classes.errors.0}}</p>
<p><input type="submit" value="提交"></p>
 </form>
</body>
</html>
View Code

老师编辑

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>老师编辑</title>
</head>
<body>
<form action="/teacher_edit{{ nid }}/" method="post">
    {% csrf_token %}
<p>老师姓名:{{obj.tname }}</p>
<p>班级:{{ obj.classes }}</p>
<input type="submit" value="提交">
</form>
</body>
</html>
View Code

 七、ModelForm

使用Django开发web程序阶段回顾:

1.手动对单表进行增、删、该、查,手动把ORM操作获取的数据渲染到模板;(阶段1)

2.Form组件(类),自动生成标签(input、select),并对用户输入的数据做规则验证;(阶段2)

3.ModelForm顾名思义就Form和Django的Model数据库模型结合体,可以简单、方便得对数据库进行增加、编辑操作和验证标签的生成

Form组件和ModelForm的区别

ModelForm是Django Model.py和Form组件的结合体,可以简单/快速使用 Form验证和数据库操作功能,但不如Form组件灵活,如果在使用Django做web开发过程中验证的数据和数据库字段相关(可以对表进行增、删、改操,注意 Many to many字段,也可以级联操作第3张关系表;),建议优先使用ModelForm,用起来更方便些,但是在使用ModelForm的时候慎用fields='__all__',获取数据库所有字段势必造成性能损耗;

使用ModelForm

前端:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{{ form_obj.as_p }}
{#<p>姓名:{{form_obj.name  }}</p>#}
</body>
</html>
View Code

后端视图:

from app02 import models
from django.forms import ModelForm
class UserModalForm(ModelForm):
    class Meta:
        model=models.UserInfo #(该字段必须为 model  数据库中表)
        fields= '__all__'   #(该字段必须为 fields 数据库中表)

def add(request):
     # 实例化models_form
    if request.method=='GET':
        obj = UserModalForm()
        return render(request,'rbac/user_add.html',locals())
    else:
        obj=UserModalForm(request.POST)
        if obj.is_valid():
            data=obj.cleaned_data
            obj.save()  #form验证通过直接 添加用户信息到数据库
        return render(request, 'rbac/user_add.html', locals())
View Code

 model.py

使用使用Form组件和ModelForm注意事项:

1、model.py一点要写verbose_name='名称'参数,才会在前端显示P标签的标题;

from django.db import models

class Department(models.Model):
    title=models.CharField(max_length=32,verbose_name='部门名称')
    def __str__(self):
        return self.title

class UserInfo(models.Model):
    name=models.CharField(max_length=32,verbose_name='姓名')
    emai=models.EmailField(max_length=32,verbose_name='邮箱')
    pary=models.ForeignKey(Department,verbose_name='部门')
View Code

如果使用Form组件在前端显示标题,可以设置Form类中的lable参数

class UserInfoForm(Form):
    name=fields.CharField(required=True,error_messages={'reqired':'用户名不能为空'},label='姓名')
    email=fields.EmailField(required=True,error_messages={'reqired':'用户名不能为空'},label='邮箱')
    pary=fields.ChoiceField(choices=[(1,"技术部"),(2,'销售部'),(3,'市场部'),],label='部门')
View Code

2、pary=fields.ChoiceField(choices=models.Department.objects.values_list('pk','title'),label='部门'),前端select标签不能随数据库操作实时更新;

在Department表添加一条数据之后,前端select标签中的数据不能随数据库实时更新;

原因:

不管是ModelForm还是Form组件本质就是个类,fields(字段)本质就是类中的1个静态属性,在类第一次加载时赋值,永远不会更新;

 

解决方案1(手动档):

重写__init__方法,每次实例化对象,就去获取一次数据库内容,重新赋值;

class UserInfoForm(Form):
    name=fields.CharField(required=True,error_messages={'reqired':'用户名不能为空'},label='姓名')
    email=fields.EmailField(required=True,error_messages={'reqired':'用户名不能为空'},label='邮箱')
    pary=fields.ChoiceField(label='部门')
    # pary=fields.ChoiceField(choices=models.Department.objects.values_list('pk','title'),label='部门')
    def __init__(self,*args,**kwargs):
        super(UserInfoForm,self).__init__(*args,**kwargs)
        self.fields['pary'].choices=models.Department.objects.values_list('pk','title')
View Code

解决方案2(自动档)

导入ModelChoiceField 模块  from django.forms.models import ModelChoiceField 

使用ModelChoiceField 字段

from django.forms import fields
from django.forms import widgets
from django.forms import ModelForm
from django.forms.models import ModelChoiceField

class UserInfoForm(Form):
    name=fields.CharField(required=True,error_messages={'reqired':'用户名不能为空'},label='姓名')
    email=fields.EmailField(required=True,error_messages={'reqired':'用户名不能为空'},label='邮箱')
    #方案1
    pary=ModelChoiceField(queryset=models.Department.objects.all(),label='部门')
View Code

 

自动挡虽好,但是也有缺陷;前端option标签的内容,需要借助model.py中的__str__方法,生成;

八、扩展 widgets之富文本编辑框

 如果你的web应用涉及到了发表文章、表情评论就必须使用富文本编辑框,小编调研了两款 Kindeditor、CKeditor;

Kindeditor

1.Kindeditor简介

http://kindeditor.net/docs/usage.html

2.下载Kindeditor编辑器

http://www.kindsoft.net/down.php

3.引入Kindeditor编辑器

<script src="/static/kindeditor-4.1.10/kindeditor-all.js"></script>

4.使用Kindeditor编辑器

a.创建textarea 标签

<textarea name="content" id="a" cols="30" rows="10"></textarea>

b.初始化Kindeditor

<script>
  var edit=KindEditor.create('#a',{
        width:'700px',
        height:'500px',
        uploadJson:'/upload_file/',
        extraFileUploadParams:{
            'csrfmiddlewaretoken':'{{ csrf_token }}',}})
</script>

c. Kindeditor功能定制

<script>
        var edit = KindEditor.create('#a', {
            items: [ //填写自己想要的功能
                'fontname', 'fontsize', '|', 'forecolor', 'hilitecolor', 'bold', 'italic', 'underline',
                'removeformat', '|', 'justifyleft', 'justifycenter', 'justifyright', 'insertorderedlist',
                'insertunorderedlist', '|', 'emoticons', 'image', 'link'],
            width: '700px',
            height: '500px',
            uploadJson: '/upload_file/',
            extraFileUploadParams: {
                'csrfmiddlewaretoken': '{{ csrf_token }}',
            }
        })
    </script>

d.kindeditor的API

// 取得HTML内容
html = editor.html();

// 同步数据后可以直接取得textarea的value
editor.sync();
html = document.getElementById('editor_id').value; // 原生API
html = K('#editor_id').val(); // KindEditor Node API
html = $('#editor_id').val(); // jQuery

// 设置HTML内容
editor.html('HTML内容');

CKeditor结合Django Form

1.下载  django-ckeditor 和pillow

pip install django-ckeditor 
pip install pillow

2.setings

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rbac.apps.RbacConfig',
    'arya.apps.AryaConfig',
    'cmdb.apps.CmdbConfig',
    'rest_framework',
    'ckeditor',
    # 'ckeditor_uploader'
]
CKEDITOR_CONFIGS = {
    'default': {
        'toolbar': (
            # ['div','Source','-','Save','NewPage','Preview','-','Templates'],
            # ['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print','SpellChecker','Scayt'],
            # ['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'],
            # ['Form','Checkbox','Radio','TextField','Textarea','Select','Button', 'ImageButton','HiddenField'],
            # ['Bold','Italic','Underline','Strike','-','Subscript','Superscript'],
            # ['NumberedList','BulletedList','-','Outdent','Indent','Blockquote'],
            # ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'],
            # ['Link','Unlink','Anchor'],
            ['Smiley',],

            # ['Styles','Format','Font','FontSize'],
            # ['TextColor','BGColor'],
            # ['Maximize','ShowBlocks','-','About', 'pbckcode'],

        ),
    }
}
功能定制

3. 收集Django 项目所有 静态文件  到的目录static目录   /   直接下载ckeditor到项目static

python  manage.py collectstatic

#设置媒体文件的上传的路径
MEDIA_URL="/media/"
#MEDIA_ROOT=os.path.join(BASE_DIR,"media")
MEDIA_ROOT='/tmp/cmdb_media/'



STATIC_ROOT = os.path.join(BASE_DIR, 'static')
设置media路径+static路径

注意:此步骤如果提示 需要你设置MEDIA_URL目录 和STATIC_ROOT 就先暂且设置一下,最终目的是把CKeditor的js插件copy到你的静态文件存放目录下;

4.后端生成 textarea +加前端 ckedit 插件生成 富文本编辑

from ckeditor.widgets import CKEditorWidget
class comment(Form): #评论框
    content =fields.CharField(widget=CKEditorWidget)
 {{ obj.content|safe }}

<script src="/static/pligin/ckeditor/ckeditor-init.js"></script>
<script src="/static/pligin/ckeditor/ckeditor/ckeditor.js"></script>
View Code

5.CKEditorWidget API

  <script>
        $('.root_reply_btn').click(function () {
            var $reply_user = $(this).attr('name');
            var $pid = $(this).attr('pid');
            $('#comment_submit').attr('pid', $pid);
            CKEDITOR.instances.id_content.insertText('回复' + $reply_user + ':');
            CKEDITOR.instances.id_content.insertHtml('<br>');
            CKEDITOR.instances.id_content.focus(); #使CKEditorWidget进入编辑状态 (MD在后台测试了半天!!)


        });
CKEDITOR.instances.id_content.document.$.body.firstChild.textContent 获取p标签的文本内容
</script>

6.完成效果

博客链接:http://www.cnblogs.com/wupeiqi/articles/6144178.html

http://www.cnblogs.com/yuanchenqi/articles/7439088.html#3770465

DjangoForm补充:http://www.cnblogs.com/yuanchenqi/articles/7487059.html

 

一、Form组件:

django框架提供了一个form类,来处理web开发中的表单相关事项。众所周知,form最常做的是对用户输入的内容进行验证,为此django的forms类提供了全面的内容验证和保留用户上次输入数据的支持。

 form组件有2大大功能

对用户提交的内容进行验证(from表单/Ajax)

保留用户上次输入的内容

1、对用户提交的数据进行验证

form组件验证的原理

1.obj=Form()form组件类实例化时找到类中所有的字段 把这些字段 变成组合成字典;

self.fields={‘user’:正则表达式1,‘pwd’:正则表达式2}

2.循环self.fields字典(自己写的字段)

for  k,v  in self.fields.items():

    K是user,pwd

   v是正则表达式

3.每次循环通过self.fields字典的键, 一个一个的去get前端POST提交的数据 得到用户输入数据;

input_value= request.post.get(‘k’)(所以form字段的名称,要和前端的name属性匹配)

4.每次拿到用户输入的数据 (input_value)和进行正则表达式匹配;

5.匹配成功flag=True 匹配失败flag=falsh,最后 return flag  obj.is_valid=flag。

如果For自带的规则和正则满足不了验证需求,可在Form类中自定义方法,做扩展。

6.每个字段验证通过后,每个字段执执行self.clean_filelds函数(自定义 对Form类中的字段做单独验证,比如去数据库查询判断一下用户提交的数据是否存在?)

7. 执行Form组件的clean_form方法进行整体验证!(既然每个字段都验证了,就可以对用户提交的数据做整体验证了!比如进行联合唯一的验证)

 8.最后执行类似 clean_form的post_clean方法结束验证。(一般不使用post_clean做自定义过滤,clean_form方法完全可以解决)

form表单提交验证(form表单(会发起 get)提交刷新失去上次内容)

from django.shortcuts import render,HttpResponse,redirect
from django.forms import Form
from django.forms import fields

class Login(Form):
                              #from验证规则 用户名 6-10字符  required不能为空
    name=fields.CharField(max_length=10,
                          min_length=6,
                          required=True,
                           error_messages={
                               'required':'用户名不能为空',  #error_messages参数 自定义错误信息
                               'min_length':'太短了',
                                'max_length': "太长了",
                                           }

                          )
                                                                    # z注意name 必须和 from表单提交的一致,要么二则怎么对比校验呢
    pwd= fields.CharField(min_length=3,
                          required=True,
                          error_messages={
                              'required': '密码不能为空',  # error_messages参数 自定义错误信息
                              'min_length': '太短了',
                              'max_length': "太长了",
                          }



                          )


def index(request):
    if request.method=='GET':
        return render(request,'login.html')
    else:
        obj=Login(request.POST)  #把客户端提交来的form表单和 和匹配规则放在一起
        res=obj.is_valid()         #自动校验  给出结果 True 或者 False
        if res:                    #验证成功后obj.cleaned_data获取成功的数据,字典类型正好对应数据 的批量操作
            print(obj.cleaned_data)
            return redirect('http://www.baidu.com')                 #obj.errors获取错误信息(对象类型)就可以传到前端显示了!
        else:
           return  render(request,'login.html',{'obj':obj})
View Code

Aja提交验证(不会刷新,上次输入内容自动保留)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ajx提交</title>
</head>
<body>
<form method="post" action="/aja_login/" id="f1">
  {%csrf_token%}
<p>用户:<input type="text" name="name"></p>
<p>密码:<input type="password" name="pwd"></p>
<p><input type="button" onclick="Ajxform()" value="aja提交"></p>
</form>
</body>
<script src="/static/zhanggen.js"></script>
<script> function Ajxform(){
    $('.c1').remove()
    $.ajax({
        url:'/alogin/',
        type:'POST',
        dataType:'JSON',
        data:$('#f1').serialize(),
        success:function (args) {
            if (args.status){ }
            else{
{#                {status: false, msg: Object}#}
{#                console.log(args);#}
{#                Jquery循环服务端 传过来的 错误信息对象#}
                $.each(args.msg,function (index,value) {
                    console.log(index,value);
{#                 index----> name ["太短了"]#}
{#                 value-----pwd["密码不能为空"]#}
                    var tag=document.createElement('span');
                    tag.innerHTML= value[0];
                    tag.className='c1';
                    console.log(index);
{#                    寻找input下 属性为 name 和pwd的标签(字符串拼接) 在他们后半加 上tag标签也就是错误 信息 #}
                    $('#f1').find('input[name="'+ index +'"]').after(tag)

                })
            }

               }})}
</script>
</html>
View Code

Views

from django.shortcuts import render,HttpResponse,redirect
from django.forms import Form
from django.forms import fields
import json
class Login(Form):
                              #from验证规则 用户名 6-10字符  required不能为空
    name=fields.CharField(max_length=10,
                          min_length=6,
                          required=True,
                         error_messages={
                               'required':'用户名不能为空',  #error_messages参数 自定义错误信息
                               'min_length':'太短了',
                                'max_length': "太长了",
                                           }

                          )
                                                                    # z注意name 必须和 from表单提交的一致,要么二则怎么对比校验呢
    pwd= fields.CharField(min_length=3,
                          required=True,
                          error_messages={
                              'required': '密码不能为空',  # error_messages参数 自定义错误信息
                              'min_length': '太短了',
                              'max_length': "太长了",})



def agx_login(request):
    ret={'status':True,'msg':None}
    if request.method=='GET':
       return render(request,'ajalogin.html')
    else:
        obj=Login(request.POST)
        ret['status']=False
        ret['msg']=obj.errors
        return HttpResponse(json.dumps(ret))
View Code

2、动态生成HTML标签,保留用户上次输入的内容。

如何保留用户上次输入的内容?

由于form表单submit之后(发送post请求) 数据提交到 后端,不管前端输入的数据是否正确,服务端也要响应,所以页面会刷新;

所以无法保留用户上次输入的内容;如何解决呢?

1、把定义的定义的Form类,实例化(obj=Login() )内部调用一个__str__的方法,如果没有传值 返回<input type="text" name=“字段”>name='字段名空的input标签

2、把这个实例化之后的对象传到前端显示,让用户输入值;用户输入值通过post方法提交到后台。

3、如果后台实例化一个对象 obj=Login(request.POST)传入了值, <input type="text" name=“字段” value='request.post的数据'>然后后端再返回客户端就可以看到用户输入的值了!

保留用户上次输入的内容 是利用了 obj=Login(request.POST)接收了用户输入的值

视图

from django import  forms

class Myform(forms.Form):            #1、写1个继承forms.Form的类,定制form表单的数据类型;
    user=forms.CharField(max_length=32,min_length=3,label='用户名',
                         error_messages={'required':'不能为空'},
                         widget=forms.TextInput(attrs={'class':'sb','placeholder':'用户名'},)
                         )
    age=forms.IntegerField(label='年龄',error_messages={'required':'不能为空'},)
    email=forms.EmailField(label='邮箱',error_messages={'required':'不能为空'},)

def register2(request):
    if request.method=='GET':
        forms_obj=Myform()               #2、实例化类,把对象渲染到模板
        return render(request,'form——register.html',{'forms_obj':forms_obj})
    else:
        forms_obj=Myform(request.POST)   #3、把提交的数据封装成form对象
        if forms_obj.is_valid():         #4、使用 form对象的.is_valid()方法,校验提交过来的数据是否符合验证规则
            data=forms_obj.cleaned_data  #5、获取验证通过的数据(字典类型,可直接 **dict插入数据库)
            # User.objects.create_user(**data)
            return HttpResponse('OK')
        else:
            print(forms_obj.cleaned_data) #6、由于用户在form表单提交了值,利用这一点,
                                            # 把forms_obj=Myform(request.POST)渲染到前端就可以保存用户输入的值
        return render(request, 'form——register.html', {'forms_obj':forms_obj})
View Code

前台

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Django_form验证</title>
</head>
<body>
<form action="/form/" method="post" novalidate>
    {% csrf_token %}
    <p>用户:{{ forms_obj.user }}{{ forms_obj.errors.user.0 }}</p>
      <p>年龄:{{ forms_obj.age }}{{ forms_obj.errors.age.0}}</p>
    <p>邮箱:{{ forms_obj.email }}{{ forms_obj.errors.email.0}}</p>
    <button>提交</button>
</form>
</body>
</html>
View Code
from django.shortcuts import render,HttpResponse,redirect
from django.forms import Form
from django.forms import fields
import json
class Login(Form):
                              #from验证规则 用户名 6-10字符  required不能为空
    name=fields.CharField(max_length=10,
                          min_length=6,
                          required=True,
                         error_messages={
                               'required':'用户名不能为空',  #error_messages参数 自定义错误信息
                               'min_length':'太短了',
                                'max_length': "太长了",
                                           }

                          )
                                                                    # z注意name 必须和 from表单提交的一致,要么二则怎么对比校验呢
    pwd= fields.CharField(min_length=3,
                          required=True,
                          error_messages={
                              'required': '密码不能为空',  # error_messages参数 自定义错误信息
                              'min_length': '太短了',
                              'max_length': "太长了",})


def index(request):
    ret={'status':True,'msg':None}
    if request.method=='GET':
        obj=Login()                 #自动生成空白的input标签 发送给客户端)
        return render(request,'login.html',{'obj':obj})
    else:
        obj=Login(request.POST)  #把客户端提交来的form表单和 和匹配规则放在一
        res=obj.is_valid()         #自动生成空白的input标签 发送
        if res:                    #验证成功后obj.cleaned_data获取成功的数据,字典类型正好对应数据 的批量操作
            return HttpResponse('OK') #obj.errors获取错误信息(对象类型)就可以传到前端显示了!
        else:
           return render(request,'login.html',{'obj':obj})
View Code
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>
<form method="post" action="/login/" id="f1" novalidate >
     {%csrf_token%}
       <h1>用户登录</h1>
        <p>用户名 {{obj.name}}{{ obj.name.errors.0}}</p>
        <p>密码:{{ obj.pwd}}{{ obj.pwd.errors.0}}</p>
        <p><input type="submit" value="登录"></p>
</form>
</body>
</html>
View Code

 3、承上启下 form组件的套路(执行流程):

(1)在后端定义类和字段,实例化Form类;

(2)到用户 发送get请求时,服务端渲染到模板(空标签/默认值)发送到客户端显示

(3)客户端填数据,POST提交到后端;

(4)后端验证,返回结果给前端;(切记Form组件是在后端生成,发送给客户端显示,客户端填完数据在发回服务端!)

 

二、form组件使用:

1、导入Form插件

from django.forms import Form,fields
from django.forms import widgets
class Class_form(Form):
    title=fields.RegexField('全栈\d+',
                            # initial='全栈', #设置input标签中的默认值
                            min_length=2,
                            required=True,
                            error_messages={'invalid':"必须以全栈开头",
                                            'min_length':'太短了',
                                            'required':"不能为空",
                                            }
                            )
class Students(Form):
    name=fields.CharField(required=True,
    widget=widgets.TextInput(attrs={'class':'form-control'}),
    error_messages={'required':'姓名不能为空'})
    sex=fields.CharField(required=True,error_messages={'required':'不能为空'},
    widget = widgets.TextInput(attrs={'class': 'form-control'})

                         )
    cls_id=fields.IntegerField(widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'),
    attrs = {'class': 'form-control'}

                                                     ),

                                )
class teacher_form(Form):
    tname=fields.CharField(required=True,error_messages={'required':"姓名不能为空"} )
   #  classes=fields.CharField( #多选 不能用这个插件不使用request.post.getlist()取值
   #      widget=widgets.Select(choices=models.Classes.objects.values_list(),
   #      attrs={'multiple':'multiple'})
   #                           )
   #  classes=fields.CharField(widget=widgets.SelectMultiple(
   # # 如果在fields.CharField字段该插件,得到会是{'tname': 'ww', 'classes': "['2', '3']"}字符串
   #   choices= models.Classes.objects.values_list('id','title')) )
    classes=fields.MultipleChoiceField(
        choices=models.Classes.objects.values_list('id','title'),
        widget=widgets.SelectMultiple,error_messages={'required':'选择不能为空'})
    def __int__(self,*args,**kwargs):  #解决数据不同步的bug,每次form组件实例化时 都重新去数据库拿数据
        super(teacher_form,self).__init__(*args,**kwargs)
        self.fields['classes'].widget.choices=models.Classes.objects.values_list('id','title')



class Test_form(Form):
    name=fields.CharField()  #动态生成 text类型的input标签
    text=fields.CharField(widget=widgets.Textarea,) #动态生成文本框
    age=fields.CharField(widget=widgets.CheckboxInput) #动态生成单选框
    holby=fields.MultipleChoiceField(                  #MultipleChoiceField动态生成复选框
        choices=[(1,'篮球'),(2,"足球"),(3,"高俅")],
        widget=widgets.CheckboxSelectMultiple)
    sex=fields.MultipleChoiceField(
        choices=[(1,''),(2,'')],
        widget=widgets.RadioSelect
    )

    select=fields.ChoiceField(choices=[(2, '北京'), (3, '唐县')]) #通过ChoiceField字段动态生成单选框
                                                                   # 通过form组件的MultipleChoiceField字段
    mselect=fields.MultipleChoiceField(choices=[(1,'管家佐'),(2,'万宝利') ],
                                 widget=widgets.SelectMultiple)
    file=fields.FileField()  #通过form组件的.FileField动态生成 文件上传input标签,注意在提交到后台是对象
View Code


2、定义类和字段(验证规则) 扩展方法

class Form_login(Form):

                 字段                               参数

  user=fields.RegexField正则表达式,验证规则,error_messages={错误信息},widget=html标签插件attrs = {'标签插件的属性'})

四、form的钩子函数

Django的form在obj.is_valid()方法内提供2个钩子函数,以便我们随时调用他自定制一些复杂的验证规则;

 局部钩子函数 

class Class_form(Form):
    title=fields.RegexField('全栈\d+',
                            # initial='全栈', #设置input标签中的默认值
                            min_length=2,
                            required=True,
                            error_messages={'invalid':"必须以全栈开头",
                                            'min_length':'太短了',
                                            'required':"不能为空",
                                            }
                            )
class Students(Form):
    name=fields.CharField(required=True,
    widget=widgets.TextInput(attrs={'class':'form-control'}),
    error_messages={'required':'姓名不能为空'})
    sex=fields.CharField(required=True,error_messages={'required':'不能为空'},
    widget = widgets.TextInput(attrs={'class': 'form-control'})

                         )
    cls_id=fields.IntegerField(widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'),
    attrs = {'class': 'form-control'}

                                                     ),

                                )
class teacher_form(Form):
    tname=fields.CharField(required=True,error_messages={'required':"姓名不能为空"} )
   #  classes=fields.CharField( #多选 不能用这个插件不使用request.post.getlist()取值
   #      widget=widgets.Select(choices=models.Classes.objects.values_list(),
   #      attrs={'multiple':'multiple'})
   #                           )
   #  classes=fields.CharField(widget=widgets.SelectMultiple(
   # # 如果在fields.CharField字段该插件,得到会是{'tname': 'ww', 'classes': "['2', '3']"}字符串
   #   choices= models.Classes.objects.values_list('id','title')) )
    classes=fields.MultipleChoiceField(
        choices=models.Classes.objects.values_list('id','title'),
        widget=widgets.SelectMultiple,error_messages={'required':'选择不能为空'})
    def __int__(self,*args,**kwargs):  #解决数据不同步的bug,每次form组件实例化时 都重新去数据库拿数据
        super(teacher_form,self).__init__(*args,**kwargs)
        self.fields['classes'].widget.choices=models.Classes.objects.values_list('id','title')



class Test_form(Form):
    name=fields.CharField()  #动态生成 text类型的input标签
    text=fields.CharField(widget=widgets.Textarea,) #动态生成文本框
    age=fields.CharField(widget=widgets.CheckboxInput) #动态生成单选框
    holby=fields.MultipleChoiceField(                  #MultipleChoiceField动态生成复选框
        choices=[(1,'篮球'),(2,"足球"),(3,"高俅")],
        widget=widgets.CheckboxSelectMultiple)
    sex=fields.MultipleChoiceField(
        choices=[(1,''),(2,'')],
        widget=widgets.RadioSelect
    )

    select=fields.ChoiceField(choices=[(2, '北京'), (3, '唐县')]) #通过ChoiceField字段动态生成单选框
                                                                   # 通过form组件的MultipleChoiceField字段
    mselect=fields.MultipleChoiceField(choices=[(1,'管家佐'),(2,'万宝利') ],
                                 widget=widgets.SelectMultiple)
    file=fields.FileField()  #通过form组件的.FileField动态生成 文件上传input标签,注意在提交到后台是对象
View Code

 全局钩子函数

如果要想要同时对2个form字段进行验证,就需要全局钩子函数(应用 验证2次输入的密码是否一致),可以调用他们自定制复杂的form验证规则,

问题1:  注册页面输入为空,报错:keyError:找不到password

def clean(self):
        print("---",self.cleaned_data)
        #  if self.cleaned_data["password"]==self.cleaned_data["repeat_password"]:        
        #  报错原因:self.cleaned_data是干净数据,如果页面没有输入内容,则self.cleaned_data没有password。
        改如下:
        if self.cleaned_data.get("password")==self.cleaned_data.get("repeat_password"):
            return self.cleaned_data
        else:
            raise ValidationError("两次密码不一致")
View Code

  

四、渲染到模板

1.简单粗暴型

注意:模板语言{{form_obj.as_p}},一定要在设置lable参数

class UserInfoForm(Form):
    name=fields.CharField(required=True,error_messages={'reqired':'用户名不能为空'},label='姓名') 
    email=fields.EmailField(required=True,error_messages={'reqired':'用户名不能为空'},label='邮箱')
    pary=fields.ChoiceField(choices=[(1,"技术部"),(2,'销售部'),(3,'市场部'),],label='部门')
{{obj.as_p}}以P标签的形式全部显示

<table> 注意加table标签形式全部显示
{{obj.as_table}}
</table>


<ul>注意加ul标签形式全部显示
{{obj.as_table}}
</ul>
View Code

2.灵活定制型

<p>姓名:{{ obj.name }}</p>
<p>性别:{{ obj.sex }}</p>
<p>爱好: {{ obj.holby }}</p>
<p>婚姻状况:{{ obj.age }}</p>
<p>个人简介 {{ obj.text }}</p>
<p>工作地点: {{ obj.select }}</p>
<p>居住地点:{{ obj.mselect }}</p>
<p>资料上传:{{obj.file}}</p>
View Code

五、页面显示用户填完数据提交回来后台验证

数据校验

obj=classForm_login(request.POST )

默认校验:obj=classForm_login(data={} ) 含有错误信息: obj=Class_form(initial={'title':class_obj.title})只有html标签

obj.is_valid()  获取校验结果

obj.errors获取错误信息

obj.cleand_data 获取正确的数据

六、基于Form组件的学生管理系统(项目)

路由

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^classes/',views.classes),
    url(r'^classes_add(\d+)/',views.classes_add),
    url(r'^classes_edit(\d+)/',views.classes_edit),
    url(r'^students/',views.students),
    url(r'^students_add(\d+)/',views.students_add),
    url(r'^students.edit(\d+)/',views.students_edit),
    url(r'^teachers/',views.teacher),
    url(r'^teacher_add/',views.teacher_add),
    url(r'^teacher_edit(\d+)/',views.teacher_edit),
    url(r'^test/',views.test),
]
View Code

视图

def classes(request):
    if request.method=='GET':
        c_list=models.Classes.objects.all()
        return render(request,'class_list.html',{'clist':c_list})

def classes_add(request,args):
    if request.method=='GET':
        obj=Class_form()
        return render(request,'class_add.html',{'obj':obj})
    else:
        obj=Class_form(request.POST)
        if obj.is_valid():
            models.Classes.objects.create(**obj.cleaned_data )
            return redirect('/classes/')
        else:
            print('NI')
            return render(request,'class_add.html',{'obj':obj})

def classes_edit(request,args):
    if request.method=='GET':
        class_obj=models.Classes.objects.filter(id=args).first()
        obj=Class_form(initial={'title':class_obj.title})
        return render(request,'class_edit.html',{'obj':obj,'nid':args})
    else:
        obj = Class_form(request.POST)
        if obj.is_valid():
            models.Classes.objects.filter(id=args).update(**obj.cleaned_data)
            return redirect('/classes/')
        else:
            return render(request, 'class_edit.html', {'obj': obj, 'nid': args})


def students(request):
    if request.method=='GET':
        students=models.Student.objects.all()
        return render(request,'students.html',{'student':students})

def students_add(request,nid):
    if request.method=='GET':
        obj=Students()
        return render(request,'student_add.html',{'obj':obj,'nid':nid})
    else:
        obj=Students(request.POST)
        if obj.is_valid():
            models.Student.objects.create(**obj.cleaned_data)
            return redirect('/students/')
        else:
            return render(request,'student_add.html',{'obj':obj,'nid':nid})


def students_edit(request,nid):
    if request.method=='GET':
        # print(models.Student.objects.filter(id=nid).values('name','sex','cls_id').first())
        obj=Students(initial=models.Student.objects.filter(id=nid).values('name','sex','cls_id').first())
        return render(request,'student_edit.html',{'obj':obj,'nid':nid})
    else:
        obj1=Students(request.POST)
        if obj1.is_valid():
            models.Student.objects.filter(id=nid).update(**obj1.cleaned_data)
            return redirect('/students/')
        else:
            return render(request, 'student_edit.html', {'obj': obj1, 'nid': nid})

def teacher(request):
    if request.method=='GET':
        teacher_list=models.teacher.objects.all()
        return render(request,'teacher_list.html',{'tlist':teacher_list})

def teacher_add(request):
    if request.method=='GET':
        print(request.method)
        obj=teacher_form()
        return render(request,'teacher_add.html',{'obj':obj})
    else:
        obj=teacher_form(request.POST)
        if obj.is_valid():
            classe_ids=obj.cleaned_data.pop('classes') #老师任教的班级
            tname=obj.cleaned_data   #老师的姓名
            create_teacher=models.teacher.objects.create(**tname)
            create_teacher.c2t.add(*classe_ids)
            return redirect('/teachers/')
        else:
            return render(request,'teacher_add.html',{'obj':obj})









def teacher_edit(request,arg):
    if request.method=='GET':
        teacher_obj=models.teacher.objects.filter(id=arg).first()
        classes_ids=teacher_obj.c2t.values_list('id')
        cid_list=list(zip(*classes_ids))[0] if list(zip(*classes_ids)) else[]
        # print(cid_list) 元组
        obj=teacher_form(initial={'tname':teacher_obj.tname,'classes':cid_list})
        return render(request,'teacher_edit.html',{'obj':obj,'nid':arg} )
    else:
        obj=teacher_form(request.POST)
        if obj.is_valid():
            teacher_name=obj.cleaned_data.pop('tname')
            models.teacher.objects.filter(id=arg).update(tname=teacher_name)
            obj1=models.teacher.objects.filter(id=arg).first()
            classes=obj.cleaned_data.pop('classes')
            obj1.c2t.set(classes)
            return redirect('/teachers/')
        else:
            return render(request,'teacher_add.html',{'obj':obj})








def test(request):
    if request.method=='GET':
        obj=Test_form(initial={'holby':[ 1,2],'name':'张根','text':'我来自太行山','sex':1})
        # # print(models.Student.cls.objects.values_list())
        # obj=models.Student.objects.filter(id=1).first()
        # print(obj.cls.title)
        return render(request,'test.html',{'obj':obj})
View Code

form组件

from django.shortcuts import render,HttpResponse,redirect
from  app01 import models
from django.forms import Form,fields
from django.forms import widgets

class Class_form(Form):
    title=fields.RegexField('全栈\d+',
                            # initial='全栈', #设置input标签中的默认值
                            min_length=2,
                            required=True,
                            error_messages={'invalid':"必须以全栈开头",
                                            'min_length':'太短了',
                                            'required':"不能为空",
                                            }
                            )
class Students(Form):
    name=fields.CharField(required=True,
    widget=widgets.TextInput(attrs={'class':'form-control'}),
    error_messages={'required':'姓名不能为空'})
    sex=fields.CharField(required=True,error_messages={'required':'不能为空'},
    widget = widgets.TextInput(attrs={'class': 'form-control'})

                         )
    cls_id=fields.IntegerField(widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'),
    attrs = {'class': 'form-control'}

                                                     ),

                                )
class teacher_form(Form):
    tname=fields.CharField(required=True,error_messages={'required':"姓名不能为空"} )
   #  classes=fields.CharField( #多选 不能用这个插件不使用request.post.getlist()取值
   #      widget=widgets.Select(choices=models.Classes.objects.values_list(),
   #      attrs={'multiple':'multiple'})
   #                           )
   #  classes=fields.CharField(widget=widgets.SelectMultiple(
   # # 如果在fields.CharField字段该插件,得到会是{'tname': 'ww', 'classes': "['2', '3']"}字符串
   #   choices= models.Classes.objects.values_list('id','title')) )
    classes=fields.MultipleChoiceField(
        choices=models.Classes.objects.values_list('id','title'),
        widget=widgets.SelectMultiple,error_messages={'required':'选择不能为空'})
    def __int__(self,*args,**kwargs):  #解决数据不同步的bug,每次form组件实例化时 都重新去数据库拿数据
        super(teacher_form,self).__init__(*args,**kwargs)
        self.fields['classes'].choices=models.Classes.objects.values_list('id','title')



class Test_form(Form):
    name=fields.CharField()  #动态生成 text类型的input标签
    text=fields.CharField(widget=widgets.Textarea,) #动态生成文本框
    age=fields.CharField(widget=widgets.CheckboxInput) #动态生成单选框
    holby=fields.MultipleChoiceField(                  #MultipleChoiceField动态生成复选框
        choices=[(1,'篮球'),(2,"足球"),(3,"高俅")],
        widget=widgets.CheckboxSelectMultiple)
    sex=fields.MultipleChoiceField(
        choices=[(1,'男'),(2,'女')],
        widget=widgets.RadioSelect
    )

    select=fields.ChoiceField(choices=[(2, '北京'), (3, '唐县')]) #通过ChoiceField字段动态生成单选框
                                                                   # 通过form组件的MultipleChoiceField字段
    mselect=fields.MultipleChoiceField(choices=[(1,'管家佐'),(2,'万宝利') ],
                                 widget=widgets.SelectMultiple)
    file=fields.FileField()  #通过form组件的.FileField动态生成 文件上传input标签,注意在提交到后台是对象
View Code

模板

班级管理(1对1)

班级显示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>班级列表</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.5-dist/css/bootstrap.css">
</head>
<body>
<div style="width: 500px;margin: 0 auto">
<h1>班级列表</h1>
<ul>
    {% for obj in clist %}
        <li>{{ obj.title }}
            <a href="/classes_add{{ obj.id }}/">添加</a>
            <a href="/classes_edit{{ obj.id }}/">编辑</a>
        </li>
    {% endfor %}
</ul>
</div>
</body>
</html>
View Code

添加班级

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>添加班级</title>
</head>
<body>
<h1>添加班级</h1>
<form action='/classes_add{{2}}/' method="post" novalidate>
    {% csrf_token %}
  <p>班级添加:{{ obj.title}} {{ obj.errors.title.0}}</p>
   <p><input type="submit" value="提交"></p>
</form>

</body>
</html>
View Code

编辑班级

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>编辑班级</title>
</head>
<body>
<form action="/classes_edit{{nid}}/" method="post">
    {% csrf_token %}
   <p>编辑班级:{{ obj.title }}{{obj.title.errors.0}} </p>
    <input type="submit" value="提交">
</form>
</body>
</html>
View Code

学生管理1对多

学生显示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>学生管理</title>
</head>
<body>
<h1>学生管理</h1>
<ul>
    {% for row in student %}
        <li>{{row.name}}
            {{row.sex}}
            <a href="/students_add{{row.id}}/">添加</a>
            <a href="/students.edit{{row.id}}/">编辑</a>
        </li>
    {% endfor %}
</ul>
</body>
</html>
View Code

学生添加

<!DOCTYPE html>
<html lang="en">
<head>
    <h1>添加学生</h1>
    <meta charset="UTF-8">
    <title>添加学生</title>
</head>
<body>
<form action="/students_add{{ nid}}/" method="post" novalidate>
     {% csrf_token%}
<p>姓名:{{ obj.name}}{{obj.name.errors.0}}</p>
<p>性别:{{ obj.sex}}{{obj.sex.errors.0}} </p>
<p>班级:{{ obj.cls_id }}{{ obj.errors.0}}  </p>
<p><input type="submit" value="提交"></p>
 </form>
</body>
</html>
View Code

学生编辑

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>老师编辑</title>
</head>
<body>
<form action="/teacher_edit{{ nid }}/" method="post">
    {% csrf_token %}
<p>老师姓名:{{obj.tname }}</p>
<p>班级:{{ obj.classes }}</p>
<input type="submit" value="提交">
</form>
</body>
</html>
View Code

 老师管理多对多

老师显示

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>老师列表</title>
</head>
<body>
<table border="1">
<thead>
<tr><td>ID</td><td>老师</td><td>任教班级</td><td colspan="3">操作</td></tr>
</thead>
<tbody>
     {% for teacher in tlist %}
    <tr>
        <td>{{ teacher.id }}</td>
        <td>{{teacher.tname}}</td>
        <td>
            {% for row in teacher.c2t.values %}
            {{ row.title }}
            {% endfor %}
         </td>
        <td><a href="/teacher_add/">添加</a></td>
        <td><a href="/teacher_edit{{ teacher.id}}/">编辑</a></td>
    </tr>
    {% endfor %}
</tbody>
</table>



</body>
</html>
View Code

老师添加

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>添加老师</title>
</head>
<body>
<form action="/teacher_add/" method="post" novalidate>
{% csrf_token %}
<p>老师姓名:{{ obj.tname }}{{obj.tname.errors.0}}</p>
<p>任教班级:{{ obj.classes }}{{obj.classes.errors.0}}</p>
<p><input type="submit" value="提交"></p>
 </form>
</body>
</html>
View Code

老师编辑

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>老师编辑</title>
</head>
<body>
<form action="/teacher_edit{{ nid }}/" method="post">
    {% csrf_token %}
<p>老师姓名:{{obj.tname }}</p>
<p>班级:{{ obj.classes }}</p>
<input type="submit" value="提交">
</form>
</body>
</html>
View Code

 七、ModelForm

使用Django开发web程序阶段回顾:

1.手动对单表进行增、删、该、查,手动把ORM操作获取的数据渲染到模板;(阶段1)

2.Form组件(类),自动生成标签(input、select),并对用户输入的数据做规则验证;(阶段2)

3.ModelForm顾名思义就Form和Django的Model数据库模型结合体,可以简单、方便得对数据库进行增加、编辑操作和验证标签的生成

Form组件和ModelForm的区别

ModelForm是Django Model.py和Form组件的结合体,可以简单/快速使用 Form验证和数据库操作功能,但不如Form组件灵活,如果在使用Django做web开发过程中验证的数据和数据库字段相关(可以对表进行增、删、改操,注意 Many to many字段,也可以级联操作第3张关系表;),建议优先使用ModelForm,用起来更方便些,但是在使用ModelForm的时候慎用fields='__all__',获取数据库所有字段势必造成性能损耗;

使用ModelForm

前端:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
{{ form_obj.as_p }}
{#<p>姓名:{{form_obj.name  }}</p>#}
</body>
</html>
View Code

后端视图:

from app02 import models
from django.forms import ModelForm
class UserModalForm(ModelForm):
    class Meta:
        model=models.UserInfo #(该字段必须为 model  数据库中表)
        fields= '__all__'   #(该字段必须为 fields 数据库中表)

def add(request):
     # 实例化models_form
    if request.method=='GET':
        obj = UserModalForm()
        return render(request,'rbac/user_add.html',locals())
    else:
        obj=UserModalForm(request.POST)
        if obj.is_valid():
            data=obj.cleaned_data
            obj.save()  #form验证通过直接 添加用户信息到数据库
        return render(request, 'rbac/user_add.html', locals())
View Code

 model.py

使用使用Form组件和ModelForm注意事项:

1、model.py一点要写verbose_name='名称'参数,才会在前端显示P标签的标题;

from django.db import models

class Department(models.Model):
    title=models.CharField(max_length=32,verbose_name='部门名称')
    def __str__(self):
        return self.title

class UserInfo(models.Model):
    name=models.CharField(max_length=32,verbose_name='姓名')
    emai=models.EmailField(max_length=32,verbose_name='邮箱')
    pary=models.ForeignKey(Department,verbose_name='部门')
View Code

如果使用Form组件在前端显示标题,可以设置Form类中的lable参数

class UserInfoForm(Form):
    name=fields.CharField(required=True,error_messages={'reqired':'用户名不能为空'},label='姓名')
    email=fields.EmailField(required=True,error_messages={'reqired':'用户名不能为空'},label='邮箱')
    pary=fields.ChoiceField(choices=[(1,"技术部"),(2,'销售部'),(3,'市场部'),],label='部门')
View Code

2、pary=fields.ChoiceField(choices=models.Department.objects.values_list('pk','title'),label='部门'),前端select标签不能随数据库操作实时更新;

在Department表添加一条数据之后,前端select标签中的数据不能随数据库实时更新;

原因:

不管是ModelForm还是Form组件本质就是个类,fields(字段)本质就是类中的1个静态属性,在类第一次加载时赋值,永远不会更新;

 

解决方案1(手动档):

重写__init__方法,每次实例化对象,就去获取一次数据库内容,重新赋值;

class UserInfoForm(Form):
    name=fields.CharField(required=True,error_messages={'reqired':'用户名不能为空'},label='姓名')
    email=fields.EmailField(required=True,error_messages={'reqired':'用户名不能为空'},label='邮箱')
    pary=fields.ChoiceField(label='部门')
    # pary=fields.ChoiceField(choices=models.Department.objects.values_list('pk','title'),label='部门')
    def __init__(self,*args,**kwargs):
        super(UserInfoForm,self).__init__(*args,**kwargs)
        self.fields['pary'].choices=models.Department.objects.values_list('pk','title')
View Code

解决方案2(自动档)

导入ModelChoiceField 模块  from django.forms.models import ModelChoiceField 

使用ModelChoiceField 字段

from django.forms import fields
from django.forms import widgets
from django.forms import ModelForm
from django.forms.models import ModelChoiceField

class UserInfoForm(Form):
    name=fields.CharField(required=True,error_messages={'reqired':'用户名不能为空'},label='姓名')
    email=fields.EmailField(required=True,error_messages={'reqired':'用户名不能为空'},label='邮箱')
    #方案1
    pary=ModelChoiceField(queryset=models.Department.objects.all(),label='部门')
View Code

 

自动挡虽好,但是也有缺陷;前端option标签的内容,需要借助model.py中的__str__方法,生成;

八、扩展 widgets之富文本编辑框

 如果你的web应用涉及到了发表文章、表情评论就必须使用富文本编辑框,小编调研了两款 Kindeditor、CKeditor;

Kindeditor

1.Kindeditor简介

http://kindeditor.net/docs/usage.html

2.下载Kindeditor编辑器

http://www.kindsoft.net/down.php

3.引入Kindeditor编辑器

<script src="/static/kindeditor-4.1.10/kindeditor-all.js"></script>

4.使用Kindeditor编辑器

a.创建textarea 标签

<textarea name="content" id="a" cols="30" rows="10"></textarea>

b.初始化Kindeditor

<script>
  var edit=KindEditor.create('#a',{
        width:'700px',
        height:'500px',
        uploadJson:'/upload_file/',
        extraFileUploadParams:{
            'csrfmiddlewaretoken':'{{ csrf_token }}',}})
</script>

c. Kindeditor功能定制

<script>
        var edit = KindEditor.create('#a', {
            items: [ //填写自己想要的功能
                'fontname', 'fontsize', '|', 'forecolor', 'hilitecolor', 'bold', 'italic', 'underline',
                'removeformat', '|', 'justifyleft', 'justifycenter', 'justifyright', 'insertorderedlist',
                'insertunorderedlist', '|', 'emoticons', 'image', 'link'],
            width: '700px',
            height: '500px',
            uploadJson: '/upload_file/',
            extraFileUploadParams: {
                'csrfmiddlewaretoken': '{{ csrf_token }}',
            }
        })
    </script>

d.kindeditor的API

// 取得HTML内容
html = editor.html();

// 同步数据后可以直接取得textarea的value
editor.sync();
html = document.getElementById('editor_id').value; // 原生API
html = K('#editor_id').val(); // KindEditor Node API
html = $('#editor_id').val(); // jQuery

// 设置HTML内容
editor.html('HTML内容');

CKeditor结合Django Form

1.下载  django-ckeditor 和pillow

pip install django-ckeditor 
pip install pillow

2.setings

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rbac.apps.RbacConfig',
    'arya.apps.AryaConfig',
    'cmdb.apps.CmdbConfig',
    'rest_framework',
    'ckeditor',
    # 'ckeditor_uploader'
]
CKEDITOR_CONFIGS = {
    'default': {
        'toolbar': (
            # ['div','Source','-','Save','NewPage','Preview','-','Templates'],
            # ['Cut','Copy','Paste','PasteText','PasteFromWord','-','Print','SpellChecker','Scayt'],
            # ['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'],
            # ['Form','Checkbox','Radio','TextField','Textarea','Select','Button', 'ImageButton','HiddenField'],
            # ['Bold','Italic','Underline','Strike','-','Subscript','Superscript'],
            # ['NumberedList','BulletedList','-','Outdent','Indent','Blockquote'],
            # ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'],
            # ['Link','Unlink','Anchor'],
            ['Smiley',],

            # ['Styles','Format','Font','FontSize'],
            # ['TextColor','BGColor'],
            # ['Maximize','ShowBlocks','-','About', 'pbckcode'],

        ),
    }
}
功能定制

3. 收集Django 项目所有 静态文件  到的目录static目录   /   直接下载ckeditor到项目static

python  manage.py collectstatic

#设置媒体文件的上传的路径
MEDIA_URL="/media/"
#MEDIA_ROOT=os.path.join(BASE_DIR,"media")
MEDIA_ROOT='/tmp/cmdb_media/'



STATIC_ROOT = os.path.join(BASE_DIR, 'static')
设置media路径+static路径

注意:此步骤如果提示 需要你设置MEDIA_URL目录 和STATIC_ROOT 就先暂且设置一下,最终目的是把CKeditor的js插件copy到你的静态文件存放目录下;

4.后端生成 textarea +加前端 ckedit 插件生成 富文本编辑

from ckeditor.widgets import CKEditorWidget
class comment(Form): #评论框
    content =fields.CharField(widget=CKEditorWidget)
 {{ obj.content|safe }}

<script src="/static/pligin/ckeditor/ckeditor-init.js"></script>
<script src="/static/pligin/ckeditor/ckeditor/ckeditor.js"></script>
View Code

5.CKEditorWidget API

  <script>
        $('.root_reply_btn').click(function () {
            var $reply_user = $(this).attr('name');
            var $pid = $(this).attr('pid');
            $('#comment_submit').attr('pid', $pid);
            CKEDITOR.instances.id_content.insertText('回复' + $reply_user + ':');
            CKEDITOR.instances.id_content.insertHtml('<br>');
            CKEDITOR.instances.id_content.focus(); #使CKEditorWidget进入编辑状态 (MD在后台测试了半天!!)


        });
CKEDITOR.instances.id_content.document.$.body.firstChild.textContent 获取p标签的文本内容
</script>

6.完成效果

博客链接:http://www.cnblogs.com/wupeiqi/articles/6144178.html

http://www.cnblogs.com/yuanchenqi/articles/7439088.html#3770465

DjangoForm补充:http://www.cnblogs.com/yuanchenqi/articles/7487059.html

猜你喜欢

转载自www.cnblogs.com/chongdongxiaoyu/p/9197362.html