专栏首页授客的专栏Python-Django 第一个Django app

Python-Django 第一个Django app

测试环境:

Python版本:python-3.4.0.amd64

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

Win7 64位

Django 1.11.4

下载地址:https://www.djangoproject.com/download/

安装django

python setup.py install

测试是否成功

>>> import django

>>> print(django.get_version())

1.10.6

>>>

或者

python -m django --version

1.10.6

参考连接:

https://docs.djangoproject.com/en/1.10/intro/install/

https://docs.djangoproject.com/en/1.10/intro/tutorial01/

第一个 Django app Part1

新建项目,选择存放项目的目录(例F:\project\Django\FirstApp),进入该目录,执行django-admin命令

例:新建mysite项目

C:\Users\laiyu>cd /d F:\project\Django\FirstApp

F:\project\Django\FirstApp>django-admin startproject mysite

注意:项目名称不能和python内置组件,或Django组件命名项目,特别是django(和Django自身冲突)或test(和python内置模块冲突)。

运行命令后,生成文件如下:

FirstApp/

mysite/

manage.py

mysite/

__init__.py

settings.py

urls.py

wsgi.py

说明:

最外层mystie: 项目根目录。

manage.py:提供各种方式同Django项目交互的命令行工具。

内层的mysite:python包,存放项目python文件目录。

mystie/__init__.py:一个空文件,告诉python,该目录应该被解析为python包

mysite/settings.py:包含Django项目的配置/设置。

mysite/urls.py:包含Django项目的url声明。

mysite/wsgi.py:服务项目的WSGI-compatible。

确认项目是否能正常运行,切换到根目录,即例中的mysite,运行如下命令:

F:\project\Django\FirstApp\mysite>python manage.py runserver

运行结果:控制台输出如下信息

Performing system checks...

System check identified no issues (0 silenced).

You have 13 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.

Run 'python manage.py migrate' to apply them.

March 13, 2017 - 23:14:26

Django version 1.10.6, using settings 'mysite.settings'

Starting development server at http://127.0.0.1:8000/

Quit the server with CTRL-BREAK.

访问http://127.0.0.1:8000/,ok

运行服务时,可指定端口,如下

python manage.py runserver 8080

也可以用指定ip来访问服务,放入如下

1.运行如下命令

python manage.py runserver 0.0.0.0:8000

2.编辑project_dir/settings.py文件,把服务所在ip添加到 ALLOWED_HOSTS列表中,如下

ALLOWED_HOSTS = ['192.168.1.103']

点击runserver查看更多关于runserver的说明

创建投票app

项目(project) vs 应用(app)

应用:即一个web应用,比如web 博客系统,存放公共记录的数据库,或者一个简单的投票系统。

项目:特定网站应用和配置的集合。一个项目包含多个应用,而一个应用只能在一个项目中。

接下来,在项目根目录下,创建poll应用,这样方便作为顶级模块导入。

例:在manage.py所在目录,即项目根目录下运行命令来创建polls app

F:\project\Django\FirstApp\mysite>python manage.py startapp polls

运行后生成polls文件:

mysite/

polls/

__init__.py

admin.py

apps.py

migrations/

__init__.py

models.py

tests.py

views.py

编写第一个view

编辑polls/views.py文件,添加python代码(带背景色部分)如下

from django.shortcuts import render

# Create your views here.

from django.http import HttpResponse

def index(request):

return HttpResponse("hello, world. You're at the polls index")

为了调用这个index view,需要把它映射到一个URL,因此需要一个URL 配置。

在polls目录下,新建一个名为urls.py的文件,以创建URLConf。现在app目录看起来如下:

polls/

__init__.py

admin.py

apps.py

migrations/

__init__.py

models.py

tests.py

urls.py

views.py

编辑urls.py,添加如下代码

from django.conf.urls import url

from . import views

urlpatterns = [

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

]

配置项目URLConf

编辑mysite/urls.py文件,如下

from django.conf.urls import include, url

from django.contrib import admin

urlpatterns = [

url(r'^polls/', include('polls.urls')),

url(r'^admin/', admin.site.urls),

]

浏览器访问

说明:

1) 正则表达式:$, xxx$:匹配xxx结尾的字符串)。

2) 当Django遇到include()时,会先把请求中的url同include()函数对应的正则表达式匹配(例中按先后顺序分别为:'^polls/','^admin/',如果匹配到,则把URL中匹配到的字符串之后的剩余URL扔给include引用的app URLconf进行后续处理。

例子:修改polls/urls.py内容如下

from django.conf.urls import url

from . import views

urlpatterns = [

url(r'test', views.index, name='index'),

]

修改访问连接:

http://127.0.0.1:8000/polls/testview

访问结果同上。

3)当且仅当需要包含其它应用的URLConf式时使用include()。这里admin.site.urls是个特例。

