Django之ModelForm

一顆桃子t發表於2019-07-24

Django之ModelForm

 

 

簡介

Model + Form ==> ModelForm。model和form的結合體,所以有以下功能:

  • 驗證
  • 資料庫操作

Form回顧

models.py

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

class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    email = models.EmailField()
    user_type = models.ForeignKey(to='UserType',to_field='id')

forms.py

from django import forms
from django.forms import fields

class UserInfoForm(forms.Form):
    # username = models.CharField(max_length=32)    <-- models
    username = fields.CharField(max_length=32)
    # email = models.EmailField()    <-- models
    email = fields.EmailField()
    # user_type = models.ForeignKey(to='UserType',to_field='id')    <-- models
    user_type = fields.ChoiceField(
        choices=models.UserType.objects.values_list('id','caption')
    )

    # 下面的操作是讓資料在網頁上實時更新。
    def __init__(self, *args, **kwargs):
        super(UserInfoForm,self).__init__(*args, **kwargs)
        self.fields['user_type'].choices = models.UserType.objects.values_list('id','caption')

index.html

<body>
    <form action="/index/" method="POST" novalidate="novalidate">
        {% csrf_token %}
        {{ obj.as_p }}
        <input type="submit" value="提交">
    </form>
</body>

  從上面的小例子能看出,models的欄位和forms的欄位大部分都是重複的,所以,django給我們提供了一種更為簡潔的ModelFrom

ModelForm的用法

forms.py

class UserInfoModelForm(forms.ModelForm):

    class Meta:
        model = models.UserInfo    # 與models建立了依賴關係
        fields = "__all__"

views.py

def index(request):
    if request.method == "GET":
        obj = UserInfoModelForm()
        return render(request,"index.html",{'obj':obj})
    elif request.method == "POST":
        obj = UserInfoModelForm(request.POST)
        print(obj.is_valid())  # 這是方法,別忘記了加括號
        print(obj.cleaned_data)
        print(obj.errors)
        return render(request,"index.html",{'obj':obj})

index.html

<body>
    <form action="/index/" method="POST" novalidate="novalidate">
        {% csrf_token %}
        {{ obj.as_p }}
        <input type="submit" value="提交">
    </form>
</body>

ModelForm常見引數

自定義欄位名(html顯示的欄位)

如何定義http上定義的欄位呢,自定義寫成中文的?之前的用法是在Form裡寫上label。Model Form定義要用verbose_name

指定顯示那些欄位

class UserInfo(models.Model):
    username = models.CharField(max_length=32, verbose_name='使用者')
    email = models.EmailField(verbose_name='郵箱')
    user_type = models.ForeignKey(to='UserType',to_field='id', verbose_name='型別')

如果不在model裡定義,在modelForm裡實現,利用labels

class UserInfoModelForm(forms.ModelForm):

    class Meta:
        model = models.UserInfo
        fields = "__all__"
        labels = {
            'username':'使用者名稱',
            'email':'郵箱',
        }

指定需要展示的欄位

fields = "__all__"上面展示所有的,也可以展示指定的列

 fields = ['username','email']   # 顯示指定列
 exclude = ['username']          # 排除指定列

錯誤資訊

 error_messages = {
            '__all__':{    # 整體錯誤資訊

            },
            'email': {
                'required': '郵箱不能為空',
                'invalid': '郵箱格式錯誤..',
            }
        }

給欄位新增css屬性

widgets = {
            'username': Fwidgets.Textarea(attrs={'class': 'c1'})
        }

ModelForm的驗證

跟form一樣,ModleForm裡面也有is_validcleaned_dataerrors

# Form驗證:
    UserInfoForm -> Form -> BaseForm( 包含is_valid等方法)

# ModelForm驗證:
    UserInfoModelForm -> ModelForm -> BaseModelForm -> BaseForm

ModelForm對資料庫操作

新增資料

如果資料驗證通過,直接呼叫save()方法,django會自動往資料庫裡新增一條資料(會根據modles裡的欄位一一對應)

if obj.is_valid():
     obj.save()      # 建立資料

如果在如下一對多、多對多關係中,如:

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

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

class UserInfo(models.Model):
    username = models.CharField(max_length=32)
    email = models.EmailField()
    user_type = models.ForeignKey(to='UserType',to_field='id')
    u2g = models.ManyToManyField(UserGroup)

這樣的話,執行上面的obj.save()會自動在UserInfo表和多對多關係表裡都增加資料,灰常灰常方便。

def index(request):
    if request.method == "GET":
        obj = UserInfoModelForm()
        return render(request,'index.html',{'obj': obj})
    elif request.method == "POST":
        obj = UserInfoModelForm(request.POST)
        if obj.is_valid():
            obj.save()  # 等價以下三句
            # instance = obj.save(False)
            # instance.save()
            # obj.save_m2m()
        return render(request,'index.html',{'obj': obj})

修改資料

修改表資料是,記得把instance資訊也傳進去,如:mf = UserInfoModelForm(request.POST,instance=user_obj)

不然是新建資料,而不是對某行資料進行修改。

編輯使用者資訊,新url方式保留預設資料

urls.py

url(r'^user_list/', views.user_list),
url(r'^edit-(\d+)/', views.user_edit),

views.py

def user_list(request):
    li = models.UserInfo.objects.all().select_related('user_type')  # 這裡只能是外來鍵,多對多欄位也不可以
    return render(request,'user_list.html',{'li': li})

def user_edit(request, nid):
    # 獲取當前id物件的使用者資訊
    # 顯示使用者已經存在資料
    if request.method == "GET":
        user_obj = models.UserInfo.objects.filter(id=nid).first()
        mf = UserInfoModelForm(instance=user_obj)   # 把預設資料傳遞進去
        return render(request,'user_edit.html',{'mf': mf, 'nid': nid})
    elif request.method == 'POST':
        # 資料修改的資訊,給資料庫的哪一行做修改?
        user_obj = models.UserInfo.objects.filter(id=nid).first()
        mf = UserInfoModelForm(request.POST,instance=user_obj)  # 指定給誰做修改
        if mf.is_valid():
            mf.save()
        else:
            print(mf.errors.as_json())
        return render(request,'user_edit.html',{'mf': mf, 'nid': nid})

user_list.html

<body>
    <ul>
        {% for row in li %}
            <li>{{ row.username }} - {{ row.user_type.caption }} - <a href="/edit-{{ row.id }}/">編輯</a></li>
        {% endfor %}
    </ul>
</body>

user_edit.html

<body>
    <form method="POST" action="/edit-{{ nid }}/">
        {% csrf_token %}
    {{ mf.as_p }}
        <input type="submit" value="提交" />
    </form>
</body>

  

 
 
 
 
 
 

相關文章