专栏首页魏晓蕾的专栏【Django】Django框架进阶详述(二)

【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 = ['info@example.com']
    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('foo@example.com')
'foo@example.com'
>>> 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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 关于Oracle的几个问题及解决方案

    Windows无法启动OracleOraDb10g_home1TNSListener服务,错误1067

    魏晓蕾
  • Linux 背景知识

    版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/...

    魏晓蕾
  • 【Django】Django框架进阶详述(三)

    (2)内置Field 详见:https://docs.djangoproject.com/en/1.10/ref/models/fields/ (3)Met...

    魏晓蕾
  • Django model 层之Models与Mysql数据库小结

    下载地址:https://www.python.org/downloads/release/python-340/

    授客
  • 深入理解AdmissionWebhook part - 1

    Admission webhooks 是接收准入请求http回调并且进行处理,分为两种类型:

    有点技术
  • django admin后管定制-显示字段的实例

    1、django 自带了admin后管,如果我们需要使用,只需把我们定义的models注册即可;

    砸漏
  • django2实战2.创建博客应用创建应用文章表的字段设计激活应用开启后台将post模型注册到后台自定义文章显示字段增加可选操作

    上篇创建了一个项目,项目相当于站点,应用即是独立的功能模块。比如:淘宝是一个完整的网站,即是一个项目。而淘宝下的聚划算属于一个应用

    章鱼喵
  • 用IDEA详解Spring中的IoC和DI(挺透彻的,点进来看看吧)

    控制反转(IoC)是一个比较抽象的概念,它主要用来消减计算机程序的耦合问题,是Spring框架的核心。 依赖注入(DI)是IoC的另外一种说法,只是从不同的角度...

    泰斗贤若如
  • VUE+WebPack:开发一款太空版植物大战僵尸的前端页游

    望月从良
  • python传参是传值还是传引用

    在此之前先来看看变量和对象的关系:Python 中一切皆为对象,数字是对象,列表是对象,函数也是对象,任何东西都是对象。而变量是对象的一个引用(又称为名字或者标...

    用户1679793

扫码关注云+社区

领取腾讯云代金券