url函数

url函数接收4个参数:必选参数regex,view,可选参数 kwargs和name。

参数regex: 字符串类型的正则表达式。Django会从urlpatterns list中第一个正则表达式子开始匹配查找直到找到一个匹配的。

注意:正则表达匹配查找时,不搜索GET和POST参数以及域名。比如请求 https://www.example.com/myapp/, URLconf只查找myapp/,又如https://www.example.com/myapp/?page=3,URLconf只查找myapp/

注:正则表达式在第一次加载URLconf模块时就进行了编译,只要不是太复杂的正则表达式,查找速度都很快。

参数view:当Django找到匹配正则表达式的字符串时,会调用view函数,并把一个HttpRequest对象当作第一个函数参数,把通过正则表达式“捕获”的其它值作为其它参数。如果使用simple capture,那么捕获的值以位置参数传递,如果使用named capture则以关键词参数传递。

参数kwargs:关键词参数,以字典方式传递给目标view的关键词参数。

参数name:命名URL,以便在Django其它地方引用时不产生歧义。

参考连接:

https://docs.djangoproject.com/en/1.10/intro/tutorial01/

第一个 Django app Part2

建立数据库

打开mysite/settings.py。这是个普通的python模块,拥有代表Django配置的模块级变量。

默认的,配置使用SQLite。如果你对数据库不熟悉,或者仅是想使用试用Djano,这是个最容易的选择。SQLite包含在python中,所以不要安装其它任何东西来提供数据库支持。但是开始真正的项目时,可能需要使用其它更有伸缩性的数据库比如PostgreSQL。

如果想使用其它数据库,安装数据库并改变DATABASE 'default'项中的关键词来匹配数据库连接,如下:

DATABASES = {

'default': {

'ENGINE': 'django.db.backends.sqlite3',

'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),

}

}

说明:

ENGINE:可选值是'django.db.backends.sqlite3', 'django.db.backends.postgresql', 'django.db.backends.mysql', 或者'django.db.backends.oracle'。其它后端也可以,查看详情

NAME:数据库名字。

如果使用SQLite,数据库文件将存放在电脑上,这种情况下,NAME应该为绝对路径,包含数据库文件的文件名。默认值如上,把数据库文件存放在项目根目录下。

如果不使用SQLite,需要设置额外参数如 USER, PASSWORD,和HOST。更多详情参考DATABASES.

另外,确保提供的USER具备“create database”权限。

编辑mysite/settings.py,设置TIME_ZONE为你的时区。

注意INSTALLED_APPS设置,该设置包含了Django实例中激活的所有Django应用。应用可在多个项目中使用,可以打包并发布给其它项目使用。

默认的,INSTALLED_APPS包含以下来自Django应用:

django.contrib.admin - 管理后台

django.contrib.auth - 授权系统

django.contrib.contenttypes - content type框架

django.contrib.sessions - 会话框架

django.contrib.message - 消息框架

django.contrib.staticfiles - 管理静态文件的框架

其中,一些应用使用了数据库表,所以,我们需要在使用它们之前创建数据库表。为了达到这个目的,执行以下命令:

python manage.py migrate

Operations to perform:

Apply all migrations: admin, auth, contenttypes, sessions

Running migrations:

Applying contenttypes.0001_initial... OK

Applying auth.0001_initial... OK

Applying admin.0001_initial... OK

Applying admin.0002_logentry_remove_auto_add... OK

Applying contenttypes.0002_remove_content_type_name... OK

Applying auth.0002_alter_permission_name_max_length... OK

Applying auth.0003_alter_user_email_max_length... OK

Applying auth.0004_alter_user_username_opts... OK

Applying auth.0005_alter_user_last_login_null... OK

Applying auth.0006_require_contenttypes_0002... OK

Applying auth.0007_alter_validators_add_error_messages... OK

Applying auth.0008_alter_user_username_max_length... OK

Applying sessions.0001_initial... OK

F:\project\Django\FirstApp\mysite>

migrate查找INSTALLED_APPS设置,然后根据mysite/setting.py中的数据库设置和应用程序的数据库迁移

创建必要的数据库。查看创建的表:数据库客户端输入命令\dt(PostgreSQL),.shema(MySQL), SELECT TABLE_NAME FROM USER_TABLES(Oracle);

提醒:一些默认的应用我们不需要,可以在运行migrate之前删除、注释掉。

