Django組件-forms組件 form組件和 ModelForm 和auth模塊 和中介模型

 form組件

form組件出現的緣由

  1. 當咱們用普通的form表單提交時會刷新頁面,若是這個咱們表單中的某項填錯了,刷新後咱們正確的選項也沒有了.
  2. 傳統的form表單須要咱們本身親自校驗每一項.工做量太大

Django 中form組件的2大功能:
       1 驗證(在前端頁面顯示咱們定義好的錯誤信息)
       2 保留用戶上次輸入的信息javascript

form組件生成form表單的幾種方法:

不要忘記,表單的輸出 包含submit 標籤,和表單的<form> 按鈕。 你必須本身提供它們。html

方法1前端

對於<input>/<label> 對,還有幾個輸出選項:java

  • {{ form.as_table }} 以表格的形式將它們渲染在<tr> 標籤中
  • {{ form.as_p }} 將它們渲染在<p> 標籤中
  • {{ form.as_ul }} 將它們渲染在<li> 標籤中

注意,你必須本身提供<ul> 或<table> 元素。python

views.py算法

from django.shortcuts import render
from django import forms
# Create your views here.
class RegisterFrom(forms.Form):  #定義一個類
    user=forms.CharField()   #和咱們input表單一一對應
    pwd=forms.CharField()

def register(request):
    if request.method=="POST":
        pass
    form_obj=RegisterFrom() # 1 實例化對象  
    return render(request,"register.html",{"form_obj":form_obj})
#分析題目:
#1這步運行後到底幹了什麼 ,實例化了一個變量 self.fields={"user":"user規則","pwd":"pwd規則對象"}

html:數據庫

<form action="">
    {{ form_obj.as_p }}  #p的意思就是p標籤的意思
    <p><input type="submit" value="註冊"></p>
</form>

</body>
</html>

方法二:手動渲染任意標籤django

能夠本身寫任意標籤,可是方法1,只能用人家規定好的p標籤後端

 在上面的例子中,Django會根據{{ form.as_p }}自動生成表單,咱們也能夠本身來具體編寫表單的HTML;每一個字段都是表單的一個屬性api

除了HTML不一樣外其他的都相同

html

<form action="">
    <P><label for="">用戶名</label>
    {{ form_obj.user }}
    </P>
 <P><label for="">密碼 &nbsp;&nbsp;</label>
    {{ form_obj.pwd }}
    </P>
    <p><input type="submit" value="註冊"></p>
</form>

方法三:循環表單字段

 若是你爲你的表單使用相同的HTML,你可使用{% for %} 循環迭代每一個字段來減小重複的代碼

