95django_form

 

目錄

form.. 1

HTMLGETPOST... 1

1form處理:... 2

django form... 4

2,自動生成表單:... 6

3,手動渲染字段:... 11

form field... 11

form field args... 12

 

 

 

form

 

HTMLGETPOST

處理表單時候只會用到POST GET 方法。

 

Django 的登錄表單使用POST 方法,在這個方法中瀏覽器組合表單數據、對它們進行編碼以用於傳輸、將它們發送到服務器然後接收它的響應。

 

相反,GET 組合提交的數據爲一個字符串,然後使用它來生成一個URL 這個URL 將包含數據發送的地址以及數據的鍵和值。 如果你在Django 文檔中做一次搜索,你會立即看到這點,此時將生成一個https://docs.djangoproject.com/search/?q=forms&release=1 形式的URL

 

POST GET 用於不同的目的。

 

用於改變系統狀態的請求 —— 例如,給數據庫帶來變化的請求 —— 應該使用POST GET 只應該用於不會影響系統狀態的請求。

 

GET 還不適合密碼錶單,因爲密碼將出現在URL 中,以及瀏覽器的歷史和服務器的日誌中,而且都是以普通的文本格式。 它還不適合數據量大的表單和二進制數據,例如一張圖片。 使用GET 請求作爲管理站點的表單具有安全隱患:***者很容易模擬表單請求來取得系統的敏感數據。 POST,如果與其它的保護措施結合將對訪問提供更多的控制,例如Django CSRF protection

 

另一個方面,GET 適合網頁搜索這樣的表單,因爲這種表示一個GET 請求的URL 可以很容易地作爲書籤、分享和重新提交。

 

 

1form處理:

 

mysite/polls/templates/polls/detail.html

<h1>{{ question.question_text }}</h1>

 

{% if error_message %}

    <P><strong>{{ error_message }}</strong></P>

{% endif %}

 

<form action="{% url 'polls:vote' question.id %}">

{% csrf_token %}

    {% for choice in question.choice_set.all %}

        <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />

        <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br />

    {% endfor %}

    <input type="submit" value="Vote" />

</form>

 

 

mysite/polls/views.py

from django.shortcuts import render, get_object_or_404, reverse

from django.http import HttpResponse, HttpResponseRedirect

from .models import Question, Choice

from django.template import loader

# from django.core.urlresolvers import reverse

 

# def index(request):

#     latest_question_list = Question.objects.order_by('-pub_date')[:4]

#     template = loader.get_template('polls/index.html')

#     context = {'latest_question_list': latest_question_list}

#     # output = ', '.join([q.question_text for q in latest_question_list])

#     return HttpResponse(template.render(context))

def index(request):

    latest_question_list = Question.objects.order_by('-pub_date')[:4]

    context = {'latest_question_list': latest_question_list}

    return render(request, 'polls/index.html', context)

 

def detail(request, question_id):

    # try:

    #     question = Question.objects.get(id=question_id)

    # except Question.DoesNotExist:

    #     return HttpResponse('Not Found', status=404)

    question = get_object_or_404(Question, pk=question_id)

    return render(request, 'polls/detail.html', {'question': question})

 

def results(request, question_id):

    question = get_object_or_404(Question, pk=question_id)

    return render(request, 'polls/results.html', {'question': question})

 

def vote(request, question_id):

    question = get_object_or_404(Question, pk=question_id)

    print(request)

    if request.method == 'POST':

        choice_id = request.POST.get('choice', 0)

        try:

            selected_choice = question.choice_set.get(pk=choice_id)

        except Choice.DoesNotExist:

            return render(request, 'polls/detail.html', {

                'question': question, 'error_message': 'You did not select a choice',

            })

        else:

            selected_choice.votes += 1

            selected_choice.save()

            return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))

 

 

mysite/polls/templates/polls/results.html

<h1>{{ question.question_text }}</h1>

 

<ul>

    {% for choice in question.choice_set.all %}

    <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>

    {% endfor %}

</ul>

 

<a href="{% url 'polls:detail' question.id %}">Vote again?</a>

 

 

django form

 

模型類的屬性映射到數據庫表中的字段;

表單類的屬性映射到HTML forminput元素;

所有的表單類都作爲django.forms.Form的子類創建,包括ModelForm

 

<form>{% csrf_token %}</form>django原生支持一個簡單易用的protection against cross site request forgeries,當提交一個啓用CSRF防護的POST表單時,必須使用{ % csrf_token %}模板標籤;

