第一步 入门

检查版本

python -m django --version

创建第一个项目

django-admin startproject mysite

运行

 python manage.py runserver

更改端口

python manage.py runserver 8080

更改IP

python manage.py runserver 0:8000

1.创建app

创建投票应用

python manage.py startapp polls

polls/views.py

from django.shortcuts import render

from django.http import HttpResponse


def index(request):
    return HttpResponse("Hello, world. You're at the polls index.")

在polls里面创建一个urls.py文件,代码如下

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^$',views.index,name='index'),
]

在mysite/urls.py中添加include

from django.conf.urls import url,include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'polls/',include('polls.urls')),
]

访问:http://127.0.0.1:8000/polls/,就可以看到信息“Hello, world. You're at the polls index.”

2.创建模型

设置为Mysql数据库

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django',        #数据库名字
        'USER': 'root',          #账号
        'PASSWORD': '123456',      #密码
        'HOST': '127.0.0.1',    #IP
        'PORT': '3306',                   #端口
    }
}

polls/init.py

import pymysql
pymysql.install_as_MySQLdb()

polls/models.py

from django.db import models


class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

    def __str__(self):
        return self.question_text


class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)
    
    def __str__(self):
        return self.choice_text

然后执行下面的命令

python manage.py makemigrations      创建迁移文件

python manage.py migrate             更新到数据库中。

 3.后台管理

(1)创建管理员用户,设置用户名,邮箱,密码

python manage.py createsuperuser

登录:http://127.0.0.1:8000/admin/

(2)把polls应用添加到后台管理站点

编辑polls/admin.py

from django.contrib import admin

from polls.models import Question

admin.site.register(Question)

刷新就可以看到polls

13.Django1.11.6文档

4.视图

(1)polls/urls.py

from django.conf.urls import url

from . import views

urlpatterns = [
    # ex: /polls/
    url(r'^$', views.index, name='index'),
    # ex: /polls/5/
    url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
    # ex: /polls/5/results/
    url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
    # ex: /polls/5/vote/
    url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
]

(2)创建polls/templates/polls/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}
        <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
    {% else %}
        <p>No polls are available.</p>
    {% endif %}
</body>
</html>

(3)polls/views.py

from django.shortcuts import render,HttpResponse
from polls.models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {'latest_question_list': latest_question_list,}
    return render(request,'polls/index.html',context)

def detail(request, question_id):
    return HttpResponse("You're looking at question %s." % question_id)

def results(request, question_id):
    response = "You're looking at the results of question %s."
    return HttpResponse(response % question_id)

def vote(request, question_id):
    return HttpResponse("You're voting on question %s." % question_id)

(4)抛出404异常

from django.shortcuts import render,HttpResponse,Http404

def detail(request, question_id):
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404('Question does not exist')
    return render(request,'polls/detail.html',{'question':question})

detail.html

{{ question }}

(5)快捷方式  get_object_or_404

 更改detail()视图如下

from django.shortcuts import get_object_or_404

def detail(request, question_id):
    question = get_object_or_404(Question,pk=question_id)
    return render(request,'polls/detail.html',{'question':question})

Http404异常。

 Http404

 (6)使用模板系统

编辑detail.html

 <h1>{{ question.question_text }}</h1>
    <ul>
        {% for choice in question.choice_set.all %}
            <li>{{ choice.choice_text }}</li>
        {% endfor %}
    </ul>

方法调用发生在{% for %}循环中:Choice被解释为Python的代码question.choice_set.all,它返回一个由question.choice_set.all()对象组成的可迭代对象,并将其用于{% for %}标签。

最终:

13.Django1.11.6文档

13.Django1.11.6文档

http://127.0.0.1:8000/polls/   访问index页面,显示question列表

13.Django1.11.6文档

点任意一个击question,然后跳转到detail页面

13.Django1.11.6文档

(7)移除模板中硬编码的URL

前面在index.html中跳转到detail页面的编码

<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>

%}模板标签来移除对你的URL配置中定义的特定的URL的依赖:修改如下

<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

如果你想把polls应用中detail视图的URL改成其它样子比如polls/specifics/12/,就可以不必在该模板(或者多个模板)中修改它,只需要修改polls/urls.py

url(r'^specifics/(?P<question_id>[0-9]+)/$', views.detail, name='detail'),

(8)命名空间URL名称

Django如何区分它们URL的名字呢?

app_name来设置应用程序命名空间:

添加 app_name='polls'

from django.conf.urls import url
from . import views

app_name = 'polls'

urlpatterns = [
    url(r'^$', views.index, name='index'),
    # ex: /polls/5/
    url(r'^(?P<question_id>[0-9]+)/$', views.detail, name='detail'),
    # ex: /polls/5/results/
    url(r'^(?P<question_id>[0-9]+)/results/$', views.results, name='results'),
    # ex: /polls/5/vote/
    url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name='vote'),
]

然后修改polls/index.html

<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>

改为

<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>

 5.表单

(1)更新一下polls/details.html

<h1>{{ question.question_text }}</h1>

{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}

<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
{% for choice in question.choice_set.all %}
    <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
    <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br />
{% endfor %}
<input type="submit" value="Vote" />
</form>

简要说明:

在detail网页模板中,我们为Question对应的每个Choice都添加了一个单选按钮用于选择。choice=#,其中# 为选择的Choice的ID

由于我们创建一个POST表单(它具有修改数据的作用),所以我们需要小心跨站点请求伪造。简而言之,所有针对内部URL的POST表单都应该使用{% csrf_token %}模板标签。

(2)vote.py处理投票

现在,我们来创建一个处理提交的数据的Django视图,并用它来处理

from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect, HttpResponse
from django.urls import reverse

from .models import Choice, Question
# ...
def vote(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    try:
        selected_choice = question.choice_set.get(pk=request.POST['choice'])
    except (KeyError, Choice.DoesNotExist):
        # 重新显示该问题的表单
        return render(request, 'polls/detail.html', {
            'question': question,
            'error_message': "You didn't select a choice.",
        })
    else:
        selected_choice.votes += 1
        selected_choice.save()
        # 始终在成功处理 POST 数据后返回一个 HttpResponseRedirect ,
        # (合并上句) 这样可以防止用户点击“后退”按钮时数据被发送两次。
        # (合并至上一句)
        return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))

request.POST 的值永远是字符串。

HttpResponseRedirect只接收一个参数:用户将要被重定向的URL

'results'视图来显示最终的页面。

(3)results()

当有人对Question进行投票后,vote()视图将请求重定向到Question的结果界面。

def results(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/results.html', {'question': question})

(4)results.html

<h1>{{ question.question_text }}</h1>

<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>    #pluralize复数
{% endfor %}
</ul>

<a href="{% url 'polls:detail' question.id %}">Vote again?</a>

如果你提交时没有选择任何Choice,你应该看到错误信息。

 6.静态文件

(1)创建style.css   

-->>目录 polls/static/polls/style.css

添加下面代码到style.css

li a {
    color: green;
}

在index.html添加如下

{% load static %}

<link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}" />

{% static %}模板标签生成静态文件的绝对URL。

再访问index界面,会发现question链接是绿色的

(2)添加背景图片

polls/static/polls/目录中创建一个 images 子目录。在这个目录中,放入一张图片background.gif-->>(polls/static/polls/images/background.gif)

添加样式

body {
    background: white url("images/background.gif") no-repeat right bottom;
}

7.自定义管理后台的表单

(1)重新排序编辑表单上的字段

from django.contrib import admin

from polls.models import Question


class QuestionAdmin(admin.ModelAdmin):
    fields = ['pub_date','question_text']

admin.site.register(Question,QuestionAdmin)

任何时候你需要更改模型的管理选项,你将遵循此模式 — 创建一个模型管理类,然后将其作为第二个参数传递给admin.site.register()

(2)添加关联对象

在创建Question对象的同时可以直接添加一组Choice

from django.contrib import admin

from .models import Choice, Question


class ChoiceInline(admin.StackedInline):
    model = Choice
    extra = 3


class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        (None,               {'fields': ['question_text']}),
        ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
    ]
    inlines = [ChoiceInline]

admin.site.register(Question, QuestionAdmin)

默认提供足够3个Choice的空间。

打开“Add question”页面:

13.Django1.11.6文档

它这样工作:有三个所关联的Choice —— 由extra指定 —— 每次你回到已经存在对象的"Change"页面时,都会额外地获得三个空白Choice。

 如果你点击它,就会增加一个新的空白Choice。

 ChoiceInline声明更改为:

class ChoiceInline(admin.TabularInline):
    model = Choice
    extra = 3

使用 TabularInline(而不是StackedInline),这些相关联的对象显示成紧凑的、基于表格的形式:

13.Django1.11.6文档

(3)自定义管理变更清单

class QuestionAdmin(admin.ModelAdmin):
    list_display = ('question_text','pub_date')

13.Django1.11.6文档

(4)使用list_filter来添加过滤器

class QuestionAdmin(admin.ModelAdmin):
    list_display = ('question_text','pub_date')
    list_filter = ['pub_date']

13.Django1.11.6文档

(5)添加搜索功能

class QuestionAdmin(admin.ModelAdmin):
    list_display = ('question_text','pub_date')
    list_filter = ['pub_date']
    search_fields = ['question_text']

13.Django1.11.6文档

 

 8.定制管理后台的外观

很明显,每个管理页面的顶部都有“Django administration”不太合适。Django的管理站点是用Django自己制作出来的,它的界面代码使用的是Django自己的模板系统。

定制项目的模板

DIRS 选项:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

现在,在templates下创建一个名为admin的文件夹,然后从Django安装的原目录下(目录为django/contrib/admin/templates)将模板页面的源文件admin/base_site.html拷贝到这个文件夹里。

C:\Users\Administrator\AppData\Local\Programs\Python\Python36\Lib\site-packages\django\contrib\admin\templates\admin
我的文件位置

 如果不知道Django源文件路径,运行如下命令

$ python -c "import django; print(django.__path__)"

编辑完成后应该类似下面的代码片段:

{% extends "admin/base.html" %}

{% block title %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}

{% block branding %}
<h1 id="site-name"><a href="{% url 'admin:index' %}">{{ site_header|default:_('Django administration') }}</a></h1>
{% endblock %}

{% block nav-global %}{% endblock %}
默认的base.html
{% extends "admin/base.html" %}

{% block title %}{{ title }} | Polls Administration{% endblock %}

{% block branding %}
<h1 id="site-name"><a href="{% url 'admin:index' %}">Polls Administration</a></h1>
{% endblock %}

{% block nav-global %}{% endblock %}

改变之后效果:

13.Django1.11.6文档

 模型层

 1.字段选项

 null

False

blank

False

   blank=False,该字段就是必填的。

 choices

如果设置了choices ,默认的表单将是一个选择框而不是标准的文本框,而且这个选择框的选项就是choices 中的选项。

这是一个关于 choices 列表的例子:

例如:

from django.db import models

class Person(models.Model):
    SHIRT_SIZES = (
        ('S', 'Small'),
        ('M', 'Medium'),
        ('L', 'Large'),
    )
    name = models.CharField(max_length=60)
    shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
>>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
'L'
>>> p.get_shirt_size_display()
'Large'

default

如果可调用 ,每个新对象创建时它都会被调用。

help_text

即使字段不在表单中使用,它对生成文档也很有用。

primary_key

True,那么这个字段就是模型的主键。

unique

如果为True, 则这个字段在整张表中必须是唯一的。

2.字段的自述名

如果没有给定自述名,Django 将根据字段的属性名称自动创建自述名 —— 将属性名称的下划线替换成空格。

 

在这个例子中,自述名是 "person's first name":

first_name = models.CharField("person's first name", max_length=30)

在这个例子中,自述名是 "first name"

first_name = models.CharField(max_length=30)

ForeignKeyManyToManyField 和 OneToOneField 都要求第一个参数是一个模型类,所以要使用 verbose_name 关键字参数才能指定自述名:

poll = models.ForeignKey(
    Poll,
    on_delete=models.CASCADE,
    verbose_name="the related poll",
)
sites = models.ManyToManyField(Site, verbose_name="list of sites")
place = models.OneToOneField(
    Place,
    on_delete=models.CASCADE,
    verbose_name="related place",
)

Django 在必要的时候会自动大写首字母。

 模型继承

在Django 中有3种风格的继承。

  1. 抽象的基类
  2. 多表继承。
  3. 代理模型。

3.抽象基类

如果抽象基类和它的子类有相同的字段名,那么将会出现error(并且Django将抛出一个exception)。

一个例子:

from django.db import models

class ConmonInfo(models.Model):
    name = models.CharField(max_length=100)
    age = models.PositiveIntegerField()

    class Meta:
        abstract = True

class Student(ConmonInfo):
    home_group = models.CharField(max_length=5)

它无法生成一张数据表或者拥有一个管理器,并且不能实例化或者直接储存。

4.Meta继承

例如:

from django.db import models

class ConmonInfo(models.Model):
    name = models.CharField(max_length=100)
    age = models.PositiveIntegerField()

    class Meta:
        abstract = True
        ordering = ['name']

class Student(ConmonInfo):
    home_group = models.CharField(max_length=5)

    class Meta(ConmonInfo.Meta):
        db_table = 'student_info'  #更改表名

5.多表继承

例如:

from django.db import models

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

class Restaurant(Place):
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)

所以下面两个语句都是可以运行的:

>>> Place.objects.filter(name="Bob's Cafe")
>>> Restaurant.objects.filter(name="Bob's Cafe")

6.代理模型

使用 多表继承时,model 的每个子类都会创建一张新数据表, 通常情况下,这正是我们想要的操作。这是因为子类需要一个空间来存储不包含在基类中的字段数据。 但有时,你可能只想更改 model 在 Python 层的行为实现。比如:更改默认的 manager ,或是添加一个新方法。

不同之处在于:你可以在代理 model 中改变默认的排序设置和默认的 manager ,更不会对原始 model 产生影响。

True,就完成了对代理 model 的声明。

你可以这样做:

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

class MyPerson(Person):
    class Meta:
        proxy = True

    def do_something(self):
        # ...
        pass

MyPerson访问,反之亦然:

>>> p = Person.objects.create(first_name="foobar")
>>> MyPerson.objects.get(first_name="foobar")

 7.执行查询

一个博客应用

from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=200)
    email = models.EmailField()

    def __str__(self):              # __unicode__ on Python 2
        return self.name

class Entry(models.Model):
    blog = models.ForeignKey(Blog)
    headline = models.CharField(max_length=255)
    body_text = models.TextField()
    pub_date = models.DateField()
    mod_date = models.DateField()
    authors = models.ManyToManyField(Author)
    n_comments = models.IntegerField()
    n_pingbacks = models.IntegerField()
    rating = models.IntegerField()

    def __str__(self):              # __unicode__ on Python 2
        return self.headline

(1)使用过滤器检索特定对象

但在通常情况下,你往往想要获取的是完整数据集的一个子集。

QuerySet两个最普遍的途径是:

filter(**kwargs)
QuerySet,它包含满足查询参数的对象。
exclude(**kwargs)
QuerySet,它包含满足查询参数的对象。

QuerySet,它可以被存储及反复使用。

QuerySet 需要求值时,Django 才会真正运行这个查询。 

(2)使用get()检索单个对象

filter() 始终给你一个QuerySet,即使只有一个对象满足查询条件 —— 这种情况下,QuerySet将只包含一个元素。

如果你知道只有一个对象满足你的查询,你可以使用Managerget() 方法,它直接返回该对象:

>>> one_entry = Entry.objects.get(pk=1)

字段查询。

Entry.DoesNotExist

MultipleObjectsReturned,它同样是模型类自身的一个属性。

(3)限制QuerySet

LIMIT 子句。

5):

>>> Entry.objects.all()[:5]

下面这条语句返回第6 至第10 个对象(OFFSET 5 LIMIT 5):

>>> Entry.objects.all()[5:10]

不支持负的索引(例如Entry.objects.all()[-1])。

(3)字段查找

>>> Entry.objects.filter(pub_date__lte='2006-01-01')       # <=

像这样:

>>> Entry.objects.filter(blog_id=4)
iexact

所以,查询:

>>> Blog.objects.get(name__iexact="beatles blog")

Blog。

contains

像这样:

Entry.objects.get(headline__contains='Lennon')
startswithendswith
iendswith

(4)跨关联关系的查询

若要跨越关联关系,只需使用关联的模型字段的名称,并使用双下划线分隔,直至你想要的字段:

Entry 对象:

>>> Entry.objects.filter(blog__name='Beatles Blog')

这种跨越可以是任意的深度。

若要引用一个“反向”的关系,只需要使用该模型的小写的名称。

'Lennon':

>>> Blog.objects.filter(entry__headline__contains='Lennon')

选择所有包含同时满足两个条件的entry的blog,这两个条件是headline 包含Lennon 和发表时间是2008 (同一个entry 满足两个条件),我们的代码是:

Blog.objects.filter(entry__headline__contains='Lennon', entry__pub_date__year=2008)

要选择所有这样的blog,有一个entry的headline包含“Lennon”有一个entry发表时间是2008,我们将这样编写:

Blog.objects.filter(entry__headline__contains='Lennon').filter(entry__pub_date__year=2008)

(5)F()

如果你想将模型的一个字段与同一个模型的另外一个字段进行比较该怎么办?

这些引用可以用于查询的filter 中来比较相同模型实例上不同字段之间值的比较。

例如,为了查找comments 数目多于pingbacks 的Entry,我们将构造一个F() 对象来引用pingback 数目,并在查询中使用该F() 对象:

>>> from django.db.models import F
>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks'))   

为了查找comments 数目比pingbacks 两倍还要多的Entry,我们将查询修改为:

>>> Entry.objects.filter(n_comments__gt=F('n_pingbacks') * 2)

为了查询rating 比pingback 和comment 数目总和要小的Entry,我们将这样查询:

>>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))

例如,如要获取author 的名字与blog 名字相同的Entry,我们可以这样查询:

>>> Entry.objects.filter(authors__name=F('blog__name'))

下面的例子将返回发布超过3天后被修改的所有Entry:

>>> from datetime import timedelta
>>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))

(6)PK查找快捷方式

pk ,它表示“primary key” 的意思。

id字段,所以这三个语句是等价的:

>>> Blog.objects.get(id__exact=14) # Explicit form
>>> Blog.objects.get(id=14) # __exact is implied
>>> Blog.objects.get(pk=14) # pk implies id__exact

(7)缓存和QuerySet

理解它是如何工作的将让你编写最高效的代码。

QuerySet 的求值将重用缓存的结果。

QuerySet,对它们求值,然后扔掉它们:

>>> print([e.headline for e in Entry.objects.all()])
>>> print([e.pub_date for e in Entry.objects.all()])

Entry被添加进来或删除掉。

QuerySet并重新使用它:

>>> queryset = Entry.objects.all()
>>> print([p.headline for p in queryset]) # Evaluate the query set.
>>> print([p.pub_date for p in queryset]) # Re-use the cache from the evaluation.

(8)使用Q对象进行复杂查找

对象

这些关键字参数就是上文“字段查询” 中所提及的那些。

Q 查询:

from django.db.models import Q
Q(question__startswith='What')

Q 对象。

Q 查询的“OR” :

Q(question__startswith='Who') | Q(question__startswith='What')

它等同于下面的SQL WHERE 子句:

WHERE question LIKE 'Who%' OR question LIKE 'What%'

Q) 查询:

Q(question__startswith='Who') | ~Q(pub_date__year=2005)

像这样:

Poll.objects.get(
    Q(question__startswith='Who'),
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)

像这样:

Poll.objects.get(
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
    question__startswith='Who',
)

错误例子:

# INVALID QUERY
Poll.objects.get(
    question__startswith='Who',
    Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
)

(9)反向查询

QuerySets,可以用上一节提到的方式进行过滤和操作。

例如:

>>> b = Blog.objects.get(id=1)
>>> b.entry_set.all() # Returns all Entry objects related to Blog.

# b.entry_set is a Manager that returns QuerySets.
>>> b.entry_set.filter(headline__contains='Lennon')
>>> b.entry_set.count()

related_name='entries'),上述示例代码如下所示:

>>> b = Blog.objects.get(id=1)
>>> b.entries.all() # Returns all Entry objects related to Blog.

# b.entries is a Manager that returns QuerySets.
>>> b.entries.filter(headline__contains='Lennon')
>>> b.entries.count()

(10)多对多关系

这些API 的工作方式与上面提到的“方向”一对多关系一样。

'_set' (和一对多关系一样)。

一个例子可以让它更好理解:

e = Entry.objects.get(id=3)
e.authors.all() # Returns all Author objects for this Entry.
e.authors.count()
e.authors.filter(name__contains='John')

a = Author.objects.get(id=5)
a.entry_set.all() # Returns all Entry objects for this Author.

(11)一对一关系

对一关系与多对一关系非常相似。 如果你在模型中定义一个OneToOneField,该模型的实例将可以通过该模型的一个简单属性访问关联的模型。

像这样:

class EntryDetail(models.Model):
    entry = models.OneToOneField(Entry, on_delete=models.CASCADE)
    details = models.TextField()

ed = EntryDetail.objects.get(id=2)
ed.entry # Returns the related Entry object.

Manager表示一个单一的对象而不是对象的集合:

e = Entry.objects.get(id=2)
e.entrydetail # returns the related EntryDetail object

反向关联的关系是如何实现的

Django 的开发人员相信这是对DRY(不要重复你自己的代码)原则的违背,所以Django 只要求你在一端定义关联关系。

但是这怎么可能?因为一个模型类直到其它模型类被加载之后才知道哪些模型类是关联的。

如果关联的模型还没有导入,Django 将保存关联关系的记录并在最终关联的模型导入时添加这些关联关系。

否则,反向的关联关系将不能正确工作。

 8.聚合

这份指南描述通过Django 查询来生成和返回聚合值的方法。

这些模型用来记录多个网上书店的库存。

from django.db import models

class Author(models.Model):
    name = models.CharField(max_length=100)
    age = models.IntegerField()

class Publisher(models.Model):
    name = models.CharField(max_length=300)
    num_awards = models.IntegerField()

class Book(models.Model):
    name = models.CharField(max_length=300)
    pages = models.IntegerField()
    price = models.DecimalField(max_digits=10, decimal_places=2)
    rating = models.FloatField()
    authors = models.ManyToManyField(Author)
    publisher = models.ForeignKey(Publisher)
    pubdate = models.DateField()

class Store(models.Model):
    name = models.CharField(max_length=300)
    books = models.ManyToManyField(Book)
    registered_users = models.PositiveIntegerField()

以下是在上述模型的基础上,进行一般的聚合查询的方法:

# Total number of books.
>>> Book.objects.count()
2452

# Total number of books with publisher=BaloneyPress
>>> Book.objects.filter(publisher__name='BaloneyPress').count()
73

# Average price across all books.
>>> from django.db.models import Avg
>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}

# Max price across all books.
>>> from django.db.models import Max
>>> Book.objects.all().aggregate(Max('price'))
{'price__max': Decimal('81.20')}

# Difference between the highest priced book and the average price of all books.
>>> from django.db.models import FloatField
>>> Book.objects.aggregate(
...     price_diff=Max('price', output_field=FloatField()) - Avg('price')))
{'price_diff': 46.85}

# All the following queries involve traversing the Book<->Publisher
# foreign key relationship backwards.

# Each publisher, each with a count of books as a "num_books" attribute.
>>> from django.db.models import Count
>>> pubs = Publisher.objects.annotate(num_books=Count('book'))
>>> pubs
<QuerySet [<Publisher: BaloneyPress>, <Publisher: SalamiPress>, ...]>
>>> pubs[0].num_books
73

# The top 5 publishers, in order by number of books.
>>> pubs = Publisher.objects.annotate(num_books=Count('book')).order_by('-num_books')[:5]
>>> pubs[0].num_books
1323

(1)通过QuerySet生成聚合

QuerySet 子句来完成。

>>> Book.objects.aggregate(Avg('price'))
{'price__avg': 34.35}

如果你想要为聚合值指定一个名称,可以向聚合子句提供它。

>>> Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 34.35}

所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:

>>> from django.db.models import Avg, Max, Min
>>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}

(2)位QuerySet中每个项目生成聚合

QuerySet中总结每本书的这种关系。

QuerySet中的每个对象都会被注上特定的值。

给图书添加作者数量的注解:

# Build an annotated queryset
>>> from django.db.models import Count
>>> q = Book.objects.annotate(Count('authors'))
# Interrogate the first object in the queryset
>>> q[0]
<Book: The Definitive Guide to Django>
>>> q[0].authors__count
2
# Interrogate the second object in the queryset
>>> q[1]
<Book: Practical Django Projects>
>>> q[1].authors__count
1

你可以在指定注解时,为默认名称提供一个别名:

>>> q = Book.objects.annotate(num_authors=Count('authors'))
>>> q[0].num_authors
2
>>> q[1].num_authors
1