创建模块

将在poll应用中创建两个模块:Question和Choice。每个Question包含一个问题和发布日期,每个Choice有两个域:选项文字和投票计数器。每个Choice同一个Question关联。

编辑polls/models.py文件

from django.db import models

# Create your models here.

class Question(models.Model):

question_text = models.CharField(max_length=200)

pub_date = models.DateTimeField('date published')

class Choice(models.Model):

question = models.ForeignKey(Question, on_delete=models.CASCADE)

choice_text = models.CharField(max_length=200)

votes = models.IntegerField(default=0)

每个模块都由django.db.models.Model的子类表示,类变量代表model中数据库Field。

每个域由一个Field类(比如代表字符串的CharField,代表时间的DateTimeField)实例表示,告诉Django每个field可容纳什么类型的数据。

Field实例(比如 question_text、pub_date)的名字,即为域的名字,可在python代码中使用,同时数据库也将把它当表字段名使用。

给Field提供的第一个可选的位置参数可用来生成便于人易读的名字。如果未提供,则使用机器易读的名字作为人类易读的名字。例中,我们仅为Question.pub_date提供了人类易读的名字date published,其它模块Field实例则使用机器易读的名字,比如choice_text。

一些Field需要必选参数,比如CharField,需要提供max_length。这不仅是用于数据库模式(schema),还用于合法性验证(validation)。

Field还有各种可选参数,比如例中把votes的default值设置为0。

最后,注意这里使用ForeignKey来确立关系,这告诉Django每个Choice和单个Question关联。Django支持所有公共数据库关系:多对一,多对多,一对一。

激活模块

上述模块代码给Django提供了许多信息,拥有它,Django可:

1)为该app创建数据库模式(CREATE TABLE语句)

2)为访问Question和Choice对象创建Python 数据库访问api

但是,要先告诉项目已安装polls应用。

为了在项目中包含该app,需要在INSTALLED_APPS设置中添加引用。PollsConfig类位于polls/apps.py文件中,点分路径为jango.apps.PollsConfig,如下:

from django.apps import AppConfig

class PollsConfig(AppConfig):

name = 'polls'

编辑mysite/settings.py,添加点分路径(带背景色内容)

INSTALLED_APPS = [

'polls.apps.PollsConfig',

'django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.messages',

'django.contrib.staticfiles',

]

运行命令:

F:\project\Django\FirstApp\mysite>python manage.py makemigrations polls

Migrations for 'polls':

polls\migrations\0001_initial.py:

- Create model Choice

- Create model Question

- Add field question to choice

通过运行makemigrations,告诉django你对模块做了些改动,并且希望记录这些改动(但不立即执行这些改动),这些改动存在在磁盘文件,上例中文件为polls/migrations/0001_initial.py。可易方式读取这些改动,查看migration带来的sql执行。

python manage.py sqlmigrate polls 0001

BEGIN;

--

-- Create model Choice

--

CREATE TABLE "polls_choice" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "c

hoice_text" varchar(200) NOT NULL, "votes" integer NOT NULL);

--

-- Create model Question

--

CREATE TABLE "polls_question" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,

"question_text" varchar(200) NOT NULL, "pub_date" datetime NOT NULL);

--

-- Add field question to choice

--

ALTER TABLE "polls_choice" RENAME TO "polls_choice__old";

CREATE TABLE "polls_choice" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "c

hoice_text" varchar(200) NOT NULL, "votes" integer NOT NULL, "question_id" integ

er NOT NULL REFERENCES "polls_question" ("id"));

INSERT INTO "polls_choice" ("question_id", "choice_text", "votes", "id") SELECT

NULL, "choice_text", "votes", "id" FROM "polls_choice__old";

DROP TABLE "polls_choice__old";

CREATE INDEX "polls_choice_7aa0f6ee" ON "polls_choice" ("question_id");

COMMIT;

注意:

1)Django会自动添加主键 id(可重写)

2)约定的,Django会添加”_id”到外键域(可重写)

可执行python manage.py check,在不执行迁移或改动数据库的情况下,来检查项目中的问题

接着,执行migrate在数据库中创建模块表,即让上述存储的改动在应用中生效。

F:\project\Django\FirstApp\mysite>python manage.py migrate

Operations to perform:

Apply all migrations: admin, auth, contenttypes, polls, sessions

Running migrations:

Applying polls.0001_initial... OK

阅读django-admin documentation查看manage.py工具的更多功能。

API交互

调用python shell

python manage.py shell

Python 3.4.0 (v3.4.0:04f714765c13, Mar 16 2014, 19:25:23) [MSC v.1600 64 bit (AM

D64)] on win32

