【Django】Django框架进阶详述(二)

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