annotate()

要查找每个商店提供的图书的价格范围,您可以使用注释:

>>> from django.db.models import Max, Min
>>> Store.objects.annotate(min_price=Min('books__price'), max_price=Max('books__price'))

Book模型,然后对每本书的价格进行聚合,得出最小值和最大值。

如果您想知道任何商店中可出售的任何图书的最低价格和最高价格,您可以使用汇总:

>>> Store.objects.aggregate(min_price=Min('books__price'), max_price=Max('books__price'))

例如,想得到所有作者当中最小的年龄是多少,就可以这样写:

>>> Store.objects.aggregate(youngest_age=Min('books__authors__age'))

(3)反向

在你所查询的模型的关联模型或者字段上的聚合和注解可以遍历"反转"关系。 关联模型的小写名称和双下划线也用在这里。

'book' 的外键反转关系):

查询所有图书中最旧的那本:

>> Publisher.objects.aggregate(oldest_pubdate=Min('book__pubdate'))

'book'的多对多的反转关系):

>>> Author.objects.annotate(total_pages=Sum('book__pages'))

查询所有图书的平均评分,这些图书由我们存档过的作者所写:

>>> Author.objects.aggregate(average_rating=Avg('book__rating'))

>>> from django.db.models import Count, Avg
>>> Book.objects.filter(name__startswith="Django").annotate(num_authors=Count('authors'))

>>> Book.objects.filter(name__startswith="Django").aggregate(Avg('price'))

(4)过滤注释

exclude() 子句中使用别名。

例如,要得到不止一个作者的图书,可以用:

>>> Book.objects.annotate(num_authors=Count('authors')).filter(num_authors__gt=1)

这个查询首先生成一个注解结果,然后再生成一个作用于注解上的过滤器。

(5)order_by()

annotate()子句的一部分。

QuerySet进行排序:

>>> Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors')

(6)values()

然后为每个唯一组提供注释;在组的所有成员上计算注释。

>>> Author.objects.annotate(average_rating=Avg('book__rating')).values('name', 'average_rating')

9.返回新的QuerySet 方法

Django 提供了一系列 的QuerySet筛选方法,用于改变 QuerySet 返回的结果类型或者SQL查询执行的方式。

filter()

filter(**kwargs)

返回一个新的QuerySet,它包含满足查询参数的对象

exclude()

exclude(**kwargs)

QuerySet,它包含满足给定的查找参数的对象。

下面的示例排除所有pub_date 晚于2005-1-3 且headline 为“Hello” 的记录:

Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3), headline='Hello')

annotate()

annotate(*args, **kwargs)

例如,如果你正在操作一个Blog列表,你可能想知道每个Blog有多少Entry:

>>> from django.db.models import Count
>>> q = Blog.objects.annotate(Count('entry'))
# The name of the first blog
>>> q[0].name
'Blogasaurus'
# The number of entries on the first blog
>>> q[0].entry__count
42

order_by()

order_by(*fields)

order_by 指定特定的排序。

例如:

Entry.objects.filter(pub_date__year=2005).order_by('-pub_date', 'headline')

升序是隐含的。

reverse()

reverse() 将恢复到原有的排序。

如要获取QuerySet 中最后五个元素,你可以这样做:

my_queryset.reverse()[:5]

Django 不支持这种访问模型(从末尾进行切片),因为它不可能利用SQL 高效地实现。

values()

values(*fields, **expressions)

QuerySet,而不是使用模型实例作为一个迭代。

每个字典表示一个对象,键对应于模型对象的属性名称。

values() 与普通的模型对象进行比较:

# This list contains a Blog object.
>>> Blog.objects.filter(name__startswith='Beatles')
<QuerySet [<Blog: Beatles Blog>]>

# This list contains a dictionary.
>>> Blog.objects.filter(name__startswith='Beatles').values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>

如果没有指定字段,每个字典将包含数据库表中所有字段的键和值。

例如:

>>> Blog.objects.values()
<QuerySet [{'id': 1, 'name': 'Beatles Blog', 'tagline': 'All the latest Beatles news.'}]>
>>> Blog.objects.values('id', 'name')
<QuerySet [{'id': 1, 'name': 'Beatles Blog'}]>

values_list(*fields, flat=False)

像这样:

>>> Entry.objects.values_list('id', 'headline')
<QuerySet [(1, 'First entry'), ...]>
>>> from django.db.models.functions import Lower
>>> Entry.objects.values_list('id', Lower('headline'))
<QuerySet [(1, 'first entry'), ...]>

select_related()

select_related(*fields)

它会生成一个复杂的查询并引起性能的损耗,但是在以后使用外键关系时将不需要数据库查询。

defer()

defer(*fields)

当你最初获取数据时不知道是否需要这些特定字段的情况下,如果你正在使用查询集的结果,你可以告诉Django不要从数据库中检索它们。

defer()实现不加载:

# 延迟body和headline两个字段。
Entry.objects.defer("body").filter(rating=5).defer("headline")

如果要清除延迟字段集,请将None作为参数传递到defer()

# 立即加载所有的字段。
my_queryset.defer(None)

only()

only(*fields)

only()指定补充的字段集可以导致更简单的代码。

10.不返回QuerySet的方法

get()

get(**kwargs)

返回按照查询参数匹配到的对象

 

count()

count()

count() 永远不会引发异常。

in_bulk()

in_bulk(id_list=None)

如果未提供列表,则会返回查询集中的所有对象。

例如:

>>> Blog.objects.in_bulk([1])
{1: <Blog: Beatles Blog>}
>>> Blog.objects.in_bulk([1, 2])
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>}
>>> Blog.objects.in_bulk([])
{}
>>> Blog.objects.in_bulk()
{1: <Blog: Beatles Blog>, 2: <Blog: Cheddar Talk>, 3: <Blog: Django Weblog>}

latest()

latest(field_name=None)

field_name,按日期返回表中的最新对象。

pub_date:

Entry.objects.latest('pub_date')

first()

first()

QuerySet 没有设置排序,则将会自动按主键进行排序

p = Article.objects.order_by('title', 'pub_date').first()

last()

last()

first(),只是返回的是查询集中最后一个对象。

aggregate()

aggregate(*args, **kwargs)

aggregate() 的每个参数指定返回的字典中将要包含的值。

exists()

exists()

False

 

11.Field查找

exact

精确匹配。

iexact

不区分大小写的精确匹配

contains

大小写敏感的包含关系测试。

例如:

Entry.objects.get(headline__contains='Lennon')

icontains

测试是否包含,不区分大小写。

in

在给定的列表。

例如:

Entry.objects.filter(id__in=[1, 3, 4])

gt

大于

例如:

Entry.objects.filter(id__gt=4)

gte

大于或等于

lt

小于

lte

小于或等于

startswith

区分大小写,开始位置匹配

例如:

Entry.objects.filter(headline__startswith='Lennon')

istartswith

不区分大小写,开始位置匹配

endswith

区分大小写。

iendswith

不区分大小写。

date

获取日期值。

例如:

Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))

year

整数年。

Entry.objects.filter(pub_date__year=2005)
Entry.objects.filter(pub_date__year__gte=2005)

month

对于日期和日期时间字段,确切的月份匹配。

day

对于日期和日期时间字段,具体到某一天的匹配。

 12.管理器

Manager。

QuerySet。

(1)修改管理器的初始QuerySet

例如,使用下面这个模型:

from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=50)

Book.objects.all()将返回数据库中的所有书籍。

QuerySet

Manager,一个返回所有的对象,另一个则只返回作者是Roald Dahl 的对象:

Book.objects.all()将返回数据库中的所有书籍。

QuerySet

Manager,一个返回所有的对象,另一个则只返回作者是Roald Dahl 的对象:

# 首先,定义管理器的子类。
class DahlBookManager(models.Manager):
    def get_queryset(self):
        return super(DahlBookManager, self).get_queryset().filter(author='Roald Dahl')

# 然后将它显式地放入Book模型。
class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=50)

    objects = models.Manager() # 默认的管理器。
    dahl_objects = DahlBookManager() # 用于Dahl的管理器。

Book.dahl_objects.all()只返回Roald Dahl写作的图书。

所以下面这些例子都是可用的:

Book.dahl_objects.all()
Book.dahl_objects.filter(title='Matilda')
Book.dahl_objects.count()

这是给模型添加通用过滤器(选择器)的一个简单方法:

像这样:

class AuthorManager(models.Manager):
    def get_queryset(self):
        return super(AuthorManager, self).get_queryset().filter(role='A')

class EditorManager(models.Manager):
    def get_queryset(self):
        return super(EditorManager, self).get_queryset().filter(role='E')

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    role = models.CharField(max_length=1, choices=(('A', _('Author')), ('E', _('Editor'))))
    people = models.Manager()
    authors = AuthorManager()
    editors = EditorManager()

这个例子让你可以使用Person.authors.all()Person.editors.all()以及Person.people.all(),都会得到预期的结果

(2)从管理器调用自定义QuerySet方法

虽然大多数标准QuerySet的方法可以从Manager中直接访问到,但是如果你需要将一些被定义到一个自定义QuerySet中的额外方法也在Manager上实现,下面所展示的这个例子是唯一可行办法:

class PersonQuerySet(models.QuerySet):
    def authors(self):
        return self.filter(role='A')

    def editors(self):
        return self.filter(role='E')

class PersonManager(models.Manager):
    def get_queryset(self):
        return PersonQuerySet(self.model, using=self._db)

    def authors(self):
        return self.get_queryset().authors()

    def editors(self):
        return self.get_queryset().editors()

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    role = models.CharField(max_length=1, choices=(('A', _('Author')), ('E', _('Editor'))))
    people = PersonManager()

这个例子允许你直接从管理器Person.people中调用authors()editors()

13.Meta选项

abstract

abstract

抽象基类。

app_label

app_label

INSTALLED_APPS没有声明,则必须使用app_lable声明其属于哪个app:

app_label = 'myapp'

model._meta.label_lower。

base_manager_name

base_manager_name
Django中的新功能1.10。

_base_manager所使用的manager的名称(模型管理器的名称)。

db_table

db_table

该模型所用的数据表的名称:

db_table = 'music_album'

Table names

startapp 中使用的名称 – 和 model 的类名称,加上一个下划线在他们之间来构成。

bookstore_book 。

db_table 参数来重写数据表的名称。

如果你的数据库表名称是SQL保留字,或包含Python变量名称中不允许的字符,特别是连字符 — 没有问题。 Django在后台引用列和表名。

db_tablespace

db_tablespace

如果后端并不支持表空间,这个选项可以忽略。

default_manager_name

default_manager_name
Django中的新功能1.10。

_default_manager用到的管理器的名称。

default_related_name

default_related_name

<model_name>_set

related_query_name。

抽象模型的关联名称

旧式查询名称已弃用:

from django.db import models

class Foo(models.Model):
    pass

class Bar(models.Model):
    foo = models.ForeignKey(Foo)

    class Meta:
        default_related_name = 'bars'
>>> bar = Bar.objects.get(pk=1)
>>> # 使用名称"bar"作为查询名称已弃用。
>>> Foo.objects.get(bar=bar)
>>> # 你应该使用default_related_name "bars"。
>>> Foo.objects.get(bars=bar)

get_latest_by

get_latest_by

earliest()中使用的默认字段。

例如:

get_latest_by = "order_date"

managed

managed

换句话说,Django会管理这些数据表的生命周期。

这包括:

  1. 为了避免给后面的代码读者带来混乱,当你在使用未被管理的模型时,强烈推荐你指定(specify)数据表中所有的列。

  2. 但是,一个被管理模型和一个未被管理模型之间的中介表就会被创建。

    ManyToManyField.through为你的自定义模型创建关联。

managed=False),那么在测试之前,你应该要确保在 测试启动时 已经创建了正确的数据表。

Proxy models.

order_with_respect_to

order_with_respect_to

Question相关联,一个问题有至少一个答案,并且答案的顺序非常重要,你可以这样做:

from django.db import models

class Question(models.Model):
    text = models.TextField()
    # ...

