以创建投票应用为例
1. 步骤精简
- 创建模型
a. 纸和笔定义模型
b.models.py
创建模型
c.makemigrations / migrate
激活和迁移
d.admin.py
放入管理 - 创建视图
a. 纸和笔设计视图
b.views.py
建立视图
c.urls.py
添加 URL 调度 - 创建模板
2. 步骤详解
2.1. 步骤1:创建模型
2.1.1. 纸和笔定义模型
投票应用涉及两个主要模型:
- 问题:描述和发布时间
- 选项:描述和当前票数
- 每个选项对应一个问题
2.1.2. models.py 创建模型
打开应用的 models.py。参考代码:
# FirstApp/models.py
import datetime # Python标准datetime模块,用于运算时间(昨天、7天内……)
from django.utils import timezone # Django时区相关工具,用于运算时间(当前)
from django.db import models
# Question 模型
class Question(models.Model): # 每个模型都是 django.db.models.Model 的子类
question_text = models.CharField(max_length=200) # 变量名 = 字段名
pub_date = models.DateTimeField('发布日期')
# 每个字段都是 Field 类的实例。
# 比如 question_text 是 CharField 实例,pub_date 是 DateTimeField 实例
def __str__(self):
# 自定义 shell 中 Question.objects.all() 返回的内容
# 也是控制在后台列表的默认显示内容
return self.question_text
def was_recently_published(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
# 上面代码的意思是,当发布日期大于等于昨天(昨天和今天),就返回
# timezone.now() 当前时间
# timezone.now() - datetime.timedelta(day=1) 表示昨天
# timezone.now() - datetime.timedelta(day=7) 表示7天内
# Choice 模型
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
# 定义关系,每个 Choice 对象关联到一个 Question 对象里
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
def __str__(self):
words = '选项 “{}” 获得{}票'.format(self.choice_text, self.votes)
return words
2.1.3. makemigrations / migrate 激活模型
- 确认在项目 settings.py 中的 INSTALLED_APP 已经写好了应用(参考:使用应用)
$ python manage.py makemigrations AppName
$ python manage.py migrate
额外提示
• 如果想看创建表的SQL可以执行:python manage.py sqlmigrate AppName 0001
• 如果想检查整个项目的问题:python manage.py check
2.1.4. admin.py 放入管理
- 引入需要管理的模型:
from .models import Question # 从同级 models 引入 Question 模型
- 注册模型:
admin.site.register(Question)
2.2. 步骤2:创建视图
视图:一类具有相同功能和模板的网页的集合。每个视图 = 一个简单的函数。
比如建立博客,可能包含的视图:
- 博客首页:展示最近的几项内容。
- 内容详情页:详细展示某项内容。
- 以年为单位的归档页:展示选中的年份里各个月份创建的内容。
- 以月为单位的归档页:展示选中的月份里各天创建的内容。
- 以天为单位的归档页:展示选中天里创建的所有内容。
- 评论处理器:用于响应为一项内容添加评论的操作。
视图可以做的事:
- 必须:返回HttpResponse对象,或者404
- 读取数据库记录
- 使用模板引擎
- 生成 pdf、xml、zip等
2.2.1. 设计视图
投票应用主要涉及以下页面/视图(和URL):
- 问题索引:最近的几个问题列表(appname/)
- 问题详情:展示选项(appname/id/)
- 问题结果:展示选项投票情况(appname/id/result/)
- 投票处理:处理用户的选项投票(appname/id/vote/)
2.2.2. views.py 建立视图
打开应用的 views.py,参考代码:
2.2.2.1. 【返回类型3:Render】
# FirstApp/views.py 返回第3种:Render返回
from django.shortcuts import render
from .models import Question # 从模型中引入 Question 模型
# 应用首页:问题索引
def index(request):
# 获取最近的条目:Question.objects.order_by() 使用的是 Django 的数据库 API
latest_question_list = Question.objects.order_by('-pub_date')[:5]
context = {
'latest_question_list': latest_question_list,
}
return render(request, 'polls/index.html', context)
2.2.2.2. 【返回类型2:HttpResponse】
# FirstApp/views.py 返回第2种:HttpResponse返回
from django.shortcuts import render, HttpResponse
# render:用于渲染
# HttpResponse:用于返回 HttpResponse 对象
from django.template import loader # 引入模板加载器
from .models import Question # 从模型中引入 Question 模型
# 应用首页:问题索引
def index(request):
# 获取最近的条目
# Question.objects.order_by() 使用的是 Django 的数据库 API
# [:5] 获取前5个,下标为0~4
latest_question_list = Question.objects.order_by('-pub_date')[:5]
template = loader.get_template('polls/index.html') # template目录中的位置
context = { # 传给模板的内容(字典)
'latest_question_list': latest_question_list,
}
return HttpResponse(template.render(context, request))
2.2.2.3. 【返回类型1:直接输出】
# FirstApp/views.py 返回第1种:直接输出
from django.shortcuts import HttpResponse
from .models import Question # 从模型中引入 Question 模型
# 应用首页:问题索引
def index(request):
# 获取最近的条目
# Question.objects.order_by() 使用的是 Django 的数据库 API
# [:5] 获取前5个,下标为0~4
latest_question_list = Question.objects.order_by('-pub_date')[:5]
output = ''
for question in latest_question_list:
output += question.question_text + ','
# 上面这三行,实际上可以写成,下面这一行:
output = ','.join([q.question_text for q in latest_question_list])
return HttpResponse(output)
2.2.2.4. 【如果取不出内容:try…except…】
# FirstApp/views.py 404第1种:try...except...
from django.http import Http404
from django.shortcuts import render
from .models import Question # 从模型中引入 Question 模型
# 问题详情
def detail(request, question_id):
try:
question = Question.objects.get(id=question_id)
except Question.DoesNotExist:
raise Http404('问题不存在')
context = {'question':question}
return render(request, 'polls/detail.html', context)
2.2.2.5. 【如果取不出内容:get_object_or_404()】
# FirstApp/views.py 404第2种:get_object_or_404
from django.shortcuts import render, get_object_or_404
from .models import Question # 从模型中引入 Question 模型
# 问题详情
def detail(request, question_id):
# 对于尝试获取,获取不到显示404,有一个快捷函数
question = get_object_or_404(Question, id=question_id)
# get_object_or_404(模型名称, 可以传给get()的关键词参数)
# 类似用法的还有 get_list_or_404(),只是第二个参数传给 filter()
context = {'question':question}
return render(request, 'polls/detail.html', context)
2.2.3. urls.py 添加URL调度
初次时需要在应用目录添加 urls.py,并在开头加上:
from django.urls import path
from . import views # 从本级目录引入视图
app_name = 'firstapp' # URL的命名空间(为了区分多个应用的URL)
urlpatterns = []
在 urlpatterns 中添加 URLs(格式是:path('URL', 对应模型, name='独立名称'),
):
urlpatterns = [
# eg: /firstapp/
path('', views.index, name='index'),
# eg: /firstapp/5
path('<int:question_id>/', views.detail, name='detail'),
path('<int:question_id>/result/', views.result, name='result'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
- 其中的 URL 也可以写成 xxx.html,但大多数情况下不要这样做
2.3. 步骤3:创建模板
- 应用目录下创建 templates 文件夹
- templates 文件夹下创建命名空间 polls(建立文件夹的原因是命名空间,避免多个应用中相同名称的模板文件导致 Django 无法区分)
- polls 下放置模板文件
模板文件可参考:
<!-- FirstApp/templates/polls/index.html -->
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<!-- 之前的代码:硬编码 -->
<li><a href="firstapp/{{question.id}}">{{ question.question_text }}</a></li>
<!-- 改进的代码 -->
<li><a href="{% url 'firstapp:detail' question.id %}">{{ question.question_text }}</a></li>
<!-- 不去硬编码在URL编写器里的代码 用 {% url '命名空间:名字' 传入内容 %} 代替 -->
<!-- 这样,如果想要修改URL,可以直接在 urls.py 中修改,而无需修改这里了 -->
{% endfor %}
</ul>
{% else %}
<p>暂无问题</p>
{% endif %}
<!-- FirstApp/templates/polls/detail.html -->
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }}</li>
<-- 这里的 ....all 会被解释成为 .....all() -->
{% endfor %}
</ul>
2.4. 静态文件处理
静态文件存放位置:appname/static/静态文件命名空间/
静态文件使用(模板中):
{% load static %}
<link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}">