forms.py中,在定義表單時,若有URLFieldEmailField或其它整數字段類型,django將使用numberurlemail這樣的HTML5輸入類型,默認browser會對這些字段進行它們自身的驗證,這些驗證比django驗證嚴格,如果要禁用HTML5中的驗證,在模板中加上formnovalidate屬性或指定一個不同的字段TextInput

 

若打算直接用來添加和編輯django模型,ModelForm可節省時間、精力、代碼,因爲它將根據Model類構建一個表單及適當的字段和屬性;

 

綁定表單和未綁定表單:

form.is_bound屬性返回True|False

不包含數據的表單稱爲未綁定表單,當render給用戶時,它將爲空或包含默認的值;

包含數據的表單稱爲綁定表單,可驗證數據是否合法,如果render一個不合法的綁定表單,它將給browser拋錯,讓用戶糾正數據;

 

widget,窗口小部件,每個表單字段都有一個對應的widget class,它對應一個HTML表單widget,如<input type="text">;大部分情況下,字段都具有一個合理默認widget,如CharField具有一個TextInput Widget,它在HTML中生成一個<input type="text">,如果需要message,在定義表單字段時應指定一個合適的widget,如message = forms.CharField(widget=forms.Textarea)

 

表單呈現選項:

form.as_p()

form.as_table()   #在模板中需自己提供<table><ul>元素

form.as_ul()

 

HTMLlabel標籤爲input元素定義標註,label標籤的for屬性應與相關元素的id屬性相同;

定義表單字段時用了label標籤,使用form.as_p自動生成labelforid_<field-name>,對應的input中的id也是id_<field-name>

 

渲染表單錯誤信息:

模板中,{{ form.non_field_errors }},查找每個字段的錯誤,並展示到上方;

|      Returns an ErrorList of errors that aren't associated with a particular

 |      field -- i.e., from Form.clean(). Returns an empty ErrorList if there

 |      are none.

模板中,{{ form.<field-name>.errors }},顯示錶單錯誤的一個清單,並渲染成一個<ul>

 

循環表單字段:

https://yiyibooks.cn/xx/Django_1.11.6/topics/forms/index.html#html-forms

若表單使用相同的html,可循環迭代每個字段來減少重複代碼;

{% for field in form %}

    <div>

        {{ field.errors }}

        {{ field.label_tag }} {{ field }}

        {% if field.help_text %}

        <p>{{ field.help_text|safe }}</p>

        {% endif %}

    </div>

{% endfor %}

 

{{ field }}屬性:

{{ field.label }},字段的label,如Email

{{ field.label_tag }},包含在HTML label標籤中的字段label,它包含表單的label_suffix,如默認label_suffix是冒號;

{{ field.id_for_label }},手動渲染字段用到,如<label for="id_email">Email:</label>lablefor對應{{ field.id_for_label }}

{{ field.value }},字段的值,如[email protected]

{{ field.html_name }},輸入元素的name屬性中將使用的名稱,它將考慮到表單的前綴;

{{ field.help_text }},與該字段關聯的提示信息(幫助文檔);

{{ field.errors }},輸出一個<ul>,包含這個字段的驗證錯誤信息;

 

表單驗證:

view中在調用form.is_valid()時驗證;

模板中{{ form.non_filed_errors }}{{ field.errors }}訪問errors屬性時隱式調用;

 

field手動校驗:

>>> form = forms.EmailField()

>>> form.clean('[email protected]')

'[email protected]'

>>> form.clean('test')

……

    raise ValidationError(errors)

django.core.exceptions.ValidationError: ['Enter a valid email address.']

 

 

>>> from django import forms

>>> class NameForm(forms.Form):

...     your_name = forms.CharField(label='your name', max_length=100)

...

>>> form = NameForm()

>>> form.is_bound

False

>>> form.as_p()   #表單呈現選項,將其渲染在<p>標籤中;label標籤爲input元素定義標註,label標籤的for屬性應與相關元素的id屬性相同

'<p><label for="id_your_name">your name:</label> <input type="text" name="your_name" maxlength="100" required id="id_your_name" /></p>'

>>> form.as_table()   #以表格形式將其渲染在<tr>

'<tr><th><label for="id_your_name">your name:</label></th><td><input type="text" name="your_name" maxlength="100" required id="id_your_name" /></td></tr>'

>>> form.as_ul()   #將其渲染在<li>標籤中

'<li><label for="id_your_name">your name:</label> <input type="text" name="your_name" maxlength="100" required id="id_your_name" /></li>'

 

>>> form=NameForm({'your_name':'jowin'})

>>> form.is_bound   #綁定表單和未綁定表單

True

>>> form

<NameForm bound=True, valid=Unknown, fields=(your_name)>

>>> form.cleaned_data['your_name']   #訪問數據前沒有form.is_valid()no attribute