class Answer(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    # ...

    class Meta:
        order_with_respect_to = 'question'

Answer对象的主键:

>>> question = Question.objects.get(id=1)
>>> question.get_answer_order()
[1, 2, 3]

Question对象相关联的Answer对象的顺序,可以通过传入一个包含Answer主键的列表来设置:

>>> question.set_answer_order([3, 1, 2])

id来排序:

>>> answer = Answer.objects.get(id=2)
>>> answer.get_next_in_order()
<Answer: 3>
>>> answer.get_previous_in_order()
<Answer: 1>

ordering

ordering

对象默认的顺序,在获取对象的列表时使用:

ordering = ['-order_date']

使用字符串“?”来随机排序。

pub_date字段的正序排序,这样写:

ordering = ['pub_date']

按照pub_date字段的倒序排序,这样写:

ordering = ['-pub_date']

先按照pub_date的倒序排序,再按照 author 的正序排序,这样写:

ordering = ['-pub_date', 'author']

permissions

permissions

can_deliver_pizzas

permissions = (("can_deliver_pizzas", "Can deliver pizzas"),)

human_readable_permission_name)。

default_permissions

default_permissions

migrate命令创建之前,这个属性必须被指定,以防一些遗漏的属性被创建。

proxy

proxy

proxy model。

required_db_features

required_db_features

避免与ORM无关的模型之间的关系。

required_db_vendor

required_db_vendor

如果此属性不为空,并且当前连接供应商不匹配,则该模型将不会同步。

select_on_save

select_on_save

INSERT操作,即使这一行已经在数据库中存在。

False

django.db.models.Model.save()。

indexes

indexes
Django中的新功能1.11。

索引的列表:

from django.db import models

class Customer(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)

    class Meta:
        indexes = [
            models.Index(fields=['last_name', 'first_name']),
            models.Index(fields=['first_name'], name='first_name_idx'),
        ]

unique_together

unique_together

用来设置的不重复的字段组合:

unique_together = (("driver", "restaurant"),)

UNIQUE语句)。

为了方便起见,处理单一字段的集合时,unique_together 可以是一维的元组:

unique_together = ("driver", "restaurant")

through模型。

ValidationError异常。

index_together

index_together
index_together = [
    ["pub_date", "deadline"],
]

INDEX语句中被使用)。

index_together可以只用一个中括号。也就是只用一个一维列表。

index_together = ["pub_date", "deadline"]

verbose_name

verbose_name

对象的一个易于理解的名称,为单数:

verbose_name = "pizza"

case,

verbose_name_plural

verbose_name_plural

该对象复数形式的名称:

verbose_name_plural = "stories"

Meta属性

label

label

'polls.Question'。

label_lower

label_lower

'polls.question'。

视图层

1.URL配置

当一个用户请求Django 站点的一个页面,下面是Django 系统决定执行哪个Python 代码遵循的算法:

  1. ROOT_URLCONF设置。
  2. django.conf.urls.url() 实例的一个Python 列表。
  3. Django 依次匹配每个URL 模式,在与请求的URL 匹配的第一个模式停下来。
  4. 一旦正则表达式匹配,Django将导入并调用给定的视图,该视图是一个简单的Python函数(或基于类的class-based view)。 视图将获得如下参数:
    • HttpRequest 实例。
    • 如果匹配的正则表达式返回了没有命名的组,那么正则表达式匹配的内容将作为位置参数提供给视图。
    • kwargs覆盖。
  5. 错误处理。

(1)示例

下面是一个简单的 URLconf:

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^articles/2003/$', views.special_case_2003),
    url(r'^articles/([0-9]{4})/$', views.year_archive),
    url(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),                  # /articles/2005/03/
    url(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),        # /articles/2003/03/03/ 
]

注:

  • 只需要在它周围放置一对圆括号。
  • ^/articles

一些请求的例子:

  • '03')
  • /articles/2005/3/ 不匹配任何URL 模式,因为列表中的第三个模式要求月份应该是两个数字。
  • views.special_case_2003(request)
  • /articles/2003 不匹配任何一个模式,因为每个模式要求URL 以一个斜线结尾。
  • '03')

(2)命名组

在更高级的用法中,可以使用命名的正则表达式组来捕获URL 中的值并以关键字 参数传递给视图。

pattern 是要匹配的模式。

下面是以上URLconf 使用命名组的重写:

from django.conf.urls import url

from . import views

urlpatterns = [
    url(r'^articles/2003/$', views.special_case_2003),
    url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
    url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive),
    url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.article_detail),
]

像这样:

  • /articles/2005/03/ 请求将调用views.month_archive(request, year='2005', month='03')函数,而不是views.month_archive(request, '2005', '03')
  • /articles/2003/03/03/ 请求将调用函数views.article_detail(request, year='2003', month='03', day='03')

当然,这些好处是以简洁为代价的;一些开发人员发现命名组语法丑陋而且太冗长。

捕获的参数总是字符串

 

例如,下面这行URLconf 中:

url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive)
year参数将是一个字符串,
[0-9]{4}只匹配整数字符串。

(3)传递额外的参数来查看函数

URLconfs 具有一个钩子,让你传递一个Python 字典作为额外的参数传递给视图函数。

django.conf.urls.url() 函数可以接收一个可选的第三个参数,它是一个字典,表示想要传递给视图函数的额外关键字参数。

像这样:

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}),
]

在这个例子中,对于/blog/2005/请求,Django 将调用views.year_archive(request, year='2005', foo='bar')

(4)URL的反向解析

或者用于在服务器端处理导航流程(重定向等)

或者参照 URL 配置创造一种生成 URL 的机制,因为这样非常容易导致线上 URL 失效。

这种机制的一个优点是,当改进 URL 设计之后无需在项目源码中大范围搜索、替换失效的 URL。

必须参与正确URL查找的其他信息片段是视图参数的类型(位置,关键字)和值。

我们为其提供 URL 配置,然后就可以双向使用:

  • 根据用户/浏览器发起的URL 请求,它调用正确的Django 视图,并从URL 中提取它的参数需要的值。
  • 根据Django 视图的标识和将要传递给它的参数的值,获取与之关联的URL。

第二种方式叫做反向解析URL反向URL匹配反向URL查询或者简单的URL反查

在需要URL 的地方,对于不同层级,Django 提供不同的工具用于URL 反查:

  • url 模板标签。
  • reverse()函数。
  • get_absolute_url() 方法。

2.视图函数

views.py的文件中。

(1)Django的快捷函数

render()

[source]

HttpResponse 对象。

render() 提供的便利是一个层次的。

必需参数

request
该request用于生成response
template_name
template loading documentation

可选参数

context
如果字典中的某个值是可调用的,视图将在渲染模板之前调用它。
content_type
DEFAULT_CONTENT_TYPE设置的值。
status
200
using
NAME。

render_to_response()

[source]

不推荐,以后可能会被弃用

redirect()

[source]

HttpResponseRedirect 给正确的URL 。

参数可以是:

  • get_absolute_url() 函数
  • reverse()将用于反向解析名称。
  • 一个绝对的或相对的URL,将原封不动的作为重定向的位置。

permanent=True发出永久重定向。

get_object_or_404()

[source]

DoesNotExist 异常。

必需参数

klass
QuerySet 实例。
**kwargs
filter()接受。

实例

下面的示例从MyModel 中使用主键1 来获取对象:

from django.shortcuts import get_object_or_404

def my_view(request):
    my_object = get_object_or_404(MyModel, pk=1)

 2.基于类的视图

它们不替换基于函数的视图,但与基于函数的视图相比具有一定的区别和优势:

  • 可以通过单独的方法而不是条件分支来解决。
  • 面向对象的技术例如Mixin(多继承)可以将代码分解成可重用的组件。

基于类的视图的核心是允许你用不同的实例方法来响应不同的HTTP 请求方法,而不是在一个视图函数中使用条件分支代码来实现。

GET 的代码看上去将像:

from django.http import HttpResponse

def my_view(request):
    if request.method == 'GET':
        # <view logic>
        return HttpResponse('result')

在基于类的视图中,它将变成:

from django.http import HttpResponse
from django.views import View

class MyView(View):
    def get(self, request):
        # <view logic>
        return HttpResponse('result')

HttpResponseNotAllowed

# urls.py
from django.conf.urls import url
from myapp.views import MyView

urlpatterns = [
    url(r'^about/$', MyView.as_view()),
]

TemplateResponse 对象。

虽然基于类的视图的最小实现不需要任何类属性来完成它的功能,但是在许多基于类的设计中类属性非常重要,有两种方式来设置类属性。

greeting 属性:

from django.http import HttpResponse
from django.views import View

class GreetingView(View):
    greeting = "Good Day"

    def get(self, request):
        return HttpResponse(self.greeting)

你可以在子类中覆盖它:

class MorningGreetingView(GreetingView):
    greeting = "Morning to ya"

另外一种方式是在URLconf 中用as_view() 调用的关键字参数配置类的属性:

urlpatterns = [
    url(r'^about/$', GreetingView.as_view(greeting="good day")),
]

3.Mixins的使用

Mixin 是多继承的一种形式,其来自多个父类的行为和属性可以组合在一起。

View 中定义的行为)。

代码在Mixin 中越分散,子类将越难阅读并知道它的行为;如果你的继承很深,将难以知道应该覆盖哪一个Mixin 的方法。

View 的类 将不能像预期的那样工作

一个最基本的用于处理表单的视图函数可能是这样的:

from django.http import HttpResponseRedirect
from django.shortcuts import render

from .forms import MyForm

def myview(request):
    if request.method == "POST":
        form = MyForm(request.POST)
        if form.is_valid():
            # <process form cleaned data>
            return HttpResponseRedirect('/success/')
    else:
        form = MyForm(initial={'key': 'value'})

    return render(request, 'form_template.html', {'form': form})

类似的一个基于类的视图看上去是这样:

from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.views import View

from .forms import MyForm

class MyFormView(View):
    form_class = MyForm
    initial = {'key': 'value'}
    template_name = 'form_template.html'

    def get(self, request, *args, **kwargs):
        form = self.form_class(initial=self.initial)
        return render(request, self.template_name, {'form': form})

    def post(self, request, *args, **kwargs):
        form = self.form_class(request.POST)
        if form.is_valid():
            # <process form cleaned data>
            return HttpResponseRedirect('/success/')

        return render(request, self.template_name, {'form': form})

)。

更多-->>http://usyiyi.cn/translate/Django_111/topics/class-based-views/mixins.html

4.装饰基于类的视图

as_view() 还是创建一个子类。

(1)在URLconf中进行装饰

最方便的地方是URLconf 中部署视图的位置:

from django.contrib.auth.decorators import login_required, permission_required
from django.views.generic import TemplateView

from .views import VoteView

urlpatterns = [
    url(r'^about/$', login_required(TemplateView.as_view(template_name="secret.html"))),
    url(r'^vote/$', permission_required('polls.can_vote')(VoteView.as_view())),
]

如果想让视图的每个实例都被装饰,你需要一种不同的方法。

(2)装饰类

dispatch() 方法上来实现这点。

像这样:

from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import TemplateView

class ProtectedView(TemplateView):
    template_name = 'secret.html'

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super(ProtectedView, self).dispatch(*args, **kwargs)

或者,更简洁的是,您可以装饰类,并将要装饰的方法的名称作为关键字参数name传递:

@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
    template_name = 'secret.html'

这两个类是相当的:

decorators = [never_cache, login_required]

@method_decorator(decorators, name='dispatch')
class ProtectedView(TemplateView):
    template_name = 'secret.html'

@method_decorator(never_cache, name='dispatch')
@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
    template_name = 'secret.html'

login_required()之前处理请求。

5.中间件

它是一个轻量级、底层的“插件”系统,用于在全局修改Django 的输入或输出。

AuthenticationMiddleware ,它使用会话将用户和请求关联起来。

(1)编写自己的中间件

中间件是一个可调用的函数,它接受请求并返回响应,就像视图一样。

中间件可以写成一个如下所示的功能:

def simple_middleware(get_response):
    # 一次性配置和初始化。

    def middleware(request):
        # 在调用视图(以及稍后的中间件)之前
        # 要为每个请求执行代码。

        response = get_response(request)

        # 为每个请求/响应执行的代码
        # 在调用视图之后

        return response

    return middleware

或者它可以写成一个类,其实例是可调用的,如下所示:

class SimpleMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response
        # 一次性配置和初始化。

    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.

        response = self.get_response(request)

        # Code to be executed for each request/response after
        # the view is called.

        return response

__init__(get_response)

记住几个注意事项:

  • __init__()定义为需要任何其他参数。
  • __init__()仅被调用一次

(2)激活中间件

MIDDLEWARE列表中。

startproject创建工程的时候生成的默认值:

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

(3)中间件顺序和分层

MIDDLEWARE(自上而下)定义的顺序应用中间件。

get_response将请求传递到下一层),一直到核心的视图,响应将通过在每一层(以相反的顺序)的路上退出。

