【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

发布了224 篇原创文章 · 获赞 605 · 访问量 130万+

猜你喜欢

转载自blog.csdn.net/gongxifacai_believe/article/details/104072831
今日推荐