Type "help", "copyright", "credits" or "license" for more information.

(InteractiveConsole)

同直接运行python不一样,因为manage.py设置DJANGO_SETTINGS_MODULE环境变量,为mysite/settings.py文件提供python导入路径。

注:也可以不用manage.py,直接设置DJANGO_SETTINGS_MODULE环境变量,然后运行python并设置Django

set DJANGO_SETTINGS_MODULE=mysite.settings

python

Python 3.4.0 (v3.4.0:04f714765c13, Mar 16 2014, 19:25:23) [MSC v.1600 64 bit (AM

D64)] on win32

Type "help", "copyright", "credits" or "license" for more information.

>>> import django

>>> django.setup()

按这种方式,必须在manage.py所在目录下开启python。或者确保这个目录在python的path中,这样import mystie才起作用。

调用数据库api

>>> from polls.models import Question, Choice

# 系统中还没有问题

>>> Question.objects.all()

<QuerySet []>

# 创建一个新的Question

>>> from django.utils import timezone

>>> q = Question(question_text="what's up?", pub_date=timezone.now())

# 保存对象到数据库。

>>> q.save()

# 输出也可能是1L,而不是1,取决于数据库。

>>> q.id

1

# 通过python属性访问模块field

>>> q.question_text

"what's up?"

>>> q.pub_date

datetime.datetime(2017, 3, 22, 12, 57, 18, 103269, tzinfo=<UTC>)

# 通过修改属性来修改field

>>> q.question_text = "what's up?"

>>> q.save()

# objects.all()展示数据库中的所有问题。

>>> Question.objects.all()

<QuerySet [<Question: Question object>]>

为了更清楚点的显示对象,可编辑polls/models.py文件,添加一个__str__()方法到Question和Choice。

from django.db import models

from django.utils.encoding import python_2_unicode_compatible

@python_2_unicode_compatible # 如果需要支持python2

Question(models.Model):

# ...

def __str__(self):

return self.question_text

@python_2_unicode_compatible # 如果需要支持python2

# ...

def __str__(self):

return self.choice_text

添加自定义方法

import datetime

from django.db import models

from django.utils import timezone

class Question(models.Model):

# ...

def was_published_recently(self):

return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

再次运行python manage.py shell

>>> from polls.models import Question, Choice

# 确认 __str__() 是否起作用

>>> Question.objects.all()