Traceback (most recent call last):

  File "<console>", line 1, in <module>

AttributeError: 'NameForm' object has no attribute 'cleaned_data'

>>> form.is_valid()   #2件事,1爲所有字段運行驗證的程序,若所有字段都是合法數據返回True2並將表單的數據放到cleaned_data屬性中

True

>>> form.cleaned_data['your_name']   #訪問數據前要先執行form.is_valid()

'jowin'

 

>>> form=NameForm({})

>>> form.is_bound

True

>>> form.is_valid()

False

>>> form.errors

{'your_name': ['This field is required.']}

 

>>> form = NameForm({'your_name':'jowin'*50})

>>> form.is_bound

True

>>> form.is_valid()

False

>>> form.errors

{'your_name': ['Ensure this value has at most 100 characters (it has 250).']}

 

 

2,自動生成表單:

 

出版社、作者、書籍案例;

模板中用{{ form }}自動生成;

<form method="post" action="">

    {% csrf_token %}

    {{ form }}

    <input type="submit" value="Submit">

</form>

 

(webproject) C:\webproject\mysite>django-admin startapp books

 

(webproject) C:\webproject\mysite>pip install django-bootstrap3

 

https://github.com/dyve/django-bootstrap3

 

mysite/mysite/settings.py

INSTALLED_APPS = [

    'bootstrap3',

 

 

mysite/books/models.py

from django.db import models

from django import forms

 

class Publisher(models.Model):

    name = models.CharField(max_length=30)

    address = models.CharField(max_length=50)

    city = models.CharField(max_length=60)

    state_province = models.CharField(max_length=30)

    country = models.CharField(max_length=50)

    website = models.URLField()

 

    def __str__(self):

        return self.name

 

class Author(models.Model):

    first_name = models.CharField(max_length=30)

    last_name = models.CharField(max_length=40)

    email = models.EmailField()

 

    def __str__(self):

        return '{} {}'.format(self.first_name, self.last_name)

 

class Book(models.Model):

    title = models.CharField(max_length=100)

    authors = models.ManyToManyField(Author)

    publisher = models.ForeignKey(Publisher)

    publication_date = models.DateField()

 

    def __str__(self):

        return self.title

 

 

mysite/books/forms.py

from django import forms

 

class PublisherForm(forms.Form):

    name = forms.CharField(label='Your name', max_length=30)

    address = forms.CharField(max_length=50)

    city = forms.CharField(max_length=60)

    state_province = forms.CharField(max_length=30)

    country = forms.CharField(max_length=20)

    website = forms.CharField(max_length=50)

 

 

mysite/books/views.py

from django.shortcuts import render, redirect

from .models import Publisher

from .forms import PublisherForm

from django.http import HttpResponse

 

# def publisher_add(request):   #ver1,驗證用戶輸入(驗證用戶輸入和model的約束重複)、返回用戶errorhtml構造form、代碼冗長,用django form解決

#     if request.method == 'POST':

#         name = request.POST.get('name', '')

#         address = request.POST.get('address', '')

#         city = request.POST.get('city', '')

#         state_province = request.POST.get('state_province', '')

#         country = request.POST.get('country', '')

#         website = request.POST.get('website', '')

#

#         error_message = []

#         if not name:

#             error_message.append('name is required')

#         if len(name) > 100:

#             error_message.append('name should be short than 100')

#         if not address:

#             error_message.append('address is required')

#

#         if error_message:

#             return render(request, 'books/book_add.html', {'error_message': error_message})

#         else:

#             publisher = Publisher(name=name, address=address, city=city, state_province=state_province,

#                                   country=country, website=website)

#             # return redirect('books:publisher-detail', kwargs={'publisher_id': publisher.id})

#             publisher.save()

#             return HttpResponse('add succeed')

#     else:

#         return render(request, 'books/publisher_add.html')

 

def publisher_add(request):   #ver2,用戶的輸入不用手動獲取;用戶輸入驗證自動完成;模板中的表單不用手寫html;帶來的問題,需要編寫Form結構(和model重複),需要手動獲取內容保存到數據庫,解決,使用modelform

    if request.method == 'POST':   #若使用POST請求提交表單,將創建一個表單實例,並用browser傳來的數據填充,這被稱爲將數據綁定到表單

        form = PublisherForm(request.POST)   #將數據綁定到表單

        if form.is_valid():

            publisher = Publisher(

                name=form.cleaned_data['name'],   #form.cleaned_data('name')訪問數據前要先form.is_valid()form.is_valid()會將數據放到cleaned_data屬性中                address=form.cleaned_data['address'],

                city=form.cleaned_data['city'],

                state_province=form.cleaned_data['state_province'],

                country=form.cleaned_data['country'],

                website=form.cleaned_data['website']

            )

            publisher.save()   #form.cleaned_data['website']找到所有合法的表單數據,在發送HTTP重定向給browser告訴它下一步的去向前,用這些數據更新DB

            return HttpResponse('add successed')

        else:   #form.is_valid()不是True,返回到表單的模板,這時表單不再爲空(未綁定),HTML表單將用之前提交的數據填充,再根據要求編輯並改正它

            return render(request, 'books/books_add.html', {'form': form})

    else:   #如果是GET請求,將創建一個空的表單實例並將其放置在要渲染的模板的上下文中

        form = PublisherForm()

        return render(request, 'books/books_add.html', {'form': form})

 

 

mysite/books/templates/books/publisher_add.html   #ver1

<!DOCTYPE html>

<html>

<head>

    <meta charset="UTF-8">

    <title>Title</title>

</head>

<body>

    <form method="post" action="">

        {% csrf_token %}

        <label>Name: </label>

        <input type="text" name="name">

        <br>

        <label>Address: </label>

        <input type="text" name="address">

        <br>

        <label>City: </label>

        <input type="text" name="city">

        <br>

        <label>State Province: </label>

        <input type="text" name="state_province">

        <br>

        <label>Country: </label>

        <input type="text" name="country">

        <br>

        <label>Website: </label>

        <input type="text" name="website">

        <br>

 

        <input type="submit" value="Submit">

    </form>

</body>

</html>

 

 

mysite/books/templates/books/books_add.html   #ver2

<!--<form method="post" action="">-->   #不用bootstrap的樣式展示

    <!--{% csrf_token %}-->   #django原生支持一個簡單易用的protection against cross site request forgeries,當提交一個啓用CSRF防護的POST表單時,必須使用{ % csrf_token %}模板標籤

    <!--{{ form }}-->   #django會根據模型類的字段和屬性,在HTML中自動生成對應表單標籤和標籤屬性,生成的標籤會放置在{{ form }}位置

    <!--<input type="submit" value="Submit">-->

<!--</form>-->

{% load bootstrap3 %}

 

<html>   #使用bootstrap樣式展示

    <head>

        <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" >

        <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>

    </head>

 

    <div>

        <form method="post">

            {% csrf_token %}

            <h1>添加</h1>

            {% bootstrap_form form %}

            {% buttons %}

            <button type="submit" class="btn btn-primary">

            {% bootstrap_icon "star" %} Submit

            </button>

            {% endbuttons %}

        </form>

    </div>

</html>

 

 

(webproject) C:\webproject\mysite>python manage.py makemigrations

(webproject) C:\webproject\mysite>python manage.py migrate

 

 

mysite/books/urls.py

from django.conf.urls import url

from . import views

 

app_name = 'books'

 

urlpatterns = [

    url(r'^$', views.publisher_add, name='index'),

]

 

 

mysite/mysite/urls.py

urlpatterns = [

    url(r'^books/', include('books.urls')),

    url(r'^admin/', admin.site.urls),

]

 

(webproject) C:\webproject\mysite>python manage.py runserver

1.jpg

 

 

3,手動渲染字段:

 

自己對展示的表單字段進行先後排序;

 

from django import forms

 

class ContactForm(forms.Form):

    subject = forms.CharField(max_length=100)

    message = forms.CharField(widget=forms.Textarea)

    sender = forms.EmailField()

    cc_myself = forms.BooleanField(required=False)

 

 

{{ form.non_field_errors }}   #

<div>

    {{ form.subject.errors }}

    <label for="{{ form.subject.id_for_label }}">Email subject:</label>

    {{ form.subject }}

</div>

<div>

    {{ form.message.errors }}

    <label for="{{ form.message.id_for_label }}">Your message:</label>

    {{ form.message }}

</div>

<div>

    {{ form.sender.errors }}

    <label for="{{ form.sender.id_for_label }}">Your email address:</label>

    {{ form.sender }}

</div>

<div>

    {{ form.cc_myself.errors }}

    <label for="{{ form.cc_myself.id_for_label }}">CC yourself?</label>

    {{ form.cc_myself }}

</div>

 

 

form field

 

https://docs.djangoproject.com/en/2.1/ref/forms/fields/

 

BooleanField

CharField

ChoiceField

DateField

DateTimeField

DecimalField

EmailField   #實際是CharField的子類,只不過是在其基礎上加了些驗證

FileField

FilePathField

FloatField   #py float

ImageField

IntergerField   #py int

 

 

form field args

 

required

label

label_suffix

widget

help_text

error_messages

 2.jpg

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章