Day23-Model操做,Form操做和序列化操做

參考源出處:http://blog.csdn.net/fgf00/article/details/54629502html

1. 搭建環境請參考:http://www.cnblogs.com/momo8238/p/7508677.html 前端

2、Form操做

通常會建立forms.py文件,單獨存放form模塊。jquery

Form 專門作數據驗證,並且很是強大。有如下兩個插件:web

1-fields :驗證(確定會用的)ajax

2-widgets:生成HTML(有時候用,有時候能夠不用)正則表達式

  2.1通常新url方式操做用widgets,由於生成url不是關鍵的,能夠保留上一次提交的數據數據庫

  2.2Ajax請求的時候,能夠不用它生成htmldjango

1.一、Form操做動態Select數據

urls.pyjson

url(r'^index/$', views.index),

 views.py瀏覽器

 

def index(request):
    from cmdb.forms import UserInfoForm
    obj = UserInfoForm()
    return render(request, 'index.html', {'obj':obj})

forms.py

from django import forms
from django.forms import widgets, fields
from cmdb import models

class UserInfoForm(forms.Form):
    user = fields.CharField(
        required=False,
        widget=widgets.Textarea(attrs={'class':'c1'})
    )
    pwd = fields.CharField(
        max_length=12,
        widget=widgets.PasswordInput(attrs={'class':'c1'})
    )
    user_type = fields.ChoiceField(
        # choices=[(1,'普通用戶'),(2,'超級用戶')],  # 手動填寫
        choices=models.UserType.objects.values_list('id','name'),  # 數據庫中獲取
        widget=widgets.Select
    )

 models.py

 

class UserType(models.Model):
    name = models.CharField(max_length=32)

index.html

<body>
    <p>{{ obj.user }}</p>
    <p>{{ obj.pwd }}</p>
    <p>{{ obj.user_type }}</p>
    <p>{{ obj.user_type2 }}</p>
</body>

 

 

上面有個問題,就是數據庫添加數據後,django須要重啓網頁才能看到新加的數據。why?

forms裏面,定義的類,類裏的user、pwd、user_type都是靜態字段(類變量),這些都是屬於類的。

__init__裏面寫的屬於對象。

而啓動Django的時候,類變量一次性都加在到內存了。

上面views.py裏的obj = UserInfoForm()生成了一個對象,會執行類裏的__init__方法,會把類裏的字段封裝到obj.fields裏面(裏面有user、pwd、user_type三個字段),因此:

views.py

def index(request):

    from cmdb.forms import UserInfoForm
    from cmdb import models

    obj = UserInfoForm()
    obj.fields['user_type'].choices = models.UserType.objects.values_list('id','name')

    return render(request, 'index.html', {'obj':obj})

 

這樣,數據庫添加數據,網頁上就能夠實時更新了。可是若是有不少choices的話,也會寫不少,也很差。

因此不在views.py裏作修改,能夠在forms.py裏類的__init__構造方法裏寫

forms.py

 

 

from django import forms
from django.forms import widgets, fields
from cmdb import models

class UserInfoForm(forms.Form):
    user = fields.CharField(
        required=False,
        widget=widgets.Textarea(attrs={'class':'c1'})
    )
    pwd = fields.CharField(
        max_length=12,
        widget=widgets.PasswordInput(attrs={'class':'c1'})
    )
    user_type = fields.ChoiceField(
        # choices=[(1,'普通用戶'),(2,'超級用戶')],  # 手動填寫
        choices=[],  # 構造方法裏取,這裏只定義就能夠了,不須要在獲取一次。
        widget=widgets.Select
    )

    #### 另一種寫法 方式 ####
    user_type2 = fields.ChoiceField(
        widget=widgets.Select(choices=[])
    )

    def __init__(self, *args, **kwargs):
        super(UserInfoForm,self).__init__(*args, **kwargs)

        self.fields['user_type'].choices = models.UserType.objects.values_list('id','name') 
        #### 另一種寫法 方式 ####
        self.fields['user_type2'].widget.choices = models.UserType.objects.values_list('id','name')

 

 

1.二、Form操做動態Select數據

上面1.1裏是本身獲取實現的,Django也能夠自動實現,只是實現的不太好。

django 裏自動實現的,看user_type3

from django import forms
from django.forms import widgets, fields
from django.forms.models import ModelChoiceField
from cmdb import models

class UserInfoForm(forms.Form):
    user = fields.CharField(
        required=False,
        widget=widgets.Textarea(attrs={'class':'c1'})
    )
    pwd = fields.CharField(
        max_length=12,
        widget=widgets.PasswordInput(attrs={'class':'c1'})
    )
    user_type = fields.ChoiceField(
        choices=[],
        widget=widgets.Select
    )
    user_type2 = fields.ChoiceField(
        widget=widgets.Select(choices=[])
    )
    user_type3 = ModelChoiceField(
        queryset=models.UserType.objects.all()
    )

    def __init__(self, *args, **kwargs):
        super(UserInfoForm,self).__init__(*args, **kwargs)

        self.fields['user_type'].choices = models.UserType.objects.values_list('id','name')
        self.fields['user_type2'].widget.choices = models.UserType.objects.values_list('id','name')

 使用Django自動提供的這個方法,有一點很差的就是:須要本身在models里加上__str__方法

 

 

