以创建投票应用为例

Django 教程参考

1. 步骤精简

  1. 创建模型
    a. 纸和笔定义模型
    b. models.py 创建模型
    c. makemigrations / migrate 激活和迁移
    d. admin.py 放入管理
  2. 创建视图
    a. 纸和笔设计视图
    b. views.py 建立视图
    c. urls.py 添加 URL 调度
  3. 创建模板

2. 步骤详解

2.1. 步骤1:创建模型

2.1.1. 纸和笔定义模型

投票应用涉及两个主要模型:

  1. 问题:描述和发布时间
  2. 选项:描述和当前票数
  3. 每个选项对应一个问题

2.1.2. models.py 创建模型

打开应用的 models.py。参考代码:

  1. # FirstApp/models.py
  2. import datetime # Python标准datetime模块,用于运算时间(昨天、7天内……)
  3. from django.utils import timezone # Django时区相关工具,用于运算时间(当前)
  4. from django.db import models
  5. # Question 模型
  6. class Question(models.Model): # 每个模型都是 django.db.models.Model 的子类
  7. question_text = models.CharField(max_length=200) # 变量名 = 字段名
  8. pub_date = models.DateTimeField('发布日期')
  9. # 每个字段都是 Field 类的实例。
  10. # 比如 question_text 是 CharField 实例,pub_date 是 DateTimeField 实例
  11. def __str__(self):
  12. # 自定义 shell 中 Question.objects.all() 返回的内容
  13. # 也是控制在后台列表的默认显示内容
  14. return self.question_text
  15. def was_recently_published(self):
  16. return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
  17. # 上面代码的意思是,当发布日期大于等于昨天(昨天和今天),就返回
  18. # timezone.now() 当前时间
  19. # timezone.now() - datetime.timedelta(day=1) 表示昨天
  20. # timezone.now() - datetime.timedelta(day=7) 表示7天内
  21. # Choice 模型
  22. class Choice(models.Model):
  23. question = models.ForeignKey(Question, on_delete=models.CASCADE)
  24. # 定义关系,每个 Choice 对象关联到一个 Question 对象里
  25. choice_text = models.CharField(max_length=200)
  26. votes = models.IntegerField(default=0)
  27. def __str__(self):
  28. words = '选项 “{}” 获得{}票'.format(self.choice_text, self.votes)
  29. return words

2.1.3. makemigrations / migrate 激活模型

  1. 确认在项目 settings.py 中的 INSTALLED_APP 已经写好了应用(参考:使用应用)
  2. $ python manage.py makemigrations AppName
  3. $ python manage.py migrate

额外提示
• 如果想看创建表的SQL可以执行:
python manage.py sqlmigrate AppName 0001
• 如果想检查整个项目的问题:
python manage.py check

2.1.4. admin.py 放入管理

  1. 引入需要管理的模型:
  1. from .models import Question # 从同级 models 引入 Question 模型
  1. 注册模型:admin.site.register(Question)

2.2. 步骤2:创建视图

视图:一类具有相同功能和模板的网页的集合。每个视图 = 一个简单的函数。

比如建立博客,可能包含的视图:

  • 博客首页:展示最近的几项内容。
  • 内容详情页:详细展示某项内容。
  • 以年为单位的归档页:展示选中的年份里各个月份创建的内容。
  • 以月为单位的归档页:展示选中的月份里各天创建的内容。
  • 以天为单位的归档页:展示选中天里创建的所有内容。
  • 评论处理器:用于响应为一项内容添加评论的操作。

视图可以做的事:

  1. 必须:返回HttpResponse对象,或者404
  2. 读取数据库记录
  3. 使用模板引擎
  4. 生成 pdf、xml、zip等

2.2.1. 设计视图

投票应用主要涉及以下页面/视图(和URL):

  1. 问题索引:最近的几个问题列表(appname/)
  2. 问题详情:展示选项(appname/id/)
  3. 问题结果:展示选项投票情况(appname/id/result/)
  4. 投票处理:处理用户的选项投票(appname/id/vote/)

2.2.2. views.py 建立视图

打开应用的 views.py,参考代码:

2.2.2.1. 【返回类型3:Render】

  1. # FirstApp/views.py 返回第3种:Render返回
  2. from django.shortcuts import render
  3. from .models import Question # 从模型中引入 Question 模型
  4. # 应用首页:问题索引
  5. def index(request):
  6. # 获取最近的条目:Question.objects.order_by() 使用的是 Django 的数据库 API
  7. latest_question_list = Question.objects.order_by('-pub_date')[:5]
  8. context = {
  9. 'latest_question_list': latest_question_list,
  10. }
  11. return render(request, 'polls/index.html', context)