<div class="container">
    <div>
        <h3>數據展現頁面</h3>
        <hr>
        <a href="{{ add_url }}"><button class="btn btn_success ">添加按鈕</button></a>
        <table class="table table-striped">
            <thead>
            <tr>
{#            運用了form組件的第三種方法#}
            {% for header in header_list %}
            <th>{{ header }}</th> {# 僅僅是得到了一個字符串名字 #}
            {% endfor %}
            </tr>
            </thead>
            <tbody>
            {% for data in new_display %}{# data爲每條信息對象 #}
                <tr>
                {% for item in data %}
                    <td>{{ item }}</td> {# 每條對象信息的每一個屬性值 #}

                {% endfor %}
                </tr>

            {% endfor %}

            </tbody>
        </table>
    </div>
</div>

處理表單返回的數據

Form.is_valid() :Form對象的首要任務就是驗證數據。 對於綁定的Form實例,能夠調用is_valid()方法來執行驗證,該方法會返回一個表示數據是否合法的布爾值。

驗證成功

無論表單提交的是什麼數據,一旦經過is.valid() 的驗證,返回True,則,驗證後的表單數據將位於form.cleaned_data 字典中。 這些數據已經爲你轉換好爲Python 的類型。

cleaned_data:,驗證後的表單數據將位於form.cleaned_data 字典中

注意:此時你也能夠從request.post得到驗證成功的數據傳到數據庫(若是須要的話),可是咱們更期待你從from.class_data字典中來得到數據.

注意:若是你的數據沒有 經過驗證,cleaned_data 字典中只包含合法的字段

def register(request):
    if request.method=="POST":
        form=UserForm(request.POST)
        if formsis_valid():
            user=form.cleaned_data["username"]
            pwd=form.cleaned_data["password"]
obj=User.objects.create(name=username,password=pwd)
            return HttpResponse("註冊成功")

驗證失敗

 form.errors:驗證失敗後的表單數據將位於form.errors的字典

在這個字典中,鍵爲字段的名稱,值爲表示錯誤信息的Unicode 字符串組成的列表。 錯誤信息保存在列表中是由於字段可能有多個錯誤信息。

 訪問errors 屬性能夠得到錯誤信息的一個字典:

>>> f.errors
{'sender': ['Enter a valid email address.'], 'subject': ['This field is required.']}

Form.add_error(field, error)

 

該方法容許從Form.clean()方法中或從表單外部向特定字段添加錯誤;例如從一個角度。

field 參數爲字段的名稱。 若是值爲None,error 將做爲Form.non_field_errors() 返回的一個非字段錯誤。

error 參數能夠是一個簡單的字符串,或者最好是一個ValidationError 實例。 Raising ValidationError 中能夠看到定義表單錯誤時的最佳實踐。

注意,Form.add_error() 會自動刪除cleaned_data 中的相關字段。

print(form_obj.errors)

<ul class="errorlist"><li>user<ul class="errorlist">
<li>Ensure this value has at least 6 characters (it has 2).
<ul class="errorlist">
    <li>user
        <ul class="errorlist">
            <li>Ensure this value has at least 6 characters (it has 2).</li>
        </ul>
    </li>
</ul>

print(type(form_obj.errors))

<class 'django.forms.utils.ErrorDict'> 是一個特殊的字典類型
那咱們就用字典的方法取值: print(type(form_obj.errors["user"]))
<class 'django.forms.utils.ErrorList'>  #表名這是一個列表類型,能夠用列表方法取值
print(type(form_obj.errors["user"][0]))

class  str  表名這是一個字符串類型
print((form_obj.errors["user"][0]))
Ensure this value has at least 6 characters (it has 2).
print(type(form_obj.fields)
<class 'collections.OrderedDict'>

源碼解析:

def _clean_fields(self):
        for name, field in self.fields.items():   #name就是你在class中定義的對象(如user), filed 就是你每一個對象對象的字段規則, self.fileds就是 實例化對象對應的那個字典,item有序排列
            # value_from_datadict() gets the data from the data dictionaries.
            # Each widget type knows how to retrieve its own data, because some
            # widgets split data over several HTML fields.
            if field.disabled:
                value = self.get_initial_for_field(field, name)
            else:
                value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
            try:
                if isinstance(field, FileField):
                    initial = self.get_initial_for_field(field, name)
                    value = field.clean(value, initial)
                else:
                    value = field.clean(value)
                self.cleaned_data[name] = value
                if hasattr(self, 'clean_%s' % name):
                    value = getattr(self, 'clean_%s' % name)()
                    self.cleaned_data[name] = value
            except ValidationError as e:
                self.add_error(name, e)

 

</li></ul></li></ul>

整理一下,咱們獲得: user的錯誤信息

 局部鉤子

字段校驗有時候很不靈活,咱們只能校驗field的字段中有的規則,沒有的規則,咱們就不能在字段中校驗,好比說,咱們要校驗用戶名不能全爲數字,這時候你在字段中就校驗不出來了.這就是出現了局部鉤子的概念

局部鉤子:用來校驗某一個字段,本身額外添加的規則, 定義函數的名稱必須爲 def clean_字段(self),爲何寫的緣由在下邊源碼中已經解釋了

try:
  if hasattr(self, 'clean_%s' % name):
                    value = getattr(self, 'clean_%s' % name)()  #self表示當前對象
                    self.cleaned_data[name] = value #這個校驗的字段成功後放在了cleaned_data的字典中,你能夠從這個字典中得到對應的數據
except ValidationError as e:
    self.add_error(name, e) #若是校驗不經過,就把它添加到errors的字典中去

例子:

def clean_user(self):
        """
        局部鉤子
        驗證用戶名是否註冊過
        :return:
        """
        username=self.cleaned_data.get("user")#從已經符合字典中取出相應的字段,也就是說先判斷is_valid(),而後再校驗局部鉤子

        data1=models.UserInfo.objects.filter(username=username)
        if not data1:
            return username
        else:
            raise ValidationError("該用戶已經註冊")

全局鉤子

當校驗規則涉及到了多個字段時候,這時候就要用全局鉤子了.

def clean(self):
        """
        全局鉤子,驗證兩次密碼是否一致
        :return:
        """
        pwd=self.cleaned_data.get("password")
        repeat_pwd=self.cleaned_data.get("repeat_password")
        if pwd and repeat_pwd:    #判斷是否有值,由於只要前邊有錯誤,也會走全局鉤子
            if pwd == repeat_pwd:
                return self.cleaned_data
            else:
                raise ValidationError("密碼不一致")
        else:
            return self.cleaned_data

源碼解析,

全局鉤子走這個源碼: self._clean_form()

def _clean_form(self):
        try:
            cleaned_data = self.clean()
        except ValidationError as e:
            self.add_error(None, e)
        else:
            if cleaned_data is not None:
                self.cleaned_data = cleaned_data
def clean(self):
        """
        Hook for doing any extra form-wide cleaning after Field.clean() has been
        called on every field. Any ValidationError raised by this method will
        not be associated with a particular field; it will have a special-case
        association with the field named '__all__'.
        """
        return self.cleaned_data #這裏面什麼都沒幹,只是返回了cleaned_data中的數據,咱們就能夠擴展這個鉤子
Form. non_field_errors()

這個方法返回Form.errors 中不是與特定字段相關聯的錯誤。 它包含在Form.clean() 中引起的ValidationError 和使用Form.add_error(None, "...") 添加的錯誤。

注意在前端你要console.log(form.field.errors),你會發如今errors這個字典中會出現"__all__",這個字段.這個就是存放全局鉤子錯誤信息的字段

__all__:  在錯誤信息中全局鉤子對應的字段是__all__

 

modelForm

modelForm說白了就是結合了model和form的特性

models.py

class Book(models.Model):
    title=models.CharField(max_length=32)
    price=models.IntegerField(default=12)
    publishs=models.ForeignKey(to="Publish")
    authors=models.ManyToManyField(to="Author")
    def __str__(self):
        return self.title
class Publish(models.Model):
    name=models.CharField(max_length=32)
    email = models.CharField(max_length=32,default=111)
    def __str__(self):
        return self.name
class Author(models.Model):
    name=models.CharField(max_length=32)
    def __str__(self):
        return self.name

form組件的例子
form組件的例子

views.py 拿bookmodel來舉例子:

from django import forms
from django.shortcuts import render,HttpResponse,redirect
from .models import *
class BookForm(forms.Form):
    title=forms.CharField(max_length=8)
    price=forms.IntegerField()
    publish_list = Publish.objects.all().values_list("pk", "name")
    author_list = Author.objects.all().values_list("pk", "name")
    t=((1,"北京"),(2,"天津"))
    state=forms.ChoiceField(choices=publish_list)
    state1=forms.MultipleChoiceField(choices=author_list)

你會發如今model中定義了字段,可是又在form中定義了一遍,是否是感受到冗餘,ModelForm就是基於這個問題出現的.

像這樣:

from .models import *
from django.forms import ModelForm #導入ModelForm這個類
class BookModelForm(ModelForm):
    class Meta:
        model=Book
        # fields=["title","price"] #只展現"title"和"price"字段
        fields="__all__"     #展現全部的字段
        labels={         
            "title": "書名",
            "price":"價格",
            "publishs":"出版社",
            "authors":"做者"
        }#自定義字段在前端顯示的名稱
        error_messages={
            "title":{"required":"不能爲空"}
        }
form_obj=BookModelForm()#生成一個ModelForm類的對象

字段類型

生成的Form類中將具備和指定的模型字段對應的表單字段,順序爲fields 屬性中指定的順序。

每一個模型字段有一個對應的默認表單字段。具體字段詳情見中文文檔

  • QuerySet 表示成ChoiceField,它是一個django.forms.ModelChoiceField,其選項是模型的ForeignKey
  • QuerySet 表示成MultipleChoiceField,它是一個django.forms.ModelMultipleChoiceField,其選項是模型的ManyToManyField

 

  • 若是模型字段設置了blank=True,那麼表單字段的required字段會設置爲False值。 不然,required=True
  • 表單字段的verbose_name 設置爲模型字段的label,並將第一個字母大寫。
  • 表單字段的help_text 設置爲模型字段的help_text
  • 若是模型字段設置了choices,那麼表單字段的widget將會設置爲Select,選擇項從模型字段的choices而來。 選項一般會包含空選項,而且會默認選擇。若是字段是必選的,它會強制用戶選擇一個選項。 若是模型字段的default 且具備一個顯示的default 值,將不會包含空選項(初始將選擇blank=False值)。

選擇要展現的字段:

強烈創建選擇展現全部的字段

fields=["title","price"] #只展現"title"和"price"字段

(1)設置'__all__' 屬性爲特殊的值fields 以表示須要使用模型的全部字段.

(2)設置Meta 內聯的ModelForm 類的exclude 屬性爲一個要從表單中排除的字段的列表

lass PartialAuthorForm(ModelForm):
    class Meta:
        model = Author
        exclude = ['title']

save()

每一個ModelForm還具備一個save()方法。 這個方法根據表單綁定的數據建立並保存數據庫對象。

 ModelForm的子類能夠接受現有的模型實例做爲關鍵字參數instance;若是提供此功能,則save()將更新該實例

 若是沒有提供,save() 將建立模型的一個新實例:

這就是和form本質的區別

def add(request):
    if request.method=="GET":
        forms_obj=BookModelForm()
        return render(request,"add.html",locals())
    else:
        forms_obj=BookModelForm(request.POST)#建立一個對象
        if forms_obj.is_valid():
            forms_obj.save() #在modelform中直接一句話就等於取出數據添加到數據庫了操做了.
       #若是不用modelForm用form的話,要取出傳進來的參數,建立對象而後經過create添加到數據庫

            return redirect("/index/")
        else:
            return render(request, "add.html", locals())

def edit(request,id):
    edit_book = Book.objects.filter(pk=id).first()
    if request.method=="GET":
        forms_obj=BookModelForm(instance=edit_book)
        return render(request,"edit.html",locals())
    else:
        forms_obj= BookModelForm(data=request.POST,instance=edit_book)#更新對象
        if forms_obj:
            forms_obj.save()
            return redirect("/index/")

form和modelForm的區別

  1. 減小了代碼重複
  2. 提交和更新數據更簡潔

Django的用戶認證  auth模塊

若是你想用這個auth模塊: 大前提要用Django生成的auth_user 表,不能本身再建立user表了

 auth模塊是什麼?

auth模塊是Django提供的標準權限管理系統,能夠提供用戶身份認證, 用戶組和權限管理。

auth能夠和admin模塊配合使用, 快速創建網站的管理系統。

在INSTALLED_APPS中添加'django.contrib.auth'使用該APP, auth模塊默認啓用.

django.contrib.auth.middleware.AuthenticationMiddleware  這個是auth模塊的中間件

認證登陸

1 #導入auth模塊:
2 from django.contrib import auth

auth模塊提供了老多的方法在這裏咱們只介紹三種方法:

authenticate() 驗證用戶

提供了用戶認證,即驗證用戶名以及密碼是否正確,通常須要username  password兩個關鍵字參數

若是認證信息有效,會返回一個  User  對象。authenticate()會在User 對象上設置一個屬性標識那種認證後端認證了該用戶,且該信息在後面的登陸過程當中是須要的。當咱們試圖登錄一個從數據庫中直接取出來不通過authenticate()的User對象會報錯返回None!!


user = authenticate(username='someone',password='somepassword') #把從前端傳過來的用戶名和密碼在這裏進行驗證

 login(request,user) 給用戶綁定session

 該函數接受一個request的對象以及認證了的user對象

此函數利用Django的session框架給某個已認證的用戶加上session ID等信息.

from django.contrib.auth import authenticate,login #導入auth模塊下的authenticate類和login類

def my_view(request):
    username=request.POST.get("username")
    password=request.POST.get("password")
    user=authenticate(username=username,password=password)
    if user is not None:
        login(request,user) #把request,和user傳入login函數中進行session的寫入操做,至關於request.session["name"]=username
        return redirect("/index/")
    else:
        return redirect("login")

logout(request)註銷用戶 

from django.contrib.auth import logout
def logout_view(request):
    logout(request)#其實這裏至關於咱們寫的request.session.flush()
    return redirect("/login/")

該函數接受一個HttpRequest對象,無返回值。當調用該函數時,當前請求的session信息會所有清除。該用戶即便沒有登陸,使用該函數也不會報錯.

user對象的is_authenticated()

要求:

1  用戶登錄後才能訪問某些頁面,

2  若是用戶沒有登陸就訪問該頁面的話直接跳到登陸頁面

3  用戶在跳轉的登錄界面中完成登錄後,自動訪問跳轉到以前訪問的地址

方法1:

def my_view(request):
    if not request.user.is_authenticated():
        return redirect("%s?next=%s"%(settings.LOGIN_URL,request.path))##這句話不理解

方法2:login_required函數

django已經爲咱們設計好了一個用於此種狀況的裝飾器:login_requierd()

1
2
3
4
5
from  django.contrib.auth.decorators  import  login_required
     
@login_required
def  my_view(request):
   ...

若用戶沒有登陸,則會跳轉到django默認的 登陸URL '/accounts/login/ ' (這個值能夠在settings文件中經過LOGIN_URL進行修改)。並傳遞  當前訪問url的絕對路徑 (登錄成功後,會重定向到該路徑)。 

user對象

當你用了auth模塊後,request的會生成一個user屬性,裏邊封裝了你登陸了的對象,這樣你能夠在request.user.用戶的信息,若是你沒有登陸print(request.user)會打印出匿名用戶對象的英文單詞,

重點: 你能夠在全部的視圖函數和HTML頁面的中用request.user來獲取登陸人的相關信息

 

User 對象屬性:username, password(必填項)password用哈希算法保存到數據庫

is_staff : 用戶是否擁有網站的管理權限.

is_active : 是否容許用戶登陸, 設置爲``False``,能夠不用刪除用戶來禁止 用戶登陸

user對象的方法

is_authenticated()

若是是真正的 User 對象,返回值恆爲 True 。 用於檢查用戶是否已經經過了認證。
經過認證並不意味着用戶擁有任何權限,甚至也不檢查該用戶是否處於激活狀態,這只是代表用戶成功的經過了認證。 這個方法很重要, 在後臺用request.user.is_authenticated()判斷用戶是否已經登陸,若是true則能夠向前臺展現request.user.name.

def index(request):
    user=request.user
    if not user.is_authenticated():
        return redirect("/login/")
    return render(request,"index.html",local())

 

  註冊用戶create_user()

使用 create_user 註冊用戶:

from django.contrib.auth.models import User #這個就是Django本身建立的auth_user表,你要對他進行操做就須要用它建立的這個類User
user=User.objects.create_user(username="xiaohia",password="123456") #注意這裏不能用create()函數這樣也能註冊新用戶可是密碼不會加密,必須用人家提供的create_user()方法
user.save()#別忘記了保存

驗證密碼check_password(passwd)

用戶須要修改密碼的時候 首先要讓他輸入原來的密碼 ,若是給定的字符串經過了密碼檢查,返回 True

def my_view(request):
    user=request.user
    if request.method=="POST":
        old_password=request.POST.get("old_password")
        if user.check_password(old_password):
            return HttpResponse("ok")

這兒你可能會對request.user不理解 在這裏咱們來說一下

補充request.user

關於request的更多內容請見http://www.javashuo.com/article/p-uhxolcgp-ma.html, 這裏關於request的內容整理的很好

一個 AUTH_USER_MODEL 類型的對象,表示當前登陸的用戶。

  若是用戶當前沒有登陸,user 將設置爲 django.contrib.auth.models.AnonymousUser 的一個實例。你能夠經過 is_authenticated() 區分它們。

例如:

1
2
3
4
if  request.user.is_authenticated():
     # Do something for logged-in users.
else :
     # Do something for anonymous users.

    user 只有當Django 啓用 AuthenticationMiddleware 中間件時纔可用。

 

匿名用戶

  class models.AnonymousUser

  django.contrib.auth.models.AnonymousUser 類實現了django.contrib.auth.models.User 接口,但具備下面幾個不一樣點:

1
2
3
4
5
6
7
8
9
id 永遠爲None。
username 永遠爲空字符串。
get_username() 永遠返回空字符串。
is_staff 和 is_superuser 永遠爲False。
is_active 永遠爲 False。
groups 和 user_permissions 永遠爲空。
is_anonymous() 返回True 而不是False。
is_authenticated() 返回False 而不是True。
set_password()、check_password()、save() 和 delete () 引起 NotImplementedError。

   New in Django 1.8:

  新增 AnonymousUser.get_username() 以更好地模擬 django.contrib.auth.models.User

 

修改密碼set_password()

from django.contrib.auth.models import User
user=User.objects.get(username="xiaohia")
user.set_password(password="123456")
user.save() #別忘記了保存

註冊用戶

複製代碼
def sign_up(request):


    state = None

    if request.method == 'POST':

        password = request.POST.get('password', '')
        repeat_password = request.POST.get('repeat_password', '')
        email=request.POST.get('email', '')
        if password == '' or repeat_password == '':
            state = 'empty'
        elif password != repeat_password:
            state = 'repeat_error'
        else:
            username = request.POST.get('username', '')
            if User.objects.filter(username=username):
                state = 'user_exist'
            else:
                new_user = User.objects.create_user(username=username, password=password,email=email)
                new_user.save()
                new_my_user = MyUser(user=new_user, telephone=request.POST.get('telephone', '')) ####這步是什麼意思
                new_my_user.save()
                return redirect('/book/')
    content = {
        'state': state,
        'user': None,
    }
    return render(request, 'book/sign_up.html', content)
複製代碼

修改密碼:

@login_required
def set_password(request):
    user = request.user
    state = None
    if request.method == 'POST':
        old_password = request.POST.get('old_password', '')
        new_password = request.POST.get('new_password', '')
        repeat_password = request.POST.get('repeat_password', '')
        if user.check_password(old_password):
            if not new_password:
                state = 'empty'
            elif new_password != repeat_password:
                state = 'repeat_error'
            else:
                user.set_password(new_password)
                user.save()
                return redirect("/log_in/")
        else:
            state = 'password_error'
    content = {
        'user': user,
        'state': state,
    }
    return render(request, 'book/set_password.html', content)

修改密碼
View Code

中介模型

用中介模型的緣由

 

咱們知道咱們能夠用manytomany建立出第三張關係表, 咱們之前都作過這麼幾張表,學生表和課程表,學生表和課程表是多對多的關係, 可是我有這麼一個需求,我想在第三張關係表中記錄每一個學生每門課程的分數,這時候應該怎麼辦,咱們都知道不能在關係表中添加字段,這時候就須要第四章表了,第4張表就是中介模型,

建表代碼:

class Student(models.Model):
    name=models.CharField(max_length=32)
    courses=models.ManyToManyField(to="Course",through="Student2Course")  #在manytomany中增長一個through字段 關聯到第4張表

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

class Student2Course(models.Model):
    student=models.ForeignKey(to="Student") #用這步的緣由的是外鍵關聯到那兩張表會自動創建id字段
    course=models.ForeignKey(to="Course")
    score=models.IntegerField()

表結構:

發現了沒? 之前用manytomany自動創建的第三張表student_courses 沒有了,出現了一個新的表 student2course,讓咱們來看看這張表

是否是之前的關係表均可以繼承過來了,還增長了新字段.

注意事項

 之前\manytomany操做第3張表的方法,不少都不能用了,add、 create,remove,可是clear() 方法倒是可用的。它能夠清空某個實例全部的多對多關係, 可是咱們能夠用操做普通表的方式來操做第4張表.

相關文章
相關標籤/搜索