<QuerySet [<Question: What's up?>]>

# Django提供了一个完全由关键词参数驱动的丰富的数据库API。

>>> Question.objects.filter(id=1)

<QuerySet [<Question: What's up?>]

>>>> Question.objects.filter(question_text__startswith='What')

<QuerySet [<Question: What's up?>]>

# 获取今年发布的问题

>>> from django.utils import timezone

>>> current_year = timezone.now().year

>>> Question.objects.get(pub_date__year=current_year) #注意 pub_date和year中间有两下划线

<Question: What's up?>

# 如果请求的id不存在,将抛出异常.

>>> Question.objects.get(id=2)

Traceback (most recent call last):

...DoesNotExist: Question matching query does not exist.

# 按主键查询,以下命令等同于Question.objects.get(id=1)

>>> Question.objects.get(pk=1)

<Question: What's up?>

# 确认自定义方法起作用

>>> q = Question.objects.get(pk=1)

>>> q.was_published_recently()

True

# 给Question多个Choice。调用create函数构造一个新的Choice对象,执行INSERT 语句,添加choice到#获取的choice set,然后返回新建的Choice对象。Django创建了一个集合以容纳ForeignKey 关系的另一方#(如 question’s choice)。

>>> q = Question.objects.get(pk=1)

# 展示相关对象集的choice - 目前为空

>>> q.choice_set.all()

<QuerySet []>

>>> q.choice_set.create(choice_text='Not much', votes=0)

<Choice: Not much>

>>> q.choice_set.create(choice_text='The sky', votes=0)

<Choice: The sky>

>>> c = q.choice_set.create(choice_text='Just hacking again', votes=0)

# Choice 对象有API可访问与其相关的Question对象。

>>> c.question

<Question: What's up?>

# 反之,Question 对象可访问Choice对象。

>>> q.choice_set.all()

<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>

>>> q.choice_set.count()

3

# 只要你需要,API自动跟随关系。关系之间用下划线关联。

# 找出同Choice关联的question,要求qub_date在今年以内

>>> Choice.objects.filter(question__pub_date__year=current_year)

<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>

# 删除其中一个choice

>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')

>>> c.delete()

查看更多关于对象关系:Accessing related objects,更多关于使用双下划线进行域查找:Field lookups,数据库API完整信息:Database API reference

介绍Djando Admin

创建管理员用户

python manage.py createsuperuser

Username (leave blank to use 'laiyu'): admin

Email address: 1033553122@qq.com

Password:

Password (again):

This password is too short. It must contain at least 8 characters.

Password:

Password (again):

Superuser created successfully.

开启开发服务器

Django管理员站点默认是激活的。

如果服务器未运行,执行如下命令

python manage.py runserver

浏览器访问

进入站点

输入帐号,密码登录

可看到groups和users,这是由django.contrib.auth提供的,django的认证框架。

使得poll应用在站点管理页中可修改

如上,没看到poll应用。要展示该页面,还需告诉admin,Question对象拥有admin接口。为了达到这个目的,打开polls/admin.py,按如下编辑

from django.contrib import admin

# Register your models here.

from .models import Question

admin.site.register(Question)

点击Questions

点击what’s up

第一个 Django app Part3

Django中,web页面和其它内容都是从views派生的,每个view由python函数(或方法)表示,Django通过检查请求的域名后面的那部分URL来选择view。

编写更多的视图(view)

在polls/view.py中添加几个视图

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)

然后在polls/urls.py中添加url()调用

polls/urls.py

from django.conf.urls import url

from . import views

urlpatterns = [

# ex: /polls/

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

# ex: /polls/3/

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

# ex: /polls/3/results/

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

# ex: /polls/3/vote/

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

]

浏览器访问

默认的,从站点请求页面,比如“/polls/3”,Django会先加载mysite.urls python模块,因为ROOT_URLCONF配置指向它。先查找urlpatterns变量,并按顺序解析正则表达式,如果找到匹配‘^polls/’的,把URL中匹配到的字符串polls/去掉,然后把后面剩余部分“3/”扔给polls.urls URLCONf进行后续处理。接着匹配到r'^(?P<question_id>[0-9]+)/$',调用detail view,如下:

detail(request=<HttpRequest object>, question_id=3)

question_id=3 来自(?P<question_id>[0-9]+)。使用双括号于正则表达式,可捕获正则表达式匹配到的文本,然后当作参数发给view函数。?P<question_id>定义了用于匹配正则表达式的名称,即用来匹配函数关键词参数的pattern,[0-9]+用于匹配数字序列。

编写执行实际任务的视图

每个视图都负责这两件事之一:返回一个包含请求页面内容的HttpResponse()对象,或者是抛出异常,比如Http404

视图可从数据库读取记录,也可使用Django的模板系统,或者是第三方的Python模板系统,可生成PDF文件,输出XML,创建ZIP及其它你想要的。

根据发布日期,展示最新的5个question,逗号分隔。

在polls/view.py中添加以下内容,其它保持不变

from .models import Question

# Create your views here

def index(request):

latest_question_list = Question.objects.order_by('-pub_date')[:5]

output = ','.join([q.question_text for q in latest_question_list])

return HttpResponse("Hello, world. You’re at the polls index")

这里有个问题,就是视图中的页面设计是写死的,如果想改变页面样式,需要编辑Python代码。这里,使用Django的模板系统来创建一个可用视图。

先在polls目录下创建一个名为templates的目录,Django会在这里查找目标。

项目的TEMPLATES设置描述了Django将咋样加载并渲染模板。默认的,配置文件配置了一个DjangoTemplates后端,其APP_DIRS选项被设置为True。约定的,DjangoTemplates会在每个INSTALLED_APP中查找templates子目录。

在刚创建的templates目录下创建另一个polls目录,并在该目录下新建index.html文件。换句话说,template应该在polls/templates/polls/index.html。由于app_directories模板加载器按上述描述的方式工作,所以,可简单的使用polls/index.html引用该模板。

注意:模板命名

我们可直接在polls/templates目录下存放我们的模板,但是这样不好,Django会选择它查找到的第一个名字匹配的模板,这样的话,如果你在另一个不同的应用下有相同名称的目标,Django无法区分它们。所以,我们需要对它们进行命名,也就是把那些目标存放在以应用自身命名的另一个目录。

编辑模板

{% if latest_question_list %}

<url>

{% for question in latest_question_list %}

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

{% endfor %}

</url>

{% else %}

<p>No polls are available.</p>

<% endif %>

使用模板来更新polls/views.py里面的index视图。

from django.http import HttpResponse

from django.template import loader

from .models import Question

def index(request):

latest_question_list = Question.objects.order_by('-pub_date')[:5]

template = loader.get_template('polls/index.html')

context = {

'latest_question_list':latest_question_list

}

return HttpResponse(template.render(context, request))

代码加载名为polls/index.html的模板,并传递给context。context为一个字典,映射模板变量到python对象。

浏览器访问

点击连接,打开详情。

捷径:render()

编辑polls/views.py

from django.shortcuts import render

from .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)

render函数接收一个request作为其第一个参数,模板名字作为第二个参数,字典作为可选的第三个参数。函数返回一个经过给定context渲染的HttpResponse对象。

抛出404错误

polls/views.py

from django.http import Http404

from django.shortcuts import render

from .models import Question

# ...

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})