2.2.2.2. 【返回类型2:HttpResponse】

  1. # FirstApp/views.py 返回第2种:HttpResponse返回
  2. from django.shortcuts import render, HttpResponse
  3. # render:用于渲染
  4. # HttpResponse:用于返回 HttpResponse 对象
  5. from django.template import loader # 引入模板加载器
  6. from .models import Question # 从模型中引入 Question 模型
  7. # 应用首页:问题索引
  8. def index(request):
  9. # 获取最近的条目
  10. # Question.objects.order_by() 使用的是 Django 的数据库 API
  11. # [:5] 获取前5个,下标为0~4
  12. latest_question_list = Question.objects.order_by('-pub_date')[:5]
  13. template = loader.get_template('polls/index.html') # template目录中的位置
  14. context = { # 传给模板的内容(字典)
  15. 'latest_question_list': latest_question_list,
  16. }
  17. return HttpResponse(template.render(context, request))

2.2.2.3. 【返回类型1:直接输出】

  1. # FirstApp/views.py 返回第1种:直接输出
  2. from django.shortcuts import HttpResponse
  3. from .models import Question # 从模型中引入 Question 模型
  4. # 应用首页:问题索引
  5. def index(request):
  6. # 获取最近的条目
  7. # Question.objects.order_by() 使用的是 Django 的数据库 API
  8. # [:5] 获取前5个,下标为0~4
  9. latest_question_list = Question.objects.order_by('-pub_date')[:5]
  10. output = ''
  11. for question in latest_question_list:
  12. output += question.question_text + ','
  13. # 上面这三行,实际上可以写成,下面这一行:
  14. output = ','.join([q.question_text for q in latest_question_list])
  15. return HttpResponse(output)

2.2.2.4. 【如果取不出内容:try…except…】

  1. # FirstApp/views.py 404第1种:try...except...
  2. from django.http import Http404
  3. from django.shortcuts import render
  4. from .models import Question # 从模型中引入 Question 模型
  5. # 问题详情
  6. def detail(request, question_id):
  7. try:
  8. question = Question.objects.get(id=question_id)
  9. except Question.DoesNotExist:
  10. raise Http404('问题不存在')
  11. context = {'question':question}
  12. return render(request, 'polls/detail.html', context)

2.2.2.5. 【如果取不出内容:get_object_or_404()】

  1. # FirstApp/views.py 404第2种:get_object_or_404
  2. from django.shortcuts import render, get_object_or_404
  3. from .models import Question # 从模型中引入 Question 模型
  4. # 问题详情
  5. def detail(request, question_id):
  6. # 对于尝试获取,获取不到显示404,有一个快捷函数
  7. question = get_object_or_404(Question, id=question_id)
  8. # get_object_or_404(模型名称, 可以传给get()的关键词参数)
  9. # 类似用法的还有 get_list_or_404(),只是第二个参数传给 filter()
  10. context = {'question':question}
  11. return render(request, 'polls/detail.html', context)

2.2.3. urls.py 添加URL调度

初次时需要在应用目录添加 urls.py,并在开头加上:

  1. from django.urls import path
  2. from . import views # 从本级目录引入视图
  3. app_name = 'firstapp' # URL的命名空间(为了区分多个应用的URL)
  4. urlpatterns = []

在 urlpatterns 中添加 URLs(格式是:path('URL', 对应模型, name='独立名称'),):

  1. urlpatterns = [
  2. # eg: /firstapp/
  3. path('', views.index, name='index'),
  4. # eg: /firstapp/5
  5. path('<int:question_id>/', views.detail, name='detail'),
  6. path('<int:question_id>/result/', views.result, name='result'),
  7. path('<int:question_id>/vote/', views.vote, name='vote'),
  8. ]
  • 其中的 URL 也可以写成 xxx.html,但大多数情况下不要这样做

2.3. 步骤3:创建模板

  1. 应用目录下创建 templates 文件夹
  2. templates 文件夹下创建命名空间 polls(建立文件夹的原因是命名空间,避免多个应用中相同名称的模板文件导致 Django 无法区分)
  3. polls 下放置模板文件

模板文件可参考:

  1. <!-- FirstApp/templates/polls/index.html -->
  2. {% if latest_question_list %}
  3. <ul>
  4. {% for question in latest_question_list %}
  5. <!-- 之前的代码:硬编码 -->
  6. <li><a href="firstapp/{{question.id}}">{{ question.question_text }}</a></li>
  7. <!-- 改进的代码 -->
  8. <li><a href="{% url 'firstapp:detail' question.id %}">{{ question.question_text }}</a></li>
  9. <!-- 不去硬编码在URL编写器里的代码 用 {% url '命名空间:名字' 传入内容 %} 代替 -->
  10. <!-- 这样,如果想要修改URL,可以直接在 urls.py 中修改,而无需修改这里了 -->
  11. {% endfor %}
  12. </ul>
  13. {% else %}
  14. <p>暂无问题</p>
  15. {% endif %}
  1. <!-- FirstApp/templates/polls/detail.html -->
  2. <h1>{{ question.question_text }}</h1>
  3. <ul>
  4. {% for choice in question.choice_set.all %}
  5. <li>{{ choice.choice_text }}</li>
  6. <-- 这里的 ....all 会被解释成为 .....all() -->
  7. {% endfor %}
  8. </ul>

2.4. 静态文件处理

静态文件存放位置:appname/static/静态文件命名空间/

静态文件使用(模板中):

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