响应将只返回通过请求传递的相同的层。

(4)其它中间件钩子

除了前面描述的基本请求/响应中间件模式,您还可以向基于类的中间件添加三种其他特殊方法:

process_view()

process_view(requestview_funcview_argsview_kwargs

request)。

process_view()会在Django 调用视图之前被调用。

HttpResponse并返回结果。

process_exception()

process_exception(requestexception

exception对象。

default exception handling开始。

process_exception方法根本就不会被调用。

process_template_response()

process_template_response(请求响应

TemplateResponse对象(或等价的对象),由Django视图或者中间件返回。

TemplateResponse对象(或等价的对象)。

TemplateResponse或等价的对象。

你不需要显式渲染响应 —— 一旦所有的模板响应中间件被调用,响应会自动被渲染。

process_template_response()。

 模板层

 1.配置

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')]
        ,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

django.template.backends.jinja2.Jinja2

由于绝大多数引擎都是从文件加载模板的,所以每种模板引擎都包含两项通用设置:

  • DIRS 定义了一个目录列表,模板引擎按列表顺序搜索这些目录以查找模板源文件。
  • 每种模板引擎后端都定义了一个惯用的名称作为应用内部存放模板的子目录名称。(译者注:例如django为它自己的模板引擎指定的是 ‘templates’ ,为jinja2指定的名字是‘jinja2’)

NAME .

OPTIONS 中包含了具体的backend设置

2.模板语言

模板

)。

模版包括在使用时会被值替换掉的 变量,和控制模版逻辑的 标签

后面的文档中会解释每个元素。

{% extends "base_generic.html" %}

{% block title %}{{ section.title }}{% endblock %}

{% block content %}
<h1>{{ section.title }}</h1>

{% for story in story_list %}
<h2>
  <a href="{{ story.get_absolute_url }}">
    {{ story.headline|upper }}
  </a>
</h2>
<p>{{ story.tease|truncatewords:"100" }}</p>
{% endfor %}
{% endblock %}

变量

section 属性。

过滤器

您可以通过使用 过滤器来改变变量的显示。

|)来应用过滤器。

<p> 标签。

bio 变量的前30个词。

}}。

常用模板过滤器

默认

像这样:

{{ value|default:"nothing" }}

如果 value没有被提供,或者为空, 上面的例子将显示“nothing”。

长度

像这样:

{{ value|length }}

如果 value['a', 'b', 'c', 'd'],那么输出是 4

filesizeformat

像这样:

{{ value|filesizeformat }}

如果 value 是 123456789,输出将会是 117.7 MB

更多-->>http://usyiyi.cn/translate/Django_111/ref/templates/builtins.html#ref-templates-builtins-filters

标签

标签比变量复杂得多:有些用于在输出中创建文本,有些用于控制循环或逻辑,有些用于加载外部信息到模板中供以后的变量使用。

)。

常用标签,更多-->>http://usyiyi.cn/translate/Django_111/ref/templates/builtins.html#ref-templates-builtins-tags

对于athlete_list中提供的运动员列表:

for

<ul>
{% for athlete in athlete_list %}
    <li>{{ athlete.name }}</li>
{% endfor %}
</ul>
变量     描述
forloop.counter     循环的当前迭代(1索引)
forloop.counter0     循环的当前迭代(0索引)
forloop.revcounter     循环结束的迭代次数(1索引)
forloop.revcounter0     循环结束的迭代次数(0索引)
forloop.first     如果这是第一次通过循环,则为真
forloop.last     如果这是最后一次循环,则为真
forloop.parentloop     对于嵌套循环,这是围绕当前循环的循环

if,elif和else

计算一个变量,并且当变量是“true”时,显示块中的内容:

{% if athlete_list %}
    Number of athletes: {{ athlete_list|length }}
{% elif athlete_in_locker_room_list %}
    Athletes should be out of the locker room soon!
{% else %}
    No athletes.
{% endif %}

如果两个列表都是空的,将显示 “No athletes.” 。

block

comment

比如,当要注释掉一些代码时,可以用此来记录代码被注释掉的原因。

例如:

<p>Rendered text with {{ pub_date|date:"c" }}</p>
{% comment "Optional note" %}
    <p>Commented out text with {{ create_date|date:"c" }}</p>
{% endcomment %}

comment标签不能嵌套使用。

csrf_token

这个标签用于跨站请求伪造保护

extends

表示当前模板继承自一个父模板

注释

#}.

'hello':

{# greeting #}hello

3.模板继承

模版继承可以让您创建一个基本的“骨架”模版,它包含您站点中的全部元素,并且可以定义能够被子模版覆盖的 blocks

通过从下面这个例子开始,可以容易的理解模版继承:

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="style.css" />
    <title>{% block title %}My amazing site{% endblock %}</title>
</head>

<body>
    <div id="sidebar">
        {% block sidebar %}
        <ul>
            <li><a href="/">Home</a></li>
            <li><a href="/blog/">Blog</a></li>
        </ul>
        {% endblock %}
    </div>

    <div id="content">
        {% block content %}{% endblock %}
    </div>
</body>
</html>

“子模版”的工作是用它们的内容填充空的blocks。

block 告诉模版引擎: 子模版可能会覆盖掉模版中的这些位置。

子模版可能看起来是这样的:

{% extends "base.html" %}

{% block title %}My amazing blog{% endblock %}

{% block content %}
{% for entry in blog_entries %}
    <h2>{{ entry.title }}</h2>
    <p>{{ entry.body }}</p>
{% endfor %}
{% endblock %}

当模版系统处理这个模版时,首先,它将定位父模版——在此例中,就是“base.html”。

block 标签,并用子模版中的内容来替换这些block。

%} 标签中的内容总是被用作备选内容(fallback)。

使用继承的一个常用方式是类似下面的三级结构:

  • base.html 模版来控制您整个站点的主要视觉和体验。
  • base.html ,并且包含了每部分特有的样式和设计。
  • 这些模版继承对应分支的模版。

这种方式使代码得到最大程度的复用,并且使得添加内容到共享的内容区域更加简单,例如分支范围内的导航。

这里是使用继承的一些提示:

  • 其他的任何情况下,模版继承都将无法工作。

  • 多一点钩子总比少一点好。

  • %} 中。

  • }} 插入的数据不会被自动转义,因为父模板中的内容已经被转义。

  • 例如,此模板不会显示任何内容:

block 标签,模版的父模版将不知道使用哪个block的内容。

include

这是一种可以引入别的模板的方法。

模板名可以是变量或者是硬编码的字符串,可以用单引号也可以是双引号.

的内容:

{% include "foo/bar.html" %}

load

加载自定义模板标签集。

somelibrary 中已经注册的标签和过滤器:

{% load somelibrary package.otherlibrary %}

4.自动html转义

例如,思考这个模版片段:

Hello, {{ name }}

首先,它看起来像是一个无害的方式来显示用户的名字,但是设想一下,如果用户像下面这样输入他的名字,会发生什么:

<script>alert('hello')</script>

...这意味着浏览器会弹出一个JavaScript警报框!

跨站脚本(Cross Site Scripting) (XSS) 攻击。

为避免这个问题,你有两个选择:

  • 而且很容易忘记转义数据。
  • 本节其余部分描述自动转义是如何工作的。

明确地说,下面五个字符被转义:

  • < 会转换为&lt;
  • > 会转换为&gt;
  • '(单引号)转换为&#39;
  • " (双引号)会转换为 &quot;
  • & 会转换为 &amp;

如果你使用Django的模板系统,会处于保护之下。

如何关闭

如果你不希望数据自动转义,无论是在站点、模板还是变量级别,你可以使用几种方法来关闭它。

或者,你可能使用Django的模板系统来生成不是HTML的文本 -- 比如邮件信息。

对于单个变量

使用safe过滤器来关闭独立变量上的自动转义:

This will be escaped: {{ data }}
This will not be escaped: {{ data|safe }}

对于模板库

{% autoescape off %}
    Hello {{ name }}
{% endautoescape %}

下面是一个模板的示例

Auto-escaping is on by default. Hello {{ name }}

{% autoescape off %}
    This will not be auto-escaped: {{ data }}.

    Nor this: {{ other_data }}
    {% autoescape on %}
        Auto-escaping applies again: {{ name }}
    {% endautoescape %}
{% endautoescape %}

像这样:

{% autoescape off %}
<h1>{% block title %}{% endblock %}</h1>
{% block content %}
{% endblock %}
{% endautoescape %}

5.自定义模板标签和过滤器

INSTALLED_APPS中时,在下面描述的常规位置中定义的任何标签将自动在模板中加载。

__init__.py 文件以使得该目录可以作为Python 的包。

这个模块的名字是你稍后将要载入标签时使用的,所以要谨慎的选择名字以防与其他应用下的自定义标签和过滤器名字冲突。

poll_extras.py的文件中,那么你的app目录结构看起来应该是这样的:

polls/
    __init__.py
    models.py
    templatetags/
        __init__.py
        poll_extras.py
    views.py

然后你可以在模板中像如下这样使用:

{% load poll_extras %}

(1)编写自定义过滤器

自定义过滤器就是一个带有一个或两个参数的Python 函数:

  • (输入的)变量的值 —— 不一定是字符串形式。
  • 参数的值 —— 可以有一个初始值,或者完全不要这个参数。

在模板中有一个明显错误的情况下,引发一个异常可能仍然要好于用静默的失败来掩盖错误。

这是一个定义过滤器的例子:

def cut(value, arg):
    """Removes all values of arg from the given string"""
    return value.replace(arg, '')

下面是这个过滤器应该如何使用:

{{ somevariable|cut:"0" }}

例如:

def lower(value): # Only one argument.
    """Converts a string into all lowercase"""
    return value.lower()

(2)注册自定义过滤器

filter()

Library实例,来让它在Django模板语言中可用:

register.filter('cut', cut)
register.filter('lower', lower)

Library.filter()方法需要两个参数:

  1. 过滤器的名称(一个字符串对象)
  2. 编译的函数 – 一个Python函数(不要把函数名写成字符串)

register.filter()用作装饰器:

@register.filter(name='cut')
def cut(value, arg):
    return value.replace(arg, '')

@register.filter
def lower(value):
    return value.lower()

更多-->>http://usyiyi.cn/translate/Django_111/howto/custom-template-tags.html

(3)编写自定义模板标签

标签比过滤器更复杂,因为标签可以做任何事情。

简单标签

simple_tag()

current_time 标签可能接受一个格式字符串,并返回与之对应的格式化后的时间。

render 函数和上面提到的其他必要部分中,并在模板系统中注册它。

我们的current_time 函数从而可以这样写

import datetime
from django import template

register = template.Library()

@register.simple_tag
def current_time(format_string):
    return datetime.datetime.now().strftime(format_string)

simple_tag 辅助函数几件值得注意的事项︰

  • 检查所需参数的数量等等,在我们的函数调用的时刻已经完成,所以我们不需要做了。
  • 参数(如果有)的引号都已经被截掉,所以我们收到的只是一个普通字符串。
  • 如果该参数是一个模板变量,传递给我们的函数是当前变量的值,不是变量本身。

conditional_escape() 转义, 来保证正确的HTML和防御XSS漏洞.

mark_safe()

takes_context 参数︰

@register.simple_tag(takes_context=True)
def current_time(context, format_string):
    timezone = context['timezone']
    return your_get_current_time_method(timezone, format_string)

 表单

GET和POST

GET 方法。

POST 方法,在这个方法中浏览器组合表单数据、对它们进行编码以用于传输、将它们发送到服务器然后接收它的响应。

https://docs.djangoproject.com/search/?q=forms&release=1 形式的URL。

GET 用于不同的目的。

GET 只应该用于不会影响系统状态的请求。

CSRF protection

GET 请求的URL 可以很容易地作为书签、分享和重新提交。

Django在表单中的角色

考虑一下Django 的Admin 站点,不同类型的大量数据项需要在一个表单中准备好、渲染成HTML、使用一个方便的界面编辑、返回给服务器、验证并清除,然后保存或者向后继续处理。

Django 的表单功能可以简化并自动化大部分这些工作,而且还可以比大部分程序员自己所编写的代码更安全。

Django 会处理表单工作中的三个显著不同的部分:

  • 准备数据、重构数据,以便下一步提交。
  • 为数据创建HTML 表单
  • 接收并处理客户端提交的表单和数据

可以手工编写代码来实现,但是Django 可以帮你完成所有这些工作。

Django的Form类

Form 类描述一个表单并决定它如何工作和展现。

<input> 元素;Django 的Admin 站点就是基于这个)。

