【Django】Django框架进阶详述(二)
- 2020 年 2 月 14 日
- 笔记
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