前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python-Django 第一个Django app

Python-Django 第一个Django app

作者头像
授客
发布2019-09-11 15:39:26
1.2K0
发布2019-09-11 15:39:26
举报

测试环境:

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/

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-04-12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档