新建模板

polls/templates/polls/detail.html

{{ question }}

运行浏览器

捷径:get_object_or_404()

推荐使用get_object_or_404()

使用模板系统

到回来看detail视图,针对给定变量question,polls/detail.html模板可能如下

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

<ul>

{% for choice in question.choice_set.all %}

<li>{{ choice.choice_text }}</li>

{% endfor %}

</ul>

运行结果如下

模板系统使用点查找(dot-lookup)语法访问变量属性。{{ question.question_text }}为例,先在question对象上执行字典查找,然后在视图属性中查找-这种情况下,找到了。如果属性查找失败,则尝试列表索引查找。

方法调用发生咋{ % for %}循环:question.choice_set.all()被转为python代码 question.choice_set.all(),返回适合{% for %}标签,由Choice对象组成的可迭代对象。

移除模板中的写死的URL

polls/index.html中编写的指向question的连接

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

写死的url,对有许多模板的项目来说,更改url会变成一件很困难的事情。由于polls.urls模块的url()函数中定义了命名的参数,可通过{% url %}模板标签来移除在url配置中,特定url路径上定义的依赖:

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

以下是'detail' polls/urls.py中的定义

...

# 'name'的值被 {% url %} 模板标签定义

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

...

这样当需要更改应用的url,比如更改为polls/specifics/12/,可以不用在目标中更改写死的url,直接在polls/urls.py中更改。

...

# 添加 'specifics'到url

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

...

给URL名字增加名称空间

在URLConf中添加名称空间,以便使用{% url %}模板标签时,django能区分不用应用的url。

在polls/urls.py中添加app_name来设置应用的名称空间。

from django.conf.urls import url

from . import views

app_name = 'polls'

urlpatterns = [

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

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

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

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

]

更改polls/index.html模板

更改模板

polls/templates/polls/index.html

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

为如下:

polls/templates/polls/index.html

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

第一个 Django app Part4

编写一个简单的表格

更新detail模板(polls/detail.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>

说明:

1)每个choice都有一个对应的radio按钮。每个radio按钮的值都同关联问题choice id关联。每个radio按钮的名字为choice。这也就意味着,当某人选择其中一个radio按钮并提交表单时,发送POST数据choice=#,其中#表示所选择的choice的id

2)设置表单的action为 {% url 'polls:vote' question.id %},设置method='post'(对立的method='get'),这很重要,因为这会改变服务器端的数据。

3)forloop.counter一个表示当前循环的执行次数的整数计数器。 这个计数器是从1开始的,所以在第一次循环时 forloop.counter 将会被设置为1

4)因为是POST表单,需要考虑跨站脚本攻击,所以使用{% csrf_token %}模板标签。

接着,创建处理提交数据的视图

还记得polls/urls.py有如下设置:

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

修改polls/views.py中的样本函数vote

polls/views.py

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):

# Redisplay the question voting form.

return render(request, 'polls/detail.html', {

'question': question,

'error_message': "You didn't select a choice.",

})

else:

selected_choice.votes += 1

selected_choice.save()

# Always return an HttpResponseRedirect after successfully dealing

# with POST data. This prevents data from being posted twice if a

# user hits the Back button.

return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))

说明:

Request.POST类似字典的对象,允许通过key名称来访问提交的数据。例中,request.POST['choice']返回字符串表示choice的ID。Request.POST值总是字符串。

类似的,django提供了request.GET来访问GET data

如果POST数据中无choice,Request.POST['choice']将抛出KeyError。

增加choice计数后,code返回HttpResponseRedirect而非正常的HttpResponse。HttpResponseRedirect携带单个参数:将要重定向至的url。

使用reverse()函数避免在view视图中写死url。reverse()调用返回一个类似如下的字符串:

'/polls/3/results'

其中,3为问题id,该重订向url将会调用'results'视图来展示最终页面。

As mentioned in Tutorial 3, request is an HttpRequest object. For more on HttpRequest objects, see the request and response documentation.

投票之后,vote视图,重定向到问题的结果页面。重写vote视图:

polls/views.py

from django.shortcuts import get_object_or_404, render

def results(request, question_id):

question = get_object_or_404(Question, pk=question_id)

return render(request, 'polls/results.html', {'question': question})

创建polls/results.html模板

polls/templates/polls/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>{% endfor %}</ul>

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

浏览器访问

使用通用视图

使用通用视图来转换poll应用。

1)转换URLConf

2)删除旧的,不必要的视图

3)引入基于Django的通用视图(generic view)

改良的URLConf

polls/urls.py

from django.conf.urls import url

from . import views

app_name = 'polls'

urlpatterns = [

url(r'^$', views.IndexView.as_view(), name='index'),

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

url(r'^(?P<pk>[0-9]+)/results/$', views.ResultsView.as_view(), name='results'),

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

]

改良的视图

polls/views.py

from django.shortcuts import get_object_or_404, render

from django.http import HttpResponseRedirect

from django.urls import reverse

from django.views import generic

from .models import Choice, Question

class IndexView(generic.ListView):

template_name = 'polls/index.html'

context_object_name = 'latest_question_list'

def get_queryset(self):

"""Return the last five published questions."""

return Question.objects.order_by('-pub_date')[:5]

class DetailView(generic.DetailView):

model = Question

template_name = 'polls/detail.html'

class ResultsView(generic.DetailView):

model = Question

template_name = 'polls/results.html'

def vote(request, question_id):

... # same as above, no changes needed.

问题:问题列表这么调整后变成了空白,怎么解决?

这里使用了两种视图:ListView和DetailView。这两种对象分别抽象了list对象的展示和特定读写的详细页面展示。

每种通用视图使用model属性来区分需要作用的模块。

DetailView视图期望从ULR捕获的主键值被称为pk,所以把question_id改成了pk

默认的DetailView视图使用名为<app name>/<model name>_detail.html的模板。例子中,使用polls/question_detail.html。template_name属性告诉Django使用指定名称的模板,而不是使用默认模板名称。

类似的,ListView使用<app name>/<model name>_list.html模板。

对于ListView,自动生成context变量question_list。为了重写这个,提供context_object_name来指定自己的变量latest_question_list。

第一个 Django app Part5

第一个 Django app Part6

自定义app样式和感观。

在polls目录下新建static。Django会在这查找静态文件。类似查找模板。

Django的STATICFILES_FINDERS设置包含了finder list,告诉它怎么查找静态文件。其中一个默认的finder AppDirectoriesFinder会在每个INSTALLED_APPS查找static子目录。管理站点对静态文件使用相同的目录结构

static目录下新建一个polls目录,在该目录下新建名为style.css的文件。使用polls/style.css引用资源

编辑style.css

polls/static/polls/style.css

li a {

color: green;

}

修改polls/templates/polls/index.html

{% load static %}

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

运行效果:

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

添加背景图片

在polls/static/polls目录下新建images目录,并在该目录下存放一张名为background.gif的图片。

修改sytle.cass,新增代码如下

body {

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

}

刷新页面,可看到屏幕右上方显示动态图片

注意:{% static %}模板标签不适用非Django生成的静态文件,比如样式表单。

第一个 Django app Part7

自定义管理站点 form

polls/admin.py

from django.contrib import admin

from .models import Question

class QuestionAdmin(admin.ModelAdmin):

fields = ['pub_date', 'question_text']

admin.site.register(Question, QuestionAdmin)

上述代码使得Question field位于Publication date之后

分隔成多个fieldsets

from django.contrib import admin

from .models import Question

class QuestionAdmin(admin.ModelAdmin):

fieldsets = [

(None, {'fields': ['question_text']}),

('Date information', {'fields': ['pub_date']}),

]

admin.site.register(Question, QuestionAdmin)

添加关联对象

方式1,注册模块

polls/admin.py

from django.contrib import admin

from .models import Choice, Question

# ...

admin.site.register(Choice)

那个form中,Question field是一个select box,包含数据库中的每个问题。Django知道ForeignKey应该在<select> box中出现。

Question的编辑和+号按钮,可分别打开question编辑(需要先选定问题才可用)和添加页面。

方式2:

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是由extra指定的,点击Add another Choice链接,自动新增一个Choice。

这里有个问题,就是占用空间比较大。为此,Django提供了一个tabular的方式来展示inline相关对象。

polls/admin.py

class ChoiceInline(admin.TabularInline):

#...

delete?列用于提供删除操作(通过点击Add another Choice增加的行。)

自定义admin change list

展示单个field。使用list_display admin选项,供展示的field元组,比如列。

