首先新建一个项目,名为 mysite,命令以下:css
django-admin startproject mysite # 或用 django-admin.py
运行成功,生成一些目录:html
mysite/ manage.py # 管理 Django 项目的命令行工具 mysite/ # 包,包含项目 __init__.py settings.py # 配置文件 urls.py # 路由文件 wsgi.py # WSGI 接口,web 服务器进入点,提供底层网络通讯功能,无需关心
python manage.py runserver # 默认以 8000 端口开启 python manage.py runserver 8080 # 指定端口
执行成功,看到输出以下信息:python
在浏览器中访问 http://127.0.0.1:8000/
,看到如下信息,表示开启成功(Django2.x 如下版本不同):mysql
如今咱们新建一个应用(app),名为 polls,命令以下:jquery
cd mysite # 切好到项目里面 python manage.py startapp polls
执行成功后,能够看到 mysite 中多了一个 polls文件夹,打开 polls,里面包含如下文件:web
polls/ __init__.py admin.py # Django 提供的后台管理程序 apps.py migrations/ # 数据库表生成记录 __init__.py models.py # 模型(与数据库相关) tests.py # 测试文件 views.py # 视图(一个视图函数表示一个页面)
项目与应用的区别sql
一个视图函数表示一个 Web 页面,在 polls/views.py
中编写:shell
from django.shortcuts import render, HttpResponse def index(request): """首页""" return HttpResponse('Is Ok!')
要调用视图,咱们须要先配置 urlconf
,让 Django 找到咱们的视图函数,在此以前咱们先把 app 添加到 settings.py
中:数据库
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'polls', # 最好空一行,以示区分 ]
配置 urlconfdjango
编写 mysite/urls.py
:
from django.contrib import admin from django.urls import path, include from polls import views # 导入视图函数 urlpatterns = [ path('admin/', admin.site.urls), path('index/', views.index, name='index'), ]
访问 http://127.0.0.1:8000/index/
,若是不出意外的话,会看到 Is Ok!
的字样~
多级路由
上面咱们只建立了一个 app,所以 url 路径配置在项目 mysite/urls.py
中毫无影响,可是当有多个应用且有多个相同的名字的视图时,为了不冲突,就须要用到多级路由了。
mysite/urls.py
:from django.contrib import admin from django.urls import path, include # 引入 include urlpatterns = [ path('admin/', admin.site.urls), path('polls/', include('polls.urls')), # include 就至关于多级路由,它会将去掉 url 前面的正则,将剩余字符串传递给下一级路由,即 polls/urls.py 来判断 ]
urls.py
文件,配置以下:from django.urls import path from polls import views # 导入视图函数 urlpatterns = [ path('index/', views.index, name='index'), # url(r'^index/', views.index, name='index'), # django2.x 之前版本 ]
那么访问地址将变成 http://127.0.0.1:8000/polls/index/
。
在 Django 中模型即指数据库,Django 内置 SQLite 数据库,能够直接使用它。可是 SQLite 通常仅用来测试使用,实际开发中通常不多不会使用。若是要使用其余数据库,须要配置 settings
,并安装相应驱动,下面咱们以 MySQL 为例。
经常使用数据库配置:
'django.db.backends.sqlite3', 'django.db.backends.postgresql', 'django.db.backends.mysql', 'django.db.backends.oracle',
settings.py
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'test', # 数据库名字,须要事先建立 'USER': 'root', # 用户名 'PASSWORD': '', # 密码 'HOST': '', # 留空默认为 localhost,数据库主机名 'PORT': '3306', } }
pip install pymysql
mysite/__init__.py
文件,配置以下:import pymysql pymysql.install_as_MySQLdb()
时区和语言
Django 默认使用 UTC 时区,以及英文,咱们能够将其修改成东八区和中文:
LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Shanghai'
Django 经过 ORM(Object Relation Mapping)对象关系映射,以面向对象的方式去操做数据库,即便不懂 SQL 语句也能够操做数据库。
咱们只需在模型中建立相应的 类以及字段便可,而后再执行命令,Django会自动帮咱们生成数据表:
在此以前咱们建立了一个投票应用 polls,如今咱们将建立两个数据表:问题表 Question
(用来存储问题以及发布事件)、以及选择人们的选择表Choice
。
下面咱们编写 polls/models.py
:
from django.db import models class Question(models.Model): # 每一个类必须继承 models.Model """数据表:问题表""" question_text = models.CharField(max_length=2000) # 问题内容 pub_date = models.DateTimeField('date published') # 发布日期 class Choice(models.Model): """数据表:选择表""" choice_text = models.CharField(max_length=200) # 选择 votes = models.IntegerField(default=0) # 是否已经投票 question = models.ForeignKey(Question, on_delete=models.CASCADE) # 外键关联
max_length
,这将限制其输入范围,非必须可是最好有所限制另外咱们经过外键(数据库内容) ForeignKey
将两个表关联起来,也就是这两张表是一对多关系。
一个问题能够有多个选择,除此以外数据表间关联还有 一对1、以及多对多关系,后面讲详细介绍。
模型建立和数据迁徙
接下来就是建立模型,执行 python manage.py makemigrations polls
,会看到如下提示:
这表示在 polls\migrations\0001_initial.py
文件中建立相关模型记录,当咱们对数据表操做时,会在上面有相应记录,保存在咱们的电脑磁盘上面。
接着咱们要将数据迁徙到真正的数据库中去,执行 python manage.py migrate
:
在 Pycharm 中打开 SQLite ,能够看到建立不少数据表:
Tips
_id
,表示与主表的 ID 进行关联models.py
python manage.py makemigrations app_name
为改动建立迁徙记录python manage.py migrate
,将操做同步至数据库上面咱们经过相应命令建立了模型,那么咱们该如何操做数据表中内容呢?Django为咱们提供了一系列的 API,能够很方便地就能操做数据。
python manage.py shell
>>> from polls.models import Question, Choice # 导入模型类 >>> Question.objects.all() # 获取全部 question 对象 <QuerySet []> # 由于里面还没数据,全部是空的 >>> from django.utils import timezone # 导入 Django 内置的 timezone 模块,获取时间,来自于依赖库 pytz >>> q = Question(question_text="What's new?", pub_date=timezone.now()) # 建立 question 对象 >>> q.save() # 保存到数据库 >>> q.id # 经过对象属性调用方式,访问模型中字段的值 1 >>> q.question_text "What's new?" >>> q.pub_date datetime.datetime(2019, 2, 28, 8, 10, 18, 766500, tzinfo=<UTC>) # 修改字段的值,再保存 >>> q.question_text = "What's up?" >>> q.save() # .all() 方式查询数据库中全部对象,这里是 question 对象 >>> Question.objects.all() <QuerySet [<Question: Question object (1)>]>
在上面咱们访问 Question 中全部对象时,获得是一个 object
对象,这样显示很不友好,为此咱们能够为模型添加一个 __str()__
方法,使其可以更具备可读性:
from django.db import models import datetime from django.utils import timezone class Question(models.Model): ... def __str__(self): return self.question_text # 返回的是 question_text,而不是 object class Choice(models.Model): ... def __str__(self): return self.choice_text
>>> from polls.models import Question, Choice >>> Question.objects.all() <QuerySet [<Question: What's up?>]> # 关键字查询 filter() 方法过滤 id=1 >>> Question.objects.filter(id=1) <QuerySet [<Question: What's up?>]> # 查询 question_text 以 What 开头的 question >>> Question.objects.filter(question_text__startswith="What") <QuerySet [<Question: What's up?>]> # 导入 timezone # 查询今年发布的问题 >>> from django.utils import timezone >>> current_year = timezone.now().year # 获取今年时间:2019 >>> Question.objects.get(pub_date__year=current_year) # __year=2019 <Question: What's up?> # 查询不存在的 ID,出现异常 >>> Question.objects.get(id=2) Traceback (most recent call last): File "<console>", line 1, in <module> File "E:\Python_virtualenvs\for_django\lib\site-packages\django\db\models\manager.py", line 82, in manager_method return getattr(self.get_queryset(), name)(*args, **kwargs) File "E:\Python_virtualenvs\for_django\lib\site-packages\django\db\models\query.py", line 399, in get self.model._meta.object_name polls.models.Question.DoesNotExist: Question matching query does not exist. # pk 即 primary key 缩写,与 id 等同 >>> Question.objects.get(pk=1) <Question: What's up?> >>> q = Question.objects.get(pk=1) # 建立 Question 对象 >>> q.choice_set.all() # 经过 数据表名_set.all() 方式得到与其关联的数据表的全部对象 <QuerySet []> # 建立三个 choices >>> 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) >>> c.question <Question: What's up?> >>> q.choice_set.all() <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]> >>> q.choice_set.count() 3 >>> Choice.objects.filter(question__pub_date__year=current_year) <QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]> >>> c = q.choice_set.filter(choice_text__startswith='Just hacking') >>> c.delete() # delete() 删除对象 (1, {'polls.Choice': 1})
上面是官方文档提供的一些例子,还有更多的有关 API 的操做,咱们将在后面学习到。
总结
一、建立对象 q = Question.objects.all() # QuerySet 对象集合 q = Question.objects.filter() # QuerySet 对象集合 q = Question.objects.get() # QuerySet 对象,一个 二、插入数据 q = Question(question_text="What's up?", pub_date=timezone.now()) # 方法一 q.save() 访问数据: q.id q.pub_date Question.objects.create(question_text="What's up?", pub_date=timezone.now()) # 方法二 三、查询数据 q = Question.objects.get(id=1) # 经过 q.数据表名_set.all() 方式得到与其关联的数据表对象 q.choice_set.all() # <QuerySet [<Choice: Not much>, <Choice: The sky>]> 四、删除数据 q.delete()
Django 为咱们提供了一个后台管理工具 Admin,能够对数据进行简单的增删改查等,简单易用,并支持拓展。
建立管理员用户
python manage.py createsuperuser # 运行命令,新建用户名、邮箱和密码 # username: xxx # email:xxx@qq.com # password:xxx
注册应用
将模型中的类注册到 polls/admin.py
中,接收站点的管理:
from django.contrib import admin from polls.models import Question, Choice admin.site.register(Question) admin.site.register(Choice)
访问 Admin
访问 http://127.0.0.1:8000/admin/
,输入刚才建立的用户名和密码:
样式定制
修改 polls/admin.py
:
from django.contrib import admin from polls.models import Question, Choice # 定制样式,更多样式见官方文档 class QuestionAdmin(admin.ModelAdmin): list_display = ('id', 'question_text', 'pub_date') # 要显示的字段 list_editable = ('question_text', 'pub_date') # 可编辑的 admin.site.register(Question, QuestionAdmin) admin.site.register(Choice)
Django 中每个网页都是经过视图函数来处理的,在 polls 应用中,咱们将建立如下四个视图:
URL | 视图函数 | 模板 | 说明 |
---|---|---|---|
/index/ | index() | index.html | 主页,显示最新问题 |
/results/ | results() | results.html | 投票结果 |
/detail/ | detail() | detail.html | 问题详细描述 |
/vote/ | vote() | vote.html | 投票动做,是否投票 |
mysite/urlconf
,以便可以找到相应视图函数:from django.contrib import admin from django.urls import path, include from polls import views urlpatterns = [ path('admin/', admin.site.urls), path('index/', views.index, name='index'), path('detail/', views.detail, name='detail'), path('results/', views.results, name='results'), path('vote/', views.vote, name='vote'), ]
polls/views.py
:from django.shortcuts import render, HttpResponse def index(request): """首页""" return HttpResponse('Is Ok!') def detail(request): """问题详细描述""" return HttpResponse('问题详细描述') def results(request): """投票结果""" return HttpResponse('投票结果') def vote(request): """是否投票""" return HttpResponse('是否已经投票')
如今视图函数已经建立好了,咱们能够访问相应视图看看 http://127.0.0.1:8000/detail/
返回的是什么。
在上面的视图函数中,咱们使用了 HttpResponse
对象返回了一个字符串,而实际开发中,咱们获得的都是一个 HTML页面。这就须要用到咱们的模板系统了。
在 polls 目录下建立一个 templates
目录,再在 templates
目录下建立一个新的 polls
目录。而后在 polls
中建立相应的模板文件(其路径polls/templates/polls/
),如:index.html/detail.html
等。
为何要再多建立一个 polls 目录
当有另外一个 app 也有 index.html
时,能够避免 Django 匹配错误。
配置 templates
要想 Django 能找到 templates 中的模板文件,那么还要配置下 settings
:
# 当 templates 在 mysite/templates 下,不要添加 polls TEMPLATE_DIRS = (os.path.join(BASE_DIR, 'polls', 'templates'),) 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', ], }, }, ]
render()
函数,用于渲染模板文件,render()
语法格式:render(request, template_name, context=None) # 三个参数,第一个固定为请求对象request,第二个是要渲染的模板文件,第三个是个可选参数,即要传递的数据,是个字典格式
编辑 polls/views.py
:
from django.shortcuts import render, HttpResponse from .models import Question def index(request): """首页""" question_list = Question.objects.all() # 取出 Question 中全部 question return render(request, 'polls/index.html', {'question_list': question_list}) def detail(request, question_id): """问题详细描述""" question = Question.objects.get(id=question_id) return render(request, 'polls/detail.html', {'question': question})
当咱们访问 http://127.0.0.1:8000/index/
时,index() 函数会处理咱们的视图。它从 Question 取出全部的问题对象,并渲染到模板中。
polls/templates/polls/index.html
:<!--index.html--> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> {% for question in question_list %} <!-- 至关于访问 <a href='detail/1/'></a>--> <li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a> </li> {% endfor %} </body> </html>
在模板文件 index.html
中,咱们使用 for 循环将全部问题循环,当咱们点击其中的 a 标签的连接时,将会被定位到 http://127.0.0.1:8000/detail/1
中。
polls/templates/polls/detail.html
:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Detail</title> </head> <body> <h1>{{ question.question_text }}</h1> <ul> {% for choice in question.choice_set.all %} <li>{{ choice.choice_text }}</li> {% endfor %} </ul> </body> </html>
mysite/urls.py
:from django.contrib import admin from django.urls import path, include from polls import views urlpatterns = [ path('admin/', admin.site.urls), path('index/', views.index, name='index'), # 咱们将 'detail/' 修改成: 'detail/<int:question_id>',以可匹配 http://127.0.0.1:8000/detail/1 这样的路径 path('detail/<int:question_id>', views.detail, name='detail'), ]
在这里咱们将 'detail/'
修改成:'detail/<int:question_id>'
,以可匹配 http://127.0.0.1:8000/detail/1
这样的路径。其中 <int: question_id>
将匹配到一个正整数,另外不要忘了在视图函数中也要接收相应 question_id
:
def detail(request, question_id): """问题详细描述""" question = Question.objects.get(id=question_id) return render(request, 'polls/detail.html', {'question': question})
这里咱们用的是 Django 提供的模板语言,将数据库中的数据显示在页面上,后面将详细介绍。
当咱们访问不存在的路径时,会返回一个 Http404
,咱们能够定制下让其返回咱们想要的内容,编辑 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 不存在") return render(request, 'polls/detail.html', {'question': question})
另外 Django 也为咱们提供了一个快捷函数 get_object_or_404()
,只需一行便可替代上面多行:
from django.shortcuts import get_object_or_404, render from .models import Question def detail(request, question_id): question = get_object_or_404(Question, pk=question_id) # 第一个参数:模型,第二个:任意关键字 return render(request, 'polls/detail.html', {'question': question})
什么是 URL 的命名空间呢?就是给每个 URL 路径,添加一个 别名,它有以下几点好处:
index.html
中,咱们使用的就是 URL 命名空间,而不是 <a href='/detail/{{question.id}}'
这样的硬编码。这样在咱们修改匹配方法时,不须要作大量的修改。添加命名空间
from django.contrib import admin from django.urls import path, include from polls import views urlpatterns = [ path('admin/', admin.site.urls), path('index/', views.index, name='index'), # 其中 name='index' 即为 URL的 命名空间 path('detail/<int:question_id>', views.detail, name='detail'), ]
当有多个应用时
当有多个应用时,咱们只需在 urls.py
中添加一个 app_name
,并在使用时带上它便可:
... app_name = 'polls' # 添加这行 urlpatterns = [ path('admin/', admin.site.urls), path('index/', views.index, name='index'), # 其中 name='index' 即为 URL的 命名空间 path('detail/<int:question_id>', views.detail, name='detail'), ]
使用时,必定要记得带上 app_name
:
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
在建立表单以前,咱们先来分析下程序的总体运行流程:
从流程中能够看出,咱们要在问题详细页面提供单选框,以供用户选择,下面咱们来建立第一个表单:
polls/detail.html
:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Detail</title> </head> <body> <!--问题--> <h1>{{ question.question_text }}</h1> <!-- 错误信息 --> {% if error_message %} <p>{{ error_message }}</p> {% endif %} <form action="{% url 'vote' question.id %}" method="post"> {% csrf_token %} <!--csrf 攻击,表单提交必须带上这个--> <!-- 经过 question.choice_set.all 得到全部 Choice 选项 --> {% for choice in question.choice_set.all %} <!--choice一、choice2--> <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}"> <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label> {% endfor %} <!-- 提交 --> <input type="submit" value="vote"> </form> </body> </html>
detail.html
模板文件中,咱们建立了一个表单,当用户点击提交时,会被提交到 action 对应的 URL 中去。question.choice_set.all
得到全部 Choice 选项,并循环它。choice
,值为选项的 id。{% csrf_token %}
,不然将被禁止提交。mysite/urls.py
:# /index/ path('index/', views.index, name='index'), # /detail/1/ path('detail/<int:question_id>', views.detail, name='detail'), # /results/1/ path('results/<int:question_id>', views.results, name='results'), # /vote/1/ path('vote/<int:question_id>', views.vote, name='vote'),
polls/views.py
:from django.shortcuts import render, HttpResponse, get_object_or_404, redirect from .models import Question, Choice from django.http import HttpResponseRedirect from django.urls import reverse def vote(request, question_id): """处理投票""" print(question_id) question = get_object_or_404(Question, id=question_id) try: choice_id = request.POST.get('choice', None) print(choice_id) selected_choice = question.choice_set.get(id=choice_id) except (KeyError, Choice.DoesNotExist): # choice 没找到,从新返回表单页面,并给出提示信息 return render(request, 'polls/detail.html', {'question': question, 'error_message': '你没用选择选项!'}) else: selected_choice.votes += 1 selected_choice.save() ret = reverse('results', args=(question.id,)) # /results/1 return HttpResponseRedirect(ret)
question_id
为问题所对应的 iddetail.html
模板中,咱们将选项的 id 提交到了后台,经过 request.POST.get('choice')
咱们能够得到用户选择的选项 idresults
。polls/results.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>结果</title> </head> <body> <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 'detail' question.id %}">Vote again?</a> </body> </html>
至此一个简单的公共投票系统已大体编写完成,如下为演示:
在视图 polls/views
中,咱们写了大量的相似于 index()
的重复代码,存在冗余问题。
Django 为咱们提供了一种 通用视图系统,将常见的模式抽象画,能够删去不少冗余代码。为此咱们须要如下三个步骤:
转换 URLconf
编辑 mysite/urls.py
:
urlpatterns = [ path('admin/', admin.site.urls), path('index/', views.IndexView.as_view(), name='index'), path('detail/<int:pk>', views.DetailView.as_view(), name='detail'), path('results/<int:pk>', views.ResultsView.as_view(), name='results'), path('vote/<int:question_id>', views.vote, name='vote'), ]
在这里咱们将 question_id
修改成 pk,这是由于通用视图从 url 中匹配的将是主键 pk。
修改视图
from django.views import generic class IndexView(generic.ListView): template_name = 'polls/index.html' # 模板名称 context_object_name = 'question_list' # 返回给模板的变量 def get_queryset(self): return Question.objects.all() 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): pass
{'question_list':question_list}
中的 question_list
<app_name>/<model name>_list.html
更多有关通用视图:https://docs.djangoproject.com/zh-hans/2.1/topics/class-based-views/
测试是实际开发中不可或缺的一部分,它能够:
测试分为手动测试和自动测试,手动测试每每费时费力,效率低下。咱们能够借助一些测试模块,如:TestCase,自动帮咱们完成测试工做,Django也有自动测试程序,它也是基于 TestCase 模块来实现的。
在模型 models.py
中,咱们给 Question 定义了一个 was_published_recently()
方法,用于返回问题是不是最近发布的,当 Question 在最近一天发布时返回 True
。
class Question(models.Model): """数据表:问题表""" question_text = models.CharField(max_length=2000) # 问题内容 pub_date = models.DateTimeField('date published') # 发布日期 def __str__(self): return self.question_text def was_published_recently(self): # 当前时间减去前一天,与问题发布时间比较 return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
进入 Django shell 环境:
>>> import datetime >>> from django.utils import timezone >>> from polls.models import Question # 建立一个在发布日期 30 天后的问题对象 >>> future_question = Question(pub_date=timezone.now() + datetime.timedelta(days=30)) # 测试返回值,发现也是 True >>> future_question.was_published_recently() True
咱们建立了一个在发布日期 30 天后的问题,测试发现仍是返回 True,也就是说这里被容许在将来时间发布问题,这就是个 bug。
编写 polls/tests.py
:
from django.test import TestCase import datetime from django.utils import timezone from .models import Question class QuestionModelTests(TestCase): def test_was_published_recently_with_future_question(self): # 建立一个 pub_date 是将来30天后的 Question 示例,而后检查 was_published_recently() 的返回值,它应该是 False time = timezone.now() + datetime.timedelta(days=30) future_question = Question(pub_date=time) self.assertIs(future_question.was_published_recently(), False)
执行 python manage.py test polls
,会看到结果:
Creating test database for alias 'default'... System check identified no issues (0 silenced). F ====================================================================== FAIL: test_was_published_recently_with_future_question (polls.tests.QuestionModelTests) ---------------------------------------------------------------------- Traceback (most recent call last): File "E:\Python_virtualenvs\for_django\Projects\mysite\polls\tests.py", line 11, in test_was_published_recently_with_future_qu estion self.assertIs(future_question.was_published_recently(), False) AssertionError: True is not False ---------------------------------------------------------------------- Ran 1 test in 0.016s FAILED (failures=1) Destroying test database for alias 'default'...
咱们建立了一个 pub_dae
值为 30 天后的 Question 实例,用 assertls()
方法判断是否返回 False,结果发现返回 True。
咱们要让 pub_date
是将来某天时, Question.was_published_recently()
返回 False,修改 polls/models.py
:
def was_published_recently(self): now = timezone.now() return now - datetime.timedelta(days=1) <= self.pub_date <= now
再进行测试,发现测试经过。测试在项目开发中很重要,也很经常使用,在这里咱们只是作个大概的了解,到后面再详细的探讨。
静态文件即 Web 应用程序所要用到的一些必要文件,如:图片、JS 脚本、CSS 样式等。一个完整的 Web 应用应该有本身独立静态文件、模板文件,也就是说须要和项目自己区分开。
在应用 polls 下新建一个 static 的目录,再新建一个以应用名字为名的文件夹,最后再分类存储各类静态文件,其目录结构是这样的:
配置静态文件
与模板 templates
同样,再使用前,须要先配置好静态文件,这样 Django 才能找到,编辑 settings.py
:
STATIC_URL = '/static/' STATICFILES_DIRS = ( os.path.join(BASE_DIR, 'polls', 'static'), ) # 必定不要忘记最后的逗号
使用静态文件
在 polls/static/polls/
下建立一个 images
目录用来存储图片,再建立一个 css
目录用来存储 CSS 文件。而后在新建一个 style.css
的文件。
下面咱们来给首页 index.html
添加背景图片,编写如下代码:
li a{ color: red; } body { background: white url("images/2.png") no-repeat; }
而后在 index.html
中来加载 style.css
文件:
{% load static %} <!--引入 static--> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <!--再把 style.css 加载进来 --> <link rel="stylesheet" href="{% static 'polls/css/style.css' %}"> </head> <body> {% for question in question_list %} <li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a> </li> {% endfor %} </body> </html>
咱们再刷新下,发现已经给首页添加好了背景图片。除此以外咱们还能够在模板文件中直接使用静态文件,如:在模板中使用 jQuery
:
# 一样地,也要先引入 static {% load static %} <script src="{% static 'polls/js/jquery-3.1.1.js' %}"></script>