1、示例一
案例需求:出版社、作者和书籍的后台管理。
from django.db import models
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 '%s %s' % (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
2、用Django Form写一个添加出版社的 view
# books/templates/books/publisher_add.html
<!DOCTYPE html>
<html lang="en">
<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>
# books/views.py
def publisher_add(request):
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})
else:
return render(request, 'books/publish_add.html')
存在的问题:
- 验证用户输入
- 返回用户error
- html构造form
- 验证输入和model的约束重复
- 代码冗长
我们用Django Form来解决这些问题。
3、定义Form
>>> 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()
u'<p><label for="id_your_name">Your name:</label> <input id="id_your_name" maxlength="100" name="your_name" type="text" required /></p>'
>>> form.as_table()
>>> form.as_ul()
>>> form = NameForm({'your_name': 'mage'})
>>> form.is_bound
True
>>> form.is_valid()
True
>>> form.cleaned_data['your_name']
u'mage'
>>> form = NameForm({})
>>> form.is_bound
True
>>> form.is_valid()
False
>>> form.errors
{'your_name': [u'This field is required.']}
>>> form = NameForm({'your_name': 'sdf'*200})
>>> form.is_bound
True
>>> form.is_valid()
False
>>> form.errors
{'your_name': [u'Ensure this value has at most 100 characters (it has 600).']}
说明:
- field
- option
- print(form)
- print(form.as_p())
- form.is_bound
- form.is_valid()
- form.cleaned_data 必须先执行is_valid
- form.errors
# forms.py
from django import forms
class NameForm(forms.Form):
your_name = forms.CharField(label='Your name', max_length=100)
# views.py
from django.shortcuts import render
from django.http import HttpResponseRedirect
from .forms import NameForm
def get_name(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = NameForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
return HttpResponseRedirect('/thanks/')
# if a GET (or any other method) we'll create a blank form
else:
form = NameForm()
return render(request, 'name.html', {'form': form})
# name.html
<form action="/your-name/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
4、示例二
# forms.py
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)
# views.py
from django.core.mail import send_mail
if form.is_valid():
subject = form.cleaned_data['subject']
message = form.cleaned_data['message']
sender = form.cleaned_data['sender']
cc_myself = form.cleaned_data['cc_myself']
recipients = ['[email protected]']
if cc_myself:
recipients.append(sender)
send_mail(subject, message, sender, recipients)
return HttpResponseRedirect('/thanks/')
# sendmail.html
<form action="" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
# sendmail_manual.html
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.subject.errors }}
<label for="{{ form.subject.id_for_label }}">Email subject:</label>
{{ form.subject }}
</div>
<div class="fieldWrapper">
{{ form.message.errors }}
<label for="{{ form.message.id_for_label }}">Your message:</label>
{{ form.message }}
</div>
<div class="fieldWrapper">
{{ form.sender.errors }}
<label for="{{ form.sender.id_for_label }}">Your email address:</label>
{{ form.sender }}
</div>
<div class="fieldWrapper">
{{ form.cc_myself.errors }}
<label for="{{ form.cc_myself.id_for_label }}">CC yourself?</label>
{{ form.cc_myself }}
</div>
说明:
- EmailField
- widget
- BooleanField
- required
5、form和field
(1)form和field的一些属性
- {{ form.non_field_errors }}
- {{ form.errors }}
- {{ field.label }}
- {{ field.lable_tag }}
- {{ field.id_for_lable }}
- {{ field.value }}
- {{ field.html_name }}
- {{ field.help_text }}
- {{ field.errors }}
详见:https://docs.djangoproject.com/en/1.10/topics/forms/#looping-over-the-form-s-fields
(2)field校验
>>> from django import forms
>>> f = forms.EmailField()
>>> f.clean('[email protected]')
'[email protected]'
>>> f.clean('invalid email address')
Traceback (most recent call last):
...
ValidationError: ['Enter a valid email address.']
(3)内置form field 和 widgets,详见
https://docs.djangoproject.com/en/1.10/ref/forms/fields/
https://docs.djangoproject.com/en/1.10/ref/forms/widgets/
6、ModelForm
ModelForm 结合了 form和model,将model的field类型映射成form的field类型,复用了Model和Model的验证,写更少的代码,并且还实现了存储数据库的简单方法。
Model field类型 Form field类型映射关系,详见:https://docs.djangoproject.com/en/1.10/topics/forms/modelforms/#field-types
# models.py
from django.db import models
from django.forms import ModelForm
TITLE_CHOICES = (
('MR', 'Mr.'),
('MRS', 'Mrs.'),
('MS', 'Ms.'),
)
class Author(models.Model):
name = models.CharField(max_length=100)
title = models.CharField(max_length=3, choices=TITLE_CHOICES)
birth_date = models.DateField(blank=True, null=True)
def __str__(self): # __unicode__ on Python 2
return self.name
class Book(models.Model):
name = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
def __str__(self):
return self.name
# forms.py
class AuthorForm(ModelForm):
class Meta:
model = Author
fields = ['name', 'title', 'birth_date']
class BookForm(ModelForm):
class Meta:
model = Book
fields = ['name', 'authors']
基本相当于:
from django import forms
class AuthorForm(forms.Form):
name = forms.CharField(max_length=100)
title = forms.CharField(
max_length=3,
widget=forms.Select(choices=TITLE_CHOICES),
)
birth_date = forms.DateField(required=False)
class BookForm(forms.Form):
name = forms.CharField(max_length=100)
authors = forms.ModelMultipleChoiceField(queryset=Author.objects.all())
Model Form save() 方法:
>>> form = AuthorForm({'name': 'guang', 'title': 'MR'})
>>> form.is_valid()
>>> form.save()
<Author: guang>
>>> form = AuthorForm({'name': 'mage', 'title': 'MR'})
>>> form.is_valid()
>>> form.save()
<Author: mage>
>>> authors = Author.objects.all()
>>> authors_id = [author.id for author in authors]
>>> form = BookForm({'name': 'Django book', 'authors': authors_id})
>>> form.is_valid()
>>> form.save()
<Book: Django book>
>>> form = BookForm({'name': 'Python book', 'authors': authors_id})
>>> book = form.save(commit=False)
>>> book.name = 'New Python book'
>>> book.save()
>>> form.save_m2m()
7、Django Form Meta
class AuthorForm(ModelForm):
class Meta:
model = Author
fields = ('name', 'title', 'birth_date')
# fields = '__all__'
# exclude = ('birth_date')
labels = {
'name': 'Writer',
}
widgets = {
'name': Textarea(attrs={'cols': 80, 'rows': 20}),
}
help_texts = {
'name': _('Some useful help text.'),
}
error_messages = {
'name': {
'max_length': _("This writer's name is too long."),
},
}
8、Django models form 自定义验证
from django.forms import ModelForm, ValidationError
class AuthorForm(ModelForm):
class Meta:
model = Author
fields = ('name', 'title', 'birth_date')
def clean_name(self):
name = self.cleaned_data['name']
if len(name) < 30:
raise ValidationError("Length must be more than 30")
return name
def clean(self):
cleaned_data = super(AuthorForm, self).clean()
name = cleaned_data.get('name')
title = cleaned_data.get('title')
if len(name) < 40 and title == 'MR':
raise ValidationError('xxxx')
9、view和模板中使用 Model Form
# forms.py
from django.forms import ModelForm
from .models import Publisher
class PublisherForm(ModelForm):
class Meta:
model = Publisher
fields = '__all__'
# views.py
from django.shortcuts import render, redirect
from django.http import HttpResponse
from .forms import PublisherForm
def publisher_add(request):
if request.method == 'POST':
form = PublisherForm(request.POST)
if form.is_valid():
publisher = form.save()
return HttpResponse('Add success')
#return redirect('some view name')
else:
form = PublisherForm()
return render(request, 'publisher_add.html', {'form': form})
# publisher_add.html
<form method="post" action="">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit">
</form>
10、Django Model Form initial and instance
from django.shortcuts import render, redirect, get_object_or_404
from django.http import HttpResponse
from .forms import PublisherForm
def publisher_add(request):
if request.method == 'POST:
form = PublisherForm(request.POST)
if form.is_valid():
publisher = form.save()
return redirect('some view name')
else:
form = PublisherForm(initial={'name': "O'Reilly"})
return render(request, 'publisher_add.html', {'form': form})
def publisher_update(request, publisher_id):
publisher = get_object_or_404(Publisher, id=publisher_id)
if request.method == 'POST':
form = PublisherForm(request.POST, instance=publisher)
if form.is_valid():
publisher = form.save()
return redirect('some view name')
form = PublisherForm(instance=publisher)
return render(request, 'publisher_update.html', {'form': form})
11、Django form bootstrap 插件
详见:https://github.com/tzangms/django-bootstrap-form