FileField 处理的数据类型差别很大,必须完成不同的事情。

Widget class,需要时可以覆盖。

实例化、处理和渲染表单

在Django 中渲染一个对象时,我们通常:

  1. 在视图中获得它(例如,从数据库中获取)
  2. 将它传递给模板的context
  3. 使用模板变量将它扩展为HTML 标记

除了几个关键点不同之外,在模板中渲染表单和渲染其它类型的对象几乎一样。

但是渲染一个未填充的表单却非常有意义 —— 我们希望用户去填充它。

当我们处理表单时,我们一般在视图中实例化它。

当我们实例化表单时,我们可以选择让它为空还是预先填充它,例如使用:

  • 来自一个保存后的模型实例的数据(例如用于编辑的管理表单)
  • 我们从其它地方获得的数据
  • 从前面一个HTML 表单提交过来的数据

获取HTML表单数据是最有趣的,因为这样做可以让用户不仅可以阅读网站,还可以将信息发送回来。

你需要类似这样的模板:

<form action="/your-name/" method="post">
    <label for="your_name">Your name: </label>
    <input id="your_name" type="text" name="your_name" value="{{ current_name }}">
    <input type="submit" value="OK">
</form>

your_name字段。

current_name字段。

POST 请求将包含表单数据。

/your-name/ URL 的视图,它在请求中找到正确的键/值对,然后处理它们。

实际应用中,一个表单可能包含几十上百个字段,其中大部分需要预填充,而且我们预料到用户将来回编辑-提交几次才能完成操作。

即使在提交表单之前,我们也可能需要在浏览器中进行一些验证。我们可能想要使用更复杂的字段,这样可以让用户做一些事情,例如从日历中选择日期等等。

这个时候,让Django 来为我们完成大部分工作是很容易的。

 1.创建一个表单

(1)form.py

在Django 中,我们的起始点是这里:

from django import forms

class NameForm(forms.Form):
    your_name = forms.CharField(label='Your name', max_length=100)

label还是会自动生成)。

它还意味着当Django 收到浏览器发送过来的表单时,它将验证数据的长度。

当调用这个方法时,如果所有的字段都包含合法的数据,它将:

  • True
  • cleaned_data 属性中。

完整的表单,第一次渲染时,看上去将像:

<label for="your_name">Your name: </label>
<input id="your_name" type="text" name="your_name" maxlength="100" required />

我们必须自己在模板中提供它们。

(2)视图

这允许我们重用一些相同的逻辑。

要操作一个通过URL发布的表单,我们要在视图中实例化它。

from django.shortcuts import render
from django.http import HttpResponseRedirect

from .forms import NameForm

def get_name(request):
    # 如果这是一个POST请求,我们就需要处理表单数据
    if request.method == 'POST':
        # 创建一个表单实例,并且使用表单数据填充request请求:
        form = NameForm(request.POST)
        # 检查数据有效性:
        if form.is_valid():
            # 在需要时,可以在form.cleaned_date中处理数据
            # ...
            # 重定向到一个新的URL:
            return HttpResponseRedirect('/thanks/')

    # 如果是GET或者其它请求方法,我们将创建一个空的表单。
    else:
        form = NameForm()

    return render(request, 'name.html', {'form': form})

这是我们在第一次访问该URL 时预期发生的情况。

NameForm(request.POST)这被称为“将数据绑定到表单”(现在是绑定的形式)。

这时表单不再为空(未绑定),所以HTML 表单将用之前提交的数据填充,然后可以根据要求编辑并改正它。

在发送HTTP 重定向给浏览器告诉它下一步的去向之前,我们可以用这个数据来更新数据库或者做其它处理。

(3)模板

最简单的例子是:

<form action="/your-name/" method="post">
    {% csrf_token %}
    {{ form }}
    <input type="submit" value="Submit" />
</form>

Django会根据模型类的字段和属性,在HTML中自动生成对应表单标签和标签属性。生成的标签会被放置到{{ form }}所在的位置。

<form>。

一旦你理解了上面描述的基本处理过程,你应该可以理解表单系统的其它功能并准备好学习更多的底层机制。

 更多字段

考虑一个比我们上面的最小例子更有用的形式,我们可以用它来在个人网站上实现“联系我”功能:

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)

BooleanField只是三种可用的字段类型

窗口小部件

<textarea> 字段。

字段数据

这些数据已经为你转换好为Python 的类型。

float

下面是在视图中如何处理表单数据:

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/')

2.表单API

(1)绑定和绑定形式

Form要么是绑定的,要么是未绑定的

  • 如果是绑定的,那么它能够验证数据,并渲染表单及其数据成HTML。
  • ),但它仍然可以以HTML形式呈现空白表

若要创建一个未绑定的Form实例,只需简单地实例化该类:

>>> f = ContactForm()

若要绑定数据到表单,可以将数据以字典的形式传递给Form类的构造函数的第一个参数:

>>> data = {'subject': 'hello',
...         'message': 'Hi there',
...         'sender': 'foo@example.com',
...         'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_bound
True

Form实例一旦创建,你应该将它的数据视为不可变的,无论它有没有数据。

(2)使用表单验证数据

sender 是一个不合法的邮件地址:

>>> data = {'subject': '',
...         'message': 'Hi there',
...         'sender': 'invalid email address',
...         'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_valid()
False
errors

errors 属性可以获得错误信息的一个字典:

>>> f.errors
{'sender': ['Enter a valid email address.'], 'subject': ['This field is required.']}
as_data()

ValidationError 实例。

>>> f.errors.as_data()
{'sender': [ValidationError(['Enter a valid email address.'])],
'subject': [ValidationError(['This field is required.'])]}
as_json(escape_html=False)

返回JSON 序列化后的错误。

>>> f.errors.as_json()
{"sender": [{"message": "Enter a valid email address.", "code": "invalid"}],
"subject": [{"message": "This field is required.", "code": "required"}]}

(3)访问干净数据

cleaned_data

这是个非常好用的功能,因为它允许字段以多种方式输入数据,并总能得到一致的输出。

datetime.date 对象,只要它们是合法的。

cleaned_data 属性访问清洁的数据:

>>> data = {'subject': 'hello',
...         'message': 'Hi there',
...         'sender': 'foo@example.com',
...         'cc_myself': True}
>>> f = ContactForm(data)
>>> f.is_valid()
True
>>> f.cleaned_data
{'cc_myself': True, 'message': 'Hi there', 'sender': 'foo@example.com', 'subject': 'hello'}

(4)输出表单为HTML

as_p()

as_p()

as_p() 标签包含一个字段:

>>> f = ContactForm()
>>> f.as_p()
'<p><label for="id_subject">Subject:</label> <input >'
>>> print(f.as_p())
<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required /></p>
<p><label for="id_message">Message:</label> <input type="text" name="message" id="id_message" required /></p>
<p><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required /></p>
<p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself" /></p>

as_ul()

as_ul()

<ul> 的任何HTML 属性:

as_table()

as_table T0>()

as_table() 方法:

3.表单字段

[source]

每个字段都可以有自定义的验证逻辑,以及一些其它的钩子。

[source]

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.']

核心字段参数

Field类接受额外的、字段特有的参数,但以下参数应该总是能接受:

required

required

ValidationError 异常:

>>> from django import forms
>>> f = forms.CharField()
>>> f.clean('foo')
'foo'
>>> f.clean('')
Traceback (most recent call last):
...
ValidationError: ['This field is required.']
>>> f.clean(None)
Traceback (most recent call last):
...
ValidationError: ['This field is required.']
>>> f.clean(' ')
' '
>>> f.clean(0)
'0'
>>> f.clean(True)
'True'
>>> f.clean(False)
'False'

若要表示一个字段是必需的,请传递Fieldrequired=False 的构造函数:

>>> f = forms.CharField(required=False)
>>> f.clean('foo')
'foo'
>>> f.clean('')
''
>>> f.clean(None)
''
>>> f.clean(0)
'0'
>>> f.clean(True)
'True'
>>> f.clean(False)
'False'

(每个字段各不相同)。

label

label

Form中显示时将用到它。

label

auto_id=False来让输出简单一些:

>>> from django import forms
>>> class CommentForm(forms.Form):
...     name = forms.CharField(label='Your name')
...     url = forms.URLField(label='Your website', required=False)
...     comment = forms.CharField()
>>> f = CommentForm(auto_id=False)
>>> print(f)
<tr><th>Your name:</th><td><input type="text" name="name" required /></td></tr>
<tr><th>Your website:</th><td><input type="url" name="url" /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" required /></td></tr>

label_suffix

label_suffix

label_suffix:

>>> class ContactForm(forms.Form):
...     age = forms.IntegerField()
...     nationality = forms.CharField()
...     captcha_answer = forms.IntegerField(label='2 + 2', label_suffix=' =')
>>> f = ContactForm(label_suffix='?')
>>> print(f.as_p())
<p><label for="id_age">Age?</label> <input id="id_age" name="age" type="number" required /></p>
<p><label for="id_nationality">Nationality?</label> <input id="id_nationality" name="nationality" type="text" required /></p>
<p><label for="id_captcha_answer">2 + 2 =</label> <input id="id_captcha_answer" name="captcha_answer" type="number" required /></p>

initial

initial

initial时使用的初始值。

Form.initial 参数。

像这样:

>>> from django import forms
>>> class CommentForm(forms.Form):
...     name = forms.CharField(initial='Your name')
...     url = forms.URLField(initial='http://')
...     comment = forms.CharField()
>>> f = CommentForm(auto_id=False)
>>> print(f)
<tr><th>Name:</th><td><input type="text" name="name" value="Your name" required /></td></tr>
<tr><th>Url:</th><td><input type="url" name="url" value="http://" required /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" required /></td></tr>

widget

widget

widget

help_text

help_text

as_ul()显示。

help_text一样,此值不会以自动生成的形式进行HTML转义。

auto_id=False来让输出简单一些:

>>> from django import forms
>>> class HelpTextContactForm(forms.Form):
...     subject = forms.CharField(max_length=100, help_text='100 characters max.')
...     message = forms.CharField()
...     sender = forms.EmailField(help_text='A valid email address, please.')
...     cc_myself = forms.BooleanField(required=False)
>>> f = HelpTextContactForm(auto_id=False)
>>> print(f.as_table())
<tr><th>Subject:</th><td><input type="text" name="subject" maxlength="100" required /><br /><span class="helptext">100 characters max.</span></td></tr>
<tr><th>Message:</th><td><input type="text" name="message" required /></td></tr>
<tr><th>Sender:</th><td><input type="email" name="sender" required /><br />A valid email address, please.</td></tr>
<tr><th>Cc myself:</th><td><input type="checkbox" name="cc_myself" /></td></tr>
>>> print(f.as_ul()))
<li>Subject: <input type="text" name="subject" maxlength="100" required /> <span class="helptext">100 characters max.</span></li>
<li>Message: <input type="text" name="message" required /></li>
<li>Sender: <input type="email" name="sender" required /> A valid email address, please.</li>
<li>Cc myself: <input type="checkbox" name="cc_myself" /></li>
>>> print(f.as_p())
<p>Subject: <input type="text" name="subject" maxlength="100" required /> <span class="helptext">100 characters max.</span></p>
<p>Message: <input type="text" name="message" required /></p>
<p>Sender: <input type="email" name="sender" required /> A valid email address, please.</p>
<p>Cc myself: <input type="checkbox" name="cc_myself" /></p>

error_messages

error_messages

例如,下面是默认的错误信息:

>>> from django import forms
>>> generic = forms.CharField()
>>> generic.clean('')
Traceback (most recent call last):
  ...
ValidationError: ['This field is required.']

而下面是自定义的错误信息:

>>> name = forms.CharField(error_messages={'required': 'Please enter your name'})
>>> name.clean('')
Traceback (most recent call last):
  ...
ValidationError: ['Please enter your name']

validators

validators

validators 参数让你可以为字段提供一个验证函数的列表。

localize

localize

localize参数可以实现表单数据输入的定位,以及渲染输出。

disabled

disabled

即使用户篡改了提交给服务器的字段的值,它也将被忽略,有利于表单初始数据中的值。

has_changed()

[source]

False

4.内置Field类

BooleanField

[source]
  • CheckboxInput
  • False
  • False。
  • required=True(例如复选框被勾上)。
  • required


[source]
  • TextInput
  • empty_value给出的任何值。
  • 规范化为:一个Unicode 对象。
  • 否则,所有的输入都是合法的。
  • required

有三个可选参数进行验证:

max_length
min_length

如果提供,这两个参数将确保字符串的最大和最小长度。

strip

True(默认),该值将被剥离前导和尾随空格。

empty_value
Django中的新功能1.11。

默认为空字符串。

ChoiceField

[source]
  • Select
  • ''(一个空字符串)
  • 规范化为:一个Unicode 对象。
  • 验证给定的值在选项列表中存在。
  • invalid_choice

%(value)s,它将被选择的选项替换掉。

还有一个参数:

choices

choices 参数相同。

TypedChoiceField

[source]

empty_value。

  • Select
  • empty_value给出的任何值。
  • coerce 参数类型的值。
  • 验证给定的值在选项列表中存在并且可以被强制转换。
  • invalid_choice

接收的额外参数:

coerce

choices 中的值。

empty_value

coerce 参数中指定的函数强制转换,所以请根据情况进行选择。

DateField

[source]
  • DateInput
  • None
  • datetime.date 对象。
  • datetime.datetime 或指定日期格式的字符串。
  • invalid

接收一个可选的参数:

input_formats

datetime.date 对象。

input_formats,默认的输入格式为:

['%Y-%m-%d',      # '2006-10-25'
 '%m/%d/%Y',      # '10/25/2006'
 '%m/%d/%y']      # '10/25/06'

DateTimeField

[source]
  • DateTimeInput
  • None
  • datetime.datetime 对象。
  • datetime.date 或指定日期格式的字符串。
  • invalid

接收一个可选的参数:

input_formats

datetime.datetime 对象。

input_formats,默认的输入格式为:

['%Y-%m-%d %H:%M:%S',    # '2006-10-25 14:30:59'
 '%Y-%m-%d %H:%M',       # '2006-10-25 14:30'
 '%Y-%m-%d',             # '2006-10-25'
 '%m/%d/%Y %H:%M:%S',    # '10/25/2006 14:30:59'
 '%m/%d/%Y %H:%M',       # '10/25/2006 14:30'
 '%m/%d/%Y',             # '10/25/2006'
 '%m/%d/%y %H:%M:%S',    # '10/25/06 14:30:59'
 '%m/%d/%y %H:%M',       # '10/25/06 14:30'
 '%m/%d/%y']             # '10/25/06'
DecimalField 
[source]
  • TextInput。
  • None
  • decimal。
  • 忽略前导和尾随的空白。
  • min_value

%(max)s

接收四个可选的参数:

max_value
min_value

decimal.Decimal 值给出。

max_digits

值允许的最大位数(小数点之前和之后的数字总共的位数,前导的零将被删除)。

decimal_places

允许的最大小数位。

DurationField

[source]
  • TextInput
  • None
  • timedelta。
  • timedelta。
  • invalid.

parse_duration() 理解的格式。

EmailField

[source]
  • EmailInput
  • ''(一个空字符串)
  • 规范化为:一个Unicode 对象。
  • 验证给出的值是一个合法的邮件地址,使用一个适度复杂的正则表达式。
  • invalid

如果提供,这两个参数将确保字符串的最大和最小长度。

FileField

[source]
  • ClearableFileInput
  • None
  • UploadedFile 对象,它封装文件内容和文件名为一个单独的对象。
  • 可以验证非空的文件数据已经绑定到表单。
  • max_length

如果提供,这两个参数确保文件名的最大长度,而且即使文件内容为空时验证也会成功。

FloatField

[source]
  • TextInput。
  • None
  • 规范化为:一个Float 对象。
  • float() 函数一样,允许前导和尾随的空白符。
  • min_value

它们控制字段中允许的值的范围。

IntergerField

[source]
  • TextInput。
  • None
  • 规范化为:一个Python 整数或长整数。
  • int()函数。
  • min_value

max_value,它们将被真正的限制值替换。

采用两个可选参数进行验证:

max_value
min_value

它们控制字段中允许的值的范围。

GenericIPAddressField

[source]

包含IPv4或IPv6地址的字段。

  • TextInput
  • ''(一个空字符串)
  • IPv6地址如下所述进行归一化。
  • 验证给定值是有效的IP地址。
  • invalid

所有字符都转换为小写。

有两个可选参数:

protocol

匹配不区分大小写。

unpack_ipv4

'both'时使用。

MultipleChoiceField

[source]
  • SelectMultiple
  • [](一个空列表)
  • 规范化为:一个Unicode 对象列表。
  • 验证给定值列表中的每个值都存在于选择列表中。
  • required

%(value)s,它将被选择的选项替换掉。

ChoiceField。

TypedMultipleChoiceField

[source]

empty_value。

  • SelectMultiple
  • empty_value
  • coerce参数提供的类型值列表。
  • 验证给定值存在于选项列表中并且可以强制。
  • invalid_choice

%(value)s,它将被选择的选项替换掉。

coerce。

RegexField

[source]
  • TextInput
  • ''(一个空字符串)
  • 规范化为:一个Unicode 对象。
  • 验证给定值与某个正则表达式匹配。
  • invalid

需要一个必需的参数:

regex

指定为字符串或编译的正则表达式对象的正则表达式。

CharField一样工作。

strip

如果启用,则将在正则表达式验证之前应用剥离

SlugField

[source]
  • TextInput
  • ''(一个空字符串)
  • 规范化为:一个Unicode 对象。
  • 验证给定的字符串只包括字母、数字、下划线及连字符。
  • invalid

SlugField。

使用可选参数:

allow_unicode

False

TimeField

[source]
  • TextInput
  • None
  • datetime.time 对象。
  • datetime.time或以特定时间格式格式化的字符串。
  • invalid

接收一个可选的参数:

input_formats

datetime.time对象的格式列表。

input_formats,默认的输入格式为:

URLField

[source]
  • URLInput
  • ''(一个空字符串)
  • 规范化为:一个Unicode 对象。
  • 验证给定值是有效的URL。
  • invalid

采用以下可选参数:

max_length
min_length

CharField.min_length相同。

更多-->>http://usyiyi.cn/translate/Django_111/ref/forms/fields.html

5.窗口小部件

Widget 负责渲染网页上HTML 表单的输入元素和提取提交的原始数据

每当你指定表单的一个字段的时候,Django 将使用适合其数据类型的默认Widget。像这样:

from django import forms

class CommentForm(forms.Form):
    name = forms.CharField()
    url = forms.URLField()
    comment = forms.CharField(widget=forms.Textarea)

这将使用一个Textarea Widget来设置表单的评论 ,而不是默认的TextInput Widget

years属性:

from django import forms

BIRTH_YEAR_CHOICES = ('1980', '1981', '1982')
FAVORITE_COLORS_CHOICES = (
    ('blue', 'Blue'),
    ('green', 'Green'),
    ('black', 'Black'),
)

class SimpleForm(forms.Form):
    birth_year = forms.DateField(widget=forms.SelectDateWidget(years=BIRTH_YEAR_CHOICES))
    favorite_colors = forms.MultipleChoiceField(
        required=False,
        widget=forms.CheckboxSelectMultiple,
        choices=FAVORITE_COLORS_CHOICES,
    )

(1)小部件继承自select小部件

RadioSelect使用单选按钮。

像这样:

>>> from django import forms
>>> CHOICES = (('1', 'First',), ('2', 'Second',))
>>> choice_field = forms.ChoiceField(widget=forms.RadioSelect, choices=CHOICES)
>>> choice_field.choices
[('1', 'First'), ('2', 'Second')]
>>> choice_field.widget.choices
[('1', 'First'), ('2', 'Second')]
>>> choice_field.widget.choices = ()
>>> choice_field.choices = (('1', 'First and only',),)
>>> choice_field.widget.choices
[('1', 'First and only')]

提供choices 属性的Widget 也可以用于不是基于选项的字段 , 例如CharField —— 当选项与模型有关而不只是Widget 时,建议使用基于ChoiceField 的字段。

(2)样式化小部件

如果你想让某个Widget 实例与其它Widget 看上去不一样,你需要在Widget 对象实例化并赋值给一个表单字段时指定额外的属性(以及可能需要在你的CSS 文件中添加一些规则)。

例如下面这个简单的表单:

from django import forms

class CommentForm(forms.Form):
    name = forms.CharField()
    url = forms.URLField()
    comment = forms.CharField()

这表示每个Widget 的输入框将渲染得一模一样:

>>> f = CommentForm(auto_id=False)
>>> f.as_table()
<tr><th>Name:</th><td><input type="text" name="name" required /></td></tr>
<tr><th>Url:</th><td><input type="url" name="url" required /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" required /></td></tr>

Widget.attrs 参数可以实现:

class CommentForm(forms.Form):
    name = forms.CharField(widget=forms.TextInput(attrs={'class': 'special'}))
    url = forms.URLField()
    comment = forms.CharField(widget=forms.TextInput(attrs={'size': '40'}))

Django 将在渲染的输出中包含额外的属性:

>>> f = CommentForm(auto_id=False)
>>> f.as_table()
<tr><th>Name:</th><td><input type="text" name="name" class="special" required /></td></tr>
<tr><th>Url:</th><td><input type="url" name="url" required /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" size="40" required /></td></tr>

(3)基于小部件类

Widget 和MultiWidget 是所有built-in widgets 的基类,并可用于自定义Widget 的基类。

Widget

[source]

render() 方法。

ATTRS T0> 

包含渲染后的Widget 将要设置的HTML 属性。

>>> from django import forms
>>> name = forms.TextInput(attrs={'size': 10, 'title': 'Your name',})
>>> name.render('name', 'A name')
'<input title="Your name" type="text" name="name" value="A name" size="10" required />'

如果你给一个属性赋值True 或False,它将渲染成一个HTML5 风格的布尔属性:

>>> name = forms.TextInput(attrs={'required': True})
>>> name.render('name', 'A name')
'<input name="name" type="text" value="A name" required />'
>>>
>>> name = forms.TextInput(attrs={'required': False})
>>> name.render('name', 'A name')
'<input name="name" type="text" value="A name" />'
supports_microseconds T0> 

0

[source]

value不能保证是有效的输入,因此子类的实现应该防御性地编程。

在Django更改1.10:

旧的名称将工作,直到Django 2.0。

[source] 
Django中的新功能1.11。

'widget',它是包含以下键的小部件的字典表示形式:

  • 'name'name参数中的字段的名称。
  • 'is_hidden':一个布尔值,表示该小部件是否被隐藏。
  • 'required':一个布尔值,表示是否需要此窗口小部件的字段。
  • 'value':由format_value()返回的值。
  • attrs参数的组合。
  • 'template_name'self.template_name的值。

Widget子类可以通过覆盖此方法来提供自定义上下文值。

[source]

None

在这种情况下,该方法应该返回与widget的标签中的第一个ID相对应的ID值。

[source]

FORM_RENDERER设置中的渲染器。

在Django更改1.11:

支持不接受的子类将在Django 2.1中被删除。

[source] 

value_from_datadict 可能调用多次,所以如果你自定义并添加额外的耗时处理时,你应该自己实现一些缓存机制。

[source] 
Django中的新功能1.10.2。

files字典和此小部件的名称,返回是否有数据或文件的小部件。

falls back to its default。

multiple&gt;不会出现在HTML表单提交的数据中,因此用户是否提交了值是未知的。

[source]
Django中的新功能1.10.1。

required属性。

False,因为浏览器验证将需要检查所有复选框,而不是至少一个。

False,以避免在隐藏字段上进行浏览器验证。

MultiWidget

[source] 

MultiValueField 联合使用。

MultiWidget 具有一个必选参数:

widgets

一个包含需要的Widget 的可迭代对象。

以及一个必需的方法:

[source]

可以假设输入的值是合法的,但不一定是非空的。

子类必须实现 这个方法,而且因为值可能为空,实现必须要防卫这点。

“解压”的基本原理是需要“分离”组合的表单字段的值为每个Widget 的值。

datetime 值分离成两个独立的值分别表示日期和时间:

from django.forms import MultiWidget

class SplitDateTimeWidget(MultiWidget):

    # ...

    def decompress(self, value):
        if value:
            return [value.date(), value.time().replace(microsecond=0)]
        return [None, None]

它提供一些自定义上下文:

[source] 

widget['subwidgets']

这些可以在窗口小部件模板中循环:

{% for subwidget in widget.subwidgets %}
    {% include widget.template_name with widget=subwidget %}
{% endfor %}

value_from_datadict()

from datetime import date
from django.forms import widgets

class DateSelectorWidget(widgets.MultiWidget):
    def __init__(self, attrs=None):
        # create choices for days, months, years
        # example below, the rest snipped for brevity.
        years = [(year, year) for year in (2011, 2012, 2013)]
        _widgets = (
            widgets.Select(attrs=attrs, choices=days),
            widgets.Select(attrs=attrs, choices=months),
            widgets.Select(attrs=attrs, choices=years),
        )
        super(DateSelectorWidget, self).__init__(_widgets, attrs)

    def decompress(self, value):
        if value:
            return [value.day, value.month, value.year]
        return [None, None, None]

    def value_from_datadict(self, data, files, name):
        datelist = [
            widget.value_from_datadict(data, files, name + '_%s' % i)
            for i, widget in enumerate(self.widgets)]
        try:
            D = date(
                day=int(datelist[0]),
                month=int(datelist[1]),
                year=int(datelist[2]),
            )
        except ValueError:
            return ''
        else:
            return str(D)

super类使用这个元组来启动widget。

None的情况。

False

6.内建的Wedgit

Django 提供所有基本的HTML Widget,并在django.forms.widgets 模块中提供一些常见的Widget 组,包括the input of textvarious checkboxes and selectorsuploading fileshandling of multi-valued input

TextInput

[source]
  • input_type'text'
  • template_name'django/forms/widgets/text.html'
  • ...>

NumberInput

[source]
  • input_type'number'
  • template_name'django/forms/widgets/number.html'
  • ...>

True的字段。

EmailInput

[source]
  • input_type'email'
  • template_name'django/forms/widgets/email.html'
  • ...>

URLInput

[source]
  • input_type'url'
  • template_name'django/forms/widgets/url.html'
  • ...>

PasswordInput

[source]
  • input_type'password'
  • template_name'django/forms/widgets/password.html'
  • ...>

接收一个可选的参数:

render_value T0> 

False)。