class UserType(models.Model):
    name = models.CharField(max_length=32)

    def __str__(self):
        return self.name

 

ModelChoiceField 參數

ModelChoiceField(ChoiceField)
    ...                        django.forms.models.ModelChoiceField
    queryset,                  # 查詢數據庫中的數據
    empty_label="---------",   # 默認空顯示內容
    to_field_name=None,        # HTML中value的值對應的字段(html源碼中value不一樣)
    limit_choices_to=None      # ModelForm中對queryset二次篩選

 

 

ModelChoiceField 是單選的,多選的是:ModelMultipleChoiceField

 

二、Form內置鉤子(數據驗證)

 

初始化操做

 

加一個字典,生成顯示默認值

 

views.py

def index(request):
    from cmdb.forms import UserInfoForm
    obj = UserInfoForm({'user':'fgf','user_type':'2'})  # 默認值
    return render(request, 'index.html', {'obj':obj})

 

數據驗證

views.py

def index(request):

    from cmdb.forms import UserInfoForm

    if request.method == 'GET':
        obj = UserInfoForm({'user':'fgf','user_type':'2'})
        return render(request, 'index.html', {'obj':obj})
    elif request.method == 'POST':
        obj = UserInfoForm(request.POST, request.FILES)  # request.FILES:接收文件
        obj.is_valid()  # 數據驗證

 

正則表達式驗證成功了,可是若是用戶名重複了,也是不讓執行的,如何自定製呢?

註冊數據驗證示例

forms.py

from django.core.exceptions import ValidationError
class RegisterForm(forms.Form):
    user = fields.CharField
    email = fields.EmailField

    def clean_user(self):  # 對user作一個單獨的驗證
        c = models.UserType.objects.filter(name=self.cleaned_data['user'])
        if c :  # 若是用戶名已經存在,提示報錯信息
            raise ValidationError('用戶名已經存在',code='xxx')
        else:  # 不存在,驗證經過
            return self.cleaned_data['user']
    def clean_email(self):  # 對email作單獨的驗證
        # 驗證必需要有返回值
        return self.cleaned_data['email']

# 驗證順序:
    # 先user字段,在clean_user方法
    # 再email字段,在clean_email方法

 

登陸數據驗證示例

forms.py

class LoginForm(forms.Form):
    user = fields.CharField
    # 這裏也能夠validators=[],自定義正則數據驗證
    email = fields.EmailField(validators=[])

    def clean_user(self):
        pass
    def clean_pwd(self):
        pass
    # 當用戶名和密碼驗證總體錯誤,報錯給下面的方法
    def clean(self):
        c = models.User.objects.filter(name=self.cleaned_data.get('user'),pwd=self.cleaned_data.get('pwd'))
        if c:
            return self.cleaned_data
        else:
            raise ValidationError("用戶名或密碼錯誤")

    # 自定義一些其餘驗證操做
    def _post_clean(self):
        pass

 

驗證順序

這麼多鉤子,這麼強大的數據驗證,驗證順序是:

form循環,

  • 先第一個字段正則表達式判斷,執行字段鉤子函數;
  • 第二個字段正則,第二個的鉤子;
  • 全部字段完成後,執行clean鉤子;
  • clean執行完後,執行_post_clean鉤子

怎麼去記:經過看源碼去找:

先找is_valid,找到errors,找到full_clean,全部的鉤子基於full_clean執行的

驗證完成後

views.py(僞代碼)

 1 def register(request):
 2     from cmdb.forms import RegisterForm
 3     from django.core.exceptions import NON_FIELD_ERRORS
 4     obj = RegisterForm(request.POST)
 5     if obj.is_valid():
 6         obj.cleand_data
 7 
 8     else:
 9         obj.errors
10         {
11             'user':[{'code':'required','message':'xxxx'}],
12             'pwd':[{'code':'required','message':'xxxx'}],
13             # 上面每一個字段的錯誤信息放在裏面,那clean總的錯誤信息放在哪裏?
14             '__all__':[],  # 特殊的總體錯誤信息,放在這裏
15             # NON_FIELD_ERRORS:[], 和 __all__ 一個意思。
16         }

 

三、Form內置序列化錯誤信息

無論提交數據是瀏覽器提交、仍是curl方式,仍是ajax提交,只要是request.POST均可以作驗證。

Ajax 提交數據,進行驗證

urls.py

1 url(r'^login.html$', views.login),

views.py

 

 1 from django import forms
 2 from django.forms import widgets, fields
 3 class LoginForm(forms.Form):
 4     username = fields.CharField()
 5     password = fields.CharField(
 6         max_length=64,
 7         min_length=12
 8     )
 9 