class QuestionAdmin(admin.ModelAdmin):

# ...

list_display = ('question_text', 'pub_date')

还可添加was_published_recently()方法。

class QuestionAdmin(admin.ModelAdmin):

# ...

list_display = ('question_text', 'pub_date', 'was_published_recently')

可点击列标题来排序,was_published_recently列除外,因为不支持按任意方法的输出排序。另外,was_published_recently列默认为方法名称(下划线替换了空格)。

通过给方法增加属性来改善。

polls/models.py

class Question(models.Model):

# ...

def was_published_recently(self):

now = timezone.now()

return now - datetime.timedelta(days=1) <= self.pub_date <= now

was_published_recently.admin_order_field = 'pub_date'

was_published_recently.boolean = True

was_published_recently.short_description = 'Published recently?'

效果:

增加过滤器

修改polls/admin.py,新增list_filer,新增代码如下。

list_filter = ['pub_date']

效果如下:

filter展示类型取决于你过滤的field。因为pub_date是DateTimeField,Django知道怎么给恰当的filter选项:Any date,Today等

增加搜索

search_fields = ['question_text']

效果如下:

自定义admin样式和感观

自定义项目模板

在项目目录中(包含manage.py文件)下创建template目录。template可放在Django可访问的任何文件系统,但是保持模板在项目里,是需要好好遵守的约定。

编辑mysite/settings.py,在TEMPLATES设置中添加一个DIRS选项。

mysite/settings.py

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',

],

},

},

]

DIRS是当加载Django模板时,需要检查的文件系统目录列表,是一个搜索路径。

现在在templates目录中新建一个名为admin的目录,从默认的Django admin模板目录(django/contrib/admin/templates)中拷贝模板文件admin/base_site.html到该目录。

编辑文件,替换{{ site_header|default:_('Django administration') }}为自己的站点名称。

{% block branding %}

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

{% endblock %}

该例字告诉我们使用这种方法来重写模板。

模板包含很多类似{% block branding %} and {{ title }}的文本,{%和{{标签是Django的模板语言。

参考连接:

https://docs.djangoproject.com/en/1.10/intro/tutorial02/

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Oracle 参数文件及相关操作介绍

    服务器参数文件是一个二进制文件,作为初始化参数的存储仓库。实例运行时,可用ALTER SYSTEM来改变参数值,且任何对初始化参数的改变设置不会随实例的关闭、...

    授客
  • JMeter http(s)请求插件jmeter-plugin-httpBinaryFileUpload.jar

    https://gitee.com/ishouke/jmeter-plugin-httpBinaryFileUpload

    授客
  • Bootstrap Bootstrap表格插件bootstrap-table配置与应用小结

    https://gitee.com/ishouke/front_end_plugin/blob/master/jquery-3.2.1.min.js

    授客
  • pytorch .detach() .detach_() 和 .data用于切断反向传播的实现

    这篇文章主要介绍了pytorch .detach() .detach_() 和 .data用于切断反向传播的实现。

    于小勇
  • 一款可以“吃鸡”的NAS

    作为2017 TGS最佳多人游戏的《绝地求生:大逃杀》(简称:吃鸡),上市不足一年,就获得了3000万的销量。不仅让开发商韩国蓝洞赚的盆满钵满,更把萎靡了多时的...

    企鹅号小编
  • 厉害了我的京东!全球首个无人仓亮相《大国重器》,智慧物流成国家新名片

    厉害了我的国! 纪录片《大国重器》第二季的重磅归来,让我们通过央视看到了大气磅礴的国之重器“英雄谱”,首次开放的超级工厂,首次披露的核心技术关键突破,最先进的世...

    京东技术
  • dotnet OpenXML 文本 BodyProperties 的属性作用

    本文收集 a:bodyPr 文本框属性 BodyProperties 的属性的作用

    林德熙
  • Chrome OS 双系统安装

    上一篇post [虚拟机中安装Chrome OS(cloudready)]我实践了在虚拟机中安装ChromeOS,从中得出启发,成功的在我的一台PC上装上了Wi...

    gigiwangs
  • Spark性能调优篇五之使用fastUtil工具包

            今天再来介绍一个小的优化点,是一个通过使用fastutil工具包对数据格式进行优化。首先来明确一个东西,什么是fastutil?

    z小赵
  • 【DB笔试面试425】SQL Server物理有哪三种类型的文件?

    (3)事务日志文件:默认扩展名为.ldf(记录对数据库的所有操作,但不包含所操作的数据)。

    小麦苗DBA宝典

扫码关注云+社区

领取腾讯云代金券