HiddenInput

[source]
  • input_type'hidden'
  • template_name'django/forms/widgets/hidden.html'
  • ...>

MultipleHiddenInput Widget,它封装一组隐藏的输入元素。

DateInput

[source]
  • input_type'text'
  • template_name'django/forms/widgets/date.html'
  • ...>

TextInput 相同,但是带有一些可选的参数:

格式

字段的初始值应该显示的格式。

DATE_INPUT_FORMATS 中找到的第一个格式。

DateTimeInput

[source]
  • input_type'text'
  • template_name'django/forms/widgets/datetime.html'
  • ...>

TextInput 相同,但是带有一些可选的参数:

格式

字段的初始值应该显示的格式。

DATETIME_INPUT_FORMATS 中找到的第一个格式。

True的子类。

TimeInput

[source]
  • input_type'text'
  • template_name'django/forms/widgets/time.html'
  • ...>

TextInput 相同,但是带有一些可选的参数:

格式

字段的初始值应该显示的格式。

TIME_INPUT_FORMATS 中找到的第一个格式。

DateTimeInput。

Textarea

[source]
  • template_name'django/forms/widgets/textarea.html'
  • <textarea>...</textarea>

选择器和复选框小部件

.

<select>.

CheckboxInput

[source]
  • input_type'checkbox'
  • template_name'django/forms/widgets/checkbox.html'
  • ...>

接收一个可选的参数:

check_test T0> 

True。

Select

[source]
  • template_name'django/forms/widgets/select.html'
  • option_template_name'django/forms/widgets/select_option.html'
  • ...>...</select>
choices

Field的该属性更新时,它将覆盖你在这里的任何设置。

NullBooleanSelect

[source]
  • template_name'django/forms/widgets/select.html'
  • option_template_name'django/forms/widgets/select_option.html'

Select Widget,选项为‘Unknown’、‘Yes’ 和‘No’。

SelectMultiple

[source]
  • template_name'django/forms/widgets/select.html'
  • option_template_name'django/forms/widgets/select_option.html'

RadioSelect

[source]
  • template_name'django/forms/widgets/radio.html'
  • option_template_name'django/forms/widgets/radio_option.html'

<li> 标签中的一个单选按钮列表:

<ul>
  <li><input type="radio" name="..."></li>
  ...
</ul>

myform 作为Widget:

{% for radio in myform.beatles %}
<div class="myradio">
    {{ radio }}
</div>
{% endfor %}

它将生成以下HTML:

<div class="myradio">
    <label for="id_beatles_0"><input id="id_beatles_0" name="beatles" type="radio" value="john" required /> John</label>
</div>
<div class="myradio">
    <label for="id_beatles_1"><input id="id_beatles_1" name="beatles" type="radio" value="paul" required /> Paul</label>
</div>
<div class="myradio">
    <label for="id_beatles_2"><input id="id_beatles_2" name="beatles" type="radio" value="george" required /> George</label>
</div>
<div class="myradio">
    <label for="id_beatles_3"><input id="id_beatles_3" name="beatles" type="radio" value="ringo" required /> Ringo</label>
</div>

例如,这个模板...

{% for radio in myform.beatles %}
    <label for="{{ radio.id_for_label }}">
        {{ radio.choice_label }}
        <span class="radio">{{ radio.tag }}</span>
    </label>
{% endfor %}

...将导致以下HTML:

<label for="id_beatles_0">
    John
    <span class="radio"><input id="id_beatles_0" name="beatles" type="radio" value="john" required /></span>
</label>

<label for="id_beatles_1">
    Paul
    <span class="radio"><input id="id_beatles_1" name="beatles" type="radio" value="paul" required /></span>
</label>

<label for="id_beatles_2">
    George
    <span class="radio"><input id="id_beatles_2" name="beatles" type="radio" value="george" required /></span>
</label>

<label for="id_beatles_3">
    Ringo
    <span class="radio"><input id="id_beatles_3" name="beatles" type="radio" value="ringo" required /></span>
</label>

<li> 标签输出,就像上面一样。

BoundField.auto_id。

id_for_label 属性来输出元素的ID。

CheckboxSelectMultiple

[source]
  • template_name'django/forms/widgets/checkbox_select.html'
  • option_template_name'django/forms/widgets/checkbox_option.html'

SelectMultiple,但是渲染成一个复选框列表:

<ul>
  <li><input type="checkbox" name="..." ></li>
  ...
</ul>

BoundField.auto_id。

required HTML属性,如果该字段是必需的,因为浏览器验证将需要检查所有复选框,而不是至少检查一个。

id_for_label 属性来输出元素的ID。

文件上传小部件

FileInput

[source]
  • template_name'django/forms/widgets/file.html'
  • ...>

ClearableFileInput

[source]
  • template_name'django/forms/widgets/clearable_file_input.html'
  • ...> 清除字段的值,如果该字段不是必需的,并具有初始数据。

复合小部件

MultipleHiddenInput

[source]
  • template_name'django/forms/widgets/multiple_hidden.html'
  • ...&gt;标签

一个处理多个隐藏的Widget 的Widget,用于值为一个列表的字段。

choices

Field的该属性更新时,它将覆盖你在这里的任何设置。

SplitDateTimeWidget

[source]
  • template_name'django/forms/widgets/splitdatetime.html'

DateTimeField一起使用。

SplitDateTimeWidget 有两个可选的属性:

date_format

DateInput.format

time_format

TimeInput.format

SplitHiddenDateTimeWidget

[source]
  • template_name'django/forms/widgets/splithiddendatetime.html'

HiddenInput。

SelectDateWidget

[source]
  • template_name'django/forms/widgets/select_date.html'

Select Widget:分别用于年、月、日。

有几个可选参数:

years

默认为包含当前年份和未来9年的一个列表。

months

一个可选的字典,用于”月“选择框。

字典的键对应于月份的数字(从1开始),值为显示出来的月份:

MONTHS = {
    1:_('jan'), 2:_('feb'), 3:_('mar'), 4:_('apr'),
    5:_('may'), 6:_('jun'), 7:_('jul'), 8:_('aug'),
    9:_('sep'), 10:_('oct'), 11:_('nov'), 12:_('dec')
}

empty_label

'day_label')

# A custom empty label with string
field1 = forms.DateField(widget=SelectDateWidget(empty_label="Nothing"))

# A custom empty label with tuple
field1 = forms.DateField(
    widget=SelectDateWidget(
        empty_label=("Choose Year", "Choose Month", "Choose Day"),
    ),
)

 7.模型表单(ModelForm)

在这种情况下,在表单中定义字段将是冗余的,因为你已经在模型中定义了字段。

Form。

像这样:

>>> from django.forms import ModelForm
>>> from myapp.models import Article

# Create the form class.
>>> class ArticleForm(ModelForm):
...     class Meta:
...         model = Article
...         fields = ['pub_date', 'headline', 'content', 'reporter']

# Creating a form to add an article.
>>> form = ArticleForm()

# Creating a form to change an existing article.
>>> article = Article.objects.get(pk=1)
>>> form = ArticleForm(instance=article)

(1)字段类型

fields 属性中指定的顺序。

下面是一个完整的列表:

模型字段 表单域
AutoField 没有以形式表示
BigAutoField 没有以形式表示
BigIntegerField IntegerField with min_value set to -9223372036854775808 and max_value set to 9223372036854775807.
BooleanField BooleanField
CharField CharField with max_length set to the model field’s max_length andempty_value set to None if null=True.
CommaSeparatedIntegerField CharField
DateField 的DateField
DateTimeField DateTimeField字段
DecimalField DecimalField
EmailField EmailField
FileField 的FileField
FilePathField FilePathField
FloatField FloatField
ForeignKey ModelChoiceField(见下文)
ImageField ImageField
IntegerField IntegerField
IPAddressField IPAddressField
GenericIPAddressField GenericIPAddressField
ManyToManyField ModelMultipleChoiceField(见下文)
NullBooleanField NullBooleanField
PositiveIntegerField IntegerField
PositiveSmallIntegerField IntegerField
SlugField SlugField
SmallIntegerField IntegerField
TextField CharField with widget=forms.Textarea
TimeField TimeField
URLField URLField

ForeignKey 字段类型属于特殊情况:

  • QuerySet 表示成ChoiceField,它是一个django.forms.ModelChoiceField,其选项是模型的ForeignKey
  • QuerySet 表示成MultipleChoiceField,它是一个django.forms.ModelMultipleChoiceField,其选项是模型的ManyToManyField

此外,生成的每个表单字段都有以下属性集:

  • required=True
  • label,并将第一个字母大写。
  • help_text。
  • blank=False 值)。

最后,请注意你可以为给定的模型字段重新指定表单字段。

一个完整的例子

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)

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())

在ModelForm上进行验证

ModelForm主要有两步:

  1. 验证表单
  2. 验证模型实例

full_clean() 显式调用,尽管在实际应用中你将很少使用后一种方法。

Model 方法调用之后。

覆盖clean()方法

clean() 来提供额外的验证,方法和普通的表单一样。

instance 属性,表示与它绑定的模型实例。

与模型验证的交互

如果你已经排除某些模型字段,这些字段不会运行验证

模型error_message的注意事项

field级别的错误信息优先。

ValidationError 的时候,且不会有对应的表单级别的错误信息。

NON_FIELD_ERRORS 字典:

from django.forms import ModelForm
from django.core.exceptions import NON_FIELD_ERRORS

class ArticleForm(ModelForm):
    class Meta:
        error_messages = {
            NON_FIELD_ERRORS: {
                'unique_together': "%(model_name)s's %(field_labels)s are not unique.",
            }
        }

save()方法

save() 将创建模型的一个新实例:

>>> from myapp.models import Article
>>> from myapp.forms import ArticleForm

# Create a form instance from POST data.
>>> f = ArticleForm(request.POST)

# Save a new Article object from the form's data.
>>> new_article = f.save()

# Create a form to edit an existing Article, but use
# POST data to populate the form.
>>> a = Article.objects.get(pk=1)
>>> f = ArticleForm(request.POST, instance=a)
>>> f.save()

ValueError

如果您正在设计一个API并且希望使用这些小部件之一的字段的缺省回退行为,请使用自定义表单字段或小部件。

在Django更改1.10.1:

True的值。

在Django更改1.10.2:

value_omitted_from_data()方法。

commit

这是因为只有实例在数据库中存在时才可以保存实例的多对多数据。

像这样:

# Create a form instance with POST data.
>>> f = AuthorForm(request.POST)

#

相关文章:

  • 2022-01-13
  • 2022-02-24
  • 2021-10-02
  • 2022-12-23
  • 2022-12-23
  • 2021-07-07
  • 2021-12-28
  • 2021-06-04
猜你喜欢
  • 2021-10-31
  • 2021-06-03
  • 2021-06-18
  • 2022-12-23
  • 2021-12-15
  • 2021-11-12
  • 2021-12-09
相关资源
相似解决方案