10 def login(request):
11     import json
12     res = {'status':True, 'error':None, 'data': None}
13     if request.method == "GET":
14         return render(request,"login.html")
15     elif request.method == "POST":
16         obj = LoginForm(request.POST)
17         if obj.is_valid():
18             print(obj.cleand_data)
19         else:
20             # print(obj.errors, type(obj.errors))
21             res['error'] = obj.errors.as_json()  # 轉爲json格式
22             return HttpResponse(json.dumps(res))

login.html

 1 <body>
 2     <form id="fm">
 3         {% csrf_token %}
 4         <p><input type="text" name="username" /></p>
 5         <p><input type="password" name="password" /></p>
 6         <a id="submit">ajax提交</a>
 7     </form>
 8     <script src="/static/jquery-1.12.4.js"></script>
 9     <script>
10         // 頁面框架加載完自動執行
11         $('#submit').click(function(){
12             $.ajax({
13                 url:'/login.html',
14                 type:'POST',
15                 data:$('#fm').serialize(),
16                 success:function(arg){
17                     console.log(arg);
18                     arg = JSON.parse(arg);  // 轉爲字典
19                     console.log(arg);
20                 },
21                 error: function(){
22 
23                 }
24             })
25         })
26     </script>
27 </body>

 

上面的方式能夠實現,只是前端須要反解兩次,不太好,下面優化一下。

  • as_json : 生成json格式
  • as_data : 生成dict數據。

views.py

 1 from django import forms
 2 from django.forms import widgets, fields
 3 class LoginForm(forms.Form):
 4     username = fields.CharField()
 5     password = fields.CharField(
 6         max_length=64,
 7         min_length=12
 8     )
 9 
10 # 序列化,轉換爲指定數據類型
11 import json
12 from django.core.exceptions import ValidationError
13 class JsonCustomEncoder(json.JSONEncoder):
14     def default(self, field):
15         if isinstance(field, ValidationError):
16             return {'code':field.code, 'messages': field.messages}
17         else:
18             return json.JSONEncoder.default(self, field)
19 
20 def login(request):
21     res = {'status':True, 'error':None, 'data': None}
22     if request.method == "GET":
23         return render(request,"login.html")
24     elif request.method == "POST":
25         obj = LoginForm(request.POST)
26         if obj.is_valid():
27             print(obj.cleand_data)
28         else:
29             # print(obj.errors, type(obj.errors))
30             # res['error'] = obj.errors.as_json()  # 轉爲json格式
31             print(type(obj.errors.as_data()))
32             for k,v in obj.errors.as_data().items():
33                 print(k,v)  # 這裏v是ValidationError類型,不能序列化
34             res['error'] = obj.errors.as_data()
35         result = json.dumps(res, cls=JsonCustomEncoder)
36         return HttpResponse(json.dumps(result))

 

四、Django 序列化操做

Json.dumps用來作序列化,可是隻能序列化一些簡單的基本數據類型。對於沒法序列化的數據類型,只能自定製了。

ErrorDict自定義JSONEncoder

因爲json.dumps時沒法處理datetime日期,因此能夠經過自定義處理器來作擴展,如:

 1 import json 
 2 from datetime import date 
 3 from datetime import datetime 
 4 
 5 class JsonCustomEncoder(json.JSONEncoder): 
 6 
 7     def default(self, field): 
 8 
 9         if isinstance(field, datetime): 
10             return o.strftime('%Y-%m-%d %H:%M:%S') 
11         elif isinstance(field, date): 
12             return o.strftime('%Y-%m-%d')   # 轉成字符串類型
13         else: 
14             return json.JSONEncoder.default(self, field) 
15 
16 v = {'k':'123', "k1":datetime.datetime.now()}   
17 ds = json.dumps(v, cls=JsonCustomEncoder) 

 

QuerySet 第一種序列化方式

上面本身寫實現也能夠,不過Django提供了方法作序列化

1 from django.core import serializers
2 
3 v = models.tb.objects.all()
4 data = serializers.serialize("json", v)

 

QuerySet 第二種序列化方式

 

 1 import json 
 2 from datetime import date 
 3 from datetime import datetime 
 4 
 5 class JsonCustomEncoder(json.JSONEncoder): 
 6 
 7     def default(self, field): 
 8 
 9         if isinstance(field, datetime): 
10             return field.strftime('%Y-%m-%d %H:%M:%S') 
11         elif isinstance(field, date): 
12             return field.strftime('%Y-%m-%d') 
13         else: 
14             return json.JSONEncoder.default(self, field) 
15 
16 v = models.tb.objects.values('id','name','ctime')
17 v = list(v)  # 把(相似列表的queryset類型)轉換爲列表
18 v = json.dumps(v,cls=JsonCustomEncoder)  # 這裏cls只對ctime操做。
19 # 若是沒有ctime這個類型,只寫上邊的三句就能夠實現

注:form裏__all__總體的錯誤信息,前端展現以下:{{ obj.non_field_errors }}

相關文章
相關標籤/搜索