pip install Django
>>> import django
>>> print(django.get_version())
2.2.6
cd 到一个你想放置你代码的目录,然后运行以下命令:
django-admin startproject mysite
将会在当前目录下创建一个 mysite 目录,如下
mysite/
manage.py
mysite/
__init__.py
settings.py
urls.py
wsgi.py
运行下面的命令:
python manage.py runserver
你应该会看到如下输出:
Performing system checks...
System check identified no issues (0 silenced).
服务器正在运行,浏览器访问 https://127.0.0.1:8000/。你将会看到一个“祝贺”页面,随着一只火箭发射,服务器已经运行了。
默认情况下,runserver 命令会将服务器设置为监听本机内部 IP 的 8000 端口。
如果你想更换服务器的监听端口,请使用命令行参数。举个例子,下面的命令会使服务器监听 8080 端口:
python manage.py runserver 8080
如果你想要修改服务器监听的IP,在端口之前输入新的。比如,为了监听所有服务器的公开IP,使用:
python manage.py runserver 0:8000
在manage.py 所在的目录下,运行以下命令来创建一个应用:
python manage.py startapp polls
这将会创建一个 polls 目录(即该应用的顶级目录,名字可自定义),它的目录结构大致如下:
polls/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
views.py
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello, world. You're at the polls index.")
这是 Django 中最简单的视图。如果想看见效果,我们需要将一个 URL 映射到它——这就是我们需要 URLconf 的原因了。
为了创建 URLconf,请在 polls 目录里新建一个 urls.py 文件(当前应用的URLconf,用于定义当前应用涉及的访问路径)。你的应用目录现在看起来应该是这样:
polls/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
urls.py
views.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
’’
表示匹配到视图views.index的路径为空,即读取视图时,不需要额外添加路径,views.index:读取到该路径时调用的视图,name:为你的 URL 取名能使你在 Django 的任意地方唯一地引用它。
下一步是要在根 URLconf 文件中指定我们创建的 polls.urls 模块。在 mysite/urls.py 文件的 urlpatterns 列表里插入一个 include(), 如下(即在根URLconf下,把应用的URLconf包含进来,并定义访问的路径):mysite/urls.py
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('polls/', include('polls.urls')),
path('admin/', admin.site.urls),
]
何时使用 include()
- 当包括其它 URL 模式时你应该总是使用 include() , admin.site.urls 是唯一例外(因这是Django的mysite容器的配置管理路径)。
- 你现在把 index 视图添加进了 URLconf。通过以下命令验证是否正常工作:
运行python manage.py runserver
- 用你的浏览器访问 http://localhost:8000/polls/,你应该能够看见 "Hello, world. You‘re at the polls index." ,这是你在 index 视图中定义的。
- 函数 path() 具有四个参数,两个必须参数:route(路径) 和 view,两个可选参数:kwargs 和 name。其含义如下:
path() 参数: route
- route 是一个匹配 URL 的准则(类似正则表达式)。当 Django 响应一个请求时,它会从 urlpatterns 的第一项开始,按顺序依次匹配列表中的项,直到找到匹配的项。
- 这些准则不会匹配 GET 和 POST 参数或域名。例如,URLconf 在处理请求 https://www.example.com/myapp/ 时,它会尝试匹配 myapp/ 。处理请求 https://www.example.com/myapp/?page=3 时,也只会尝试匹配 myapp/。
path() 参数: view
- 当 Django 找到了一个匹配的准则,就会调用这个特定的视图函数,并传入一个 HttpRequest 对象作为第一个参数,被“捕获”的参数以关键字参数的形式传入。
path() 参数: kwargs
- 任意个关键字参数可以作为一个字典传递给目标视图函数。(即给目标视图提供参数)
path() 参数: name
- 为你的 URL 取名能使你在 Django 的任意地方唯一地引用它,尤其是在模板中。这个有用的特性允许你只改一个文件就能全局地修改某个 URL 模式。(即给前面的视图定义一个引用名)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'djangodb',
'USER': 'root',
'PASSWORD': 'root',
'HOST': '127.0.0.1',
'PORT': '3306',
}
}
文件头部的 INSTALLED_APPS 设置项。这里包括了会在你项目中启用的所有默认 Django 应用,默认开启的某些应用需要至少一个数据表,所以,在使用他们之前需要在数据库中创建一些表。可执行以下命令:(为已安装的应用创建相应的数据库表)
python manage.py migrate
这个 migrate
命令会检查 INSTALLED_APPS
设置,为其中的每个应用创建需要的数据表
查看 migrate
命令为 INSTALLED_APPS
设置创建了哪些表(sqlite数据库),用以下命令:
select * from sqlite_master;
若是配置的是MySQL,则使用以下命令查看:
show tables;
from django.db import models
import datetime
from django.utils import timezone
# Create your models here.
class Question(models.Model):
question_text = models.CharField(max_length=200)
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)
# 用ForeignKey 定义了一个关系。这将告诉 Django,每个 Choice 对象都关联到一个 Question 对象。
class Choice(models.Model):
# 定义外键和一对一关系的时候需要加on_delete选项,此参数为了避免两个表里的数据不一致问题
# on_delete有CASCADE、PROTECT、SET_NULL、SET_DEFAULT、SET()五个可选择的值;CASCADE:此值设置,是级联删除。
# 这里表示通过question字段来关联Question对象,删除Question对象时,也一并删除该对象下的所有Choice信息
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
def __str__(self):
return self.choice_text
INSTALLED_APPS
中添加设置。因为 PollsConfig
类写在文件 polls/apps.py
中(定义了应用名),所以它的点式路径是 ‘polls.apps.PollsConfig‘。在文件 mysite/settings.py
中 INSTALLED_APPS
子项添加点式路径后,它看起来像这样: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',
]
现在你的 Django 项目会包含 polls 应用。接着运行下面的命令:
python manage.py makemigrations polls
0001_initial.py
sqlmigrate :显示本次迁移命令会执行哪些 SQL 语句。
python manage.py sqlmigrate polls 0001
再次运行 migrate 命令,在数据库里创建新定义的模型的数据表:
python manage.py migrate
Applying polls.0001_initial... OK
数据库迁移被分解成生成和应用两个命令是为了让你能够在代码控制系统上提交迁移数据并使其能在多个应用里使用;这不仅仅会让开发更加简单,也给别的开发者和生产环境中的使用带来方便。
通过以下命令进入交互式 Python 命令行
python manage.py shell
我们使用这个命令而不是简单的使用 "Python" 是因为 manage.py 会设置 DJANGO_SETTINGS_MODULE
环境变量,这个变量会让 Django 根据 mysite/settings.py 文件来设置 Python 包的导入路径。
首先,我们得创建一个能登录管理页面的用户。请运行下面的命令:
python manage.py createsuperuser
键入你想要使用的用户名,然后按下回车键,如下:
Username: admin
然后根据提示输入想要使用的邮件地址:
Email address: admin@example.com
Django 的管理界面默认就是启用的。如果开发服务器未启动,用以下命令启动它:
python manage.py runserver
现在,打开浏览器,转到你本地域名的 "/admin/" 目录,访问管理页面 -- 比如
http://127.0.0.1:8000/admin/
from django.contrib import admin
from .models import Question
admin.site.register(Question)
from django.shortcuts import render, get_object_or_404
# Create your views here.
from django.http import HttpResponse
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)
def detail(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/detail.html', {'question': question})
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
from django.urls import path
from . import views
# 该当前的urls模块定义一个app_name,用于区分当前的URLCONF是属于哪个应用,方便后面模块引用这里的别名如detail时,能准确定位到该别名属于哪个URLCONF
app_name = 'polls'
urlpatterns = [
# ex: /polls/
path('', views.index, name='index'),
# ex: /polls/5/
path('<int:question_id>/', views.detail, name='detail'),
# ex: /polls/5/results/
path('<int:question_id>/results/', views.results, name='results'),
# ex: /polls/5/vote/
path('<int:question_id>/vote/', views.vote, name='vote'),
]
当某人请求你网站的某一页面时——比如说, "/polls/1/" ,Django 将会载入 mysite.urls 模块,因为这在配置项 ROOT_URLCONF
中设置了。然后 Django 寻找名为 urlpatterns 变量并且按序匹配正则表达式。在找到匹配项 ‘polls/‘,它切掉了匹配的文本("polls/"),将剩余文本——"1/",发送至 ‘polls.urls‘ URLconf 做进一步处理。在这里剩余文本匹配了 ‘
detail(request=<HttpRequest object>, question_id=1)
question_id=1
由 <int:question_id>
匹配生成。使用尖括号“捕获”这部分 URL,且以关键字参数的形式发送给视图函数。上述字符串的 :question_id> 部分定义了将被用于区分匹配模式的变量名(一般与视图中相应函数对应的参数名相同),而 int: 则是一个转换器决定了应该以什么变量类型匹配这部分的 URL 路径。
APP_DIRS
设置成了 True。这一选项将会让 DjangoTemplates 在每个 INSTALLED_APPS 文件夹中寻找 "templates" 子目录。这就是为什么尽管我们没有像在第二部分中那样修改 DIRS 设置,Django 也能正确找到 polls 的模板位置的原因。polls/templates/polls/index.html
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
{# href="{% url ‘name’ params %}" 其中:name为url.py 文件中某个url配置的name别名,params某个url对应的各个参数具体值;而polls:detail表示某个应用的某个别名#}
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
polls/views.py
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
# 这里的字典项context,在后面的模板页面如index.html引用时,会把这里的键如 latest_question_list的值 传给index.html中对应的变量
context = {'latest_question_list': latest_question_list}
return render(request, 'polls/index.html', context)
一个快捷函数: render()
- 「载入模板,填充上下文,再返回由它生成的 HttpResponse 对象」是一个非常常用的操作流程。于是 Django 提供了一个快捷函数,我们用它来重写 index() 视图:
这个render()函数,拿请求对象作为它的第一个参数,一个模板名作为它的第二个参数,一个字典作为它的第三个参数,把字典里的键值对传给模板对象,然后它返回一个该模板html对应的响应对象。
- 一个快捷函数:
get_object_or_404()
def detail(request, question_id):
# get_object_or_404()函数,拿一个Django模型作为它的第一个参数,和一个任意数量的关键字参数,它是通过模型里的get()函数来管理的,如果对象不存在,则抛出 Http404异常。
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/detail.html', {'question': question})
- 也有
get_list_or_404()
函数,工作原理和get_object_or_404()
一样,除了 get() 函数被换成了 filter() 函数。如果列表为空的话会抛出 Http404 异常。
使用模板系统
- 回过头去看看我们的 detail() 视图。它向模板传递了上下文变量 question 。下面是 polls/detail.html 模板里正式的代码:
polls/templates/polls/detail.html
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>
在 {% for %} 循环中发生的函数调用:question.choice_set.all
被解释为 Python 代码 question.choice_set.all() ,将会返回一个可迭代的 Choice 对象,这一对象可以在 {% for %} 标签内部使用。
在模板文件中通过应用名来引用和定位应用URLconf 中的别名
polls/templates/polls/index.html
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
{# href="{% url ‘name’ params %}" 其中:name为url.py 文件中某个url配置的name别名,params某个url对应的各个参数具体值;而polls:detail表示某个应用的某个别名#}
<li><a href="{% url 'polls:detail' question.id %}">{{ question.question_text }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
# as_view方法,这个方法只做一件事就是返回一个闭包,这个闭包像视图函数一样接收url解析器传送过来的参数
# 如果我们通过 def 定义视图函数,那么传入的这个可调用对象就是这个函数本身;而如果我们定义的是类视图,则必须调用类视图的 as_view 方法返回一个根据这个类生成的可调用对象
# DetailView 会根据 URLConf 中的 <int:pk> 或 <slug:slug> 筛选出一个 object;这里是依据主键值PK筛选出视图中model指定的对象
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
# 通用的基于类的视图(Class Based View)如下:
# 展示对象列表(比如所有用户,所有文章)- ListView
# 展示某个对象的详细信息(比如用户资料,比如文章详情) - DetailView
# 通过表单创建某个对象(比如创建用户,新建文章)- CreateView
# 通过表单更新某个对象信息(比如修改密码,修改文字内容)- UpdateView
# 用户填写表单后转到某个完成页面 - FormView
# 删除某个对象 - DeleteView
# 通用的基于类的视图(Class Based View)如下:
# 展示对象列表(比如所有用户,所有文章)- ListView
# 展示某个对象的详细信息(比如用户资料,比如文章详情) - DetailView
# 通过表单创建某个对象(比如创建用户,新建文章)- CreateView
# 通过表单更新某个对象信息(比如修改密码,修改文字内容)- UpdateView
# 用户填写表单后转到某个完成页面 - FormView
# 删除某个对象 - DeleteView
# template_name、context_object_name、model这些名字都是固定的
# 用generic继承基类视图,这里表示继承基类视图ListView
class IndexView(generic.ListView):
template_name = 'polls/index.html'
# listview默认使用object_list作为上下文变量。可使用context_object_name重命名。
context_object_name = 'latest_question_list'
# ListView视图默认取出该表所有数据。想要过滤自定义的数据,只能在get_queryset()中过滤
# get_context_data(self,**kwargs)#这个方法用来添加额外的内容到上下文变量中。
def get_queryset(self):
"""Return the last five published questions."""
return Question.objects.order_by('-pub_date')[:5]
# DetailView用来展示一个具体对象的详细信息。它需要URL提供访问某个对象的具体参数(如pk, slug值);即用于显示某一 Model 中的一个 object 的详细信息。
# 用属性 model 或 queryset 指定要操作的 Model 或 queryset;
# DetailView 会根据 URLConf 中的 <int:pk> 或 <slug:slug> 筛选出一个 object;
class DetailView(generic.DetailView):
# model指定了数据表。他的功能相当于取出了Question中的所有数据;也即定义要展示的数据模型
model = Question
# template_name指定跳转页面;也即定义要将数据模型展示在哪个模板上,用数据模型的数据,覆盖掉该模板上的属性变量
template_name = 'polls/detail.html'
class ResultsView(generic.DetailView):
model = Question
template_name = 'polls/results.html'
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
try:
# request.POST是一个类字典对象,让你可以通过关键字的名字获取提交的数据。 这个例子中
# request.POST['choice']以字符串形式返回选择的Choice的ID。 request.POST的值永远是字符串。即获取经由choice关键字提交过来的内容
# request.POST,request.GET: 分别可获得POST或GET方法的参数值,如上 request.POST['choice'] 可取得POST请求中,name值为choice的Value值。若POST参数中没有choice,则会产生KeyError。
selected_choice = question.choice_set.get(pk=request.POST['choice'])
except (KeyError, Choice.DoesNotExist):
# Redisplay the question voting form.
# KeyError异常时,返回detail页面,并传递question和error_message这两个参数给detail页面
return render(request, 'polls/detail.html', {
'question': question,
'error_message': "You didn't select a choice.",
})
else:
# 没有异常,则选中的选项的得票数加一
selected_choice.votes += 1
# 调用save()方法,把对象保存到数据库中
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.
# HttpResponseRedirect:响应重定向,重定向的 URL 将调用 'results' 视图来显示最终的页面;只接收一个参数:用户将要被重定向的 URL
# 在 HttpResponseRedirect 的构造函数中使用 reverse() 函数。这个函数避免了我们在视图函数中硬编码 URL。它需要我们给出我们想要跳转的视图的名字和该视图所对应的 URL 模式中需要给该视图提供的参数。
# 这里的polls:results表示,跳转到URLconf中app_name='polls',别名为results的链接,并给该链接路径传入需要的参数question.id
return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
{# 过滤器{{ 列表或数字|pluralize }} 单词的复数形式,如列表字符串个数大于1,返回s,否则返回空串#}
<li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>
<a href="{% url 'polls:detail' question.id %}">Vote again?</a>
<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 %} 循环中发生的函数调用:question.choice_set.all 被解释为 Python 代码 question.choice_set.all() , #}
{#将会返回一个可迭代的 Choice 对象,这一对象可以在 {% for %} 标签内部使用。#}
{#forloop.counter 表示 for 标签已经循环多少次#}
{#{% csrf_token %} 模板标签防御跨站请求伪造#}
{#<label>标签起绑定关联文本作用;<label>标签在单选按钮和复选按钮上经常被使用,使用该标签后,你点击单选按钮或复选按钮的文本也是可以选中的。#}
{#<label for="关联控件的id" form="所属表单id列表">文本内容</label>#}
{% 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>
from django.test import TestCase
# Create your tests here.
import datetime
from django.test import TestCase
from django.utils import timezone
from django.urls import reverse
from .models import Question
# 模型测试
class QuestionModelTests(TestCase):
def test_was_published_recently_with_future_question(self):
"""
was_published_recently() returns False for questions whose pub_date
is in the future.
"""
time = timezone.now() + datetime.timedelta(days=30)
future_question = Question(pub_date=time)
# 断言是否是False
self.assertIs(future_question.was_published_recently(), True)
def test_was_published_recently_with_old_question(self):
"""
was_published_recently() returns False for questions whose pub_date
is older than 1 day.
"""
time = timezone.now() - datetime.timedelta(days=1, seconds=1)
old_question = Question(pub_date=time)
self.assertIs(old_question.was_published_recently(), False)
def test_was_published_recently_with_recent_question(self):
"""
was_published_recently() returns True for questions whose pub_date
is within the last day.
"""
time = timezone.now() - datetime.timedelta(hours=23, minutes=59, seconds=59)
recent_question = Question(pub_date=time)
self.assertIs(recent_question.was_published_recently(), True)
# 视图测试
def create_question(question_text, days):
"""
Create a question with the given `question_text` and published the
given number of `days` offset to now (negative for questions published
in the past, positive for questions that have yet to be published).
"""
time = timezone.now() + datetime.timedelta(days=days)
return Question.objects.create(question_text=question_text, pub_date=time)
class QuestionIndexViewTests(TestCase):
def test_no_questions(self):
"""
If no questions exist, an appropriate message is displayed.
"""
response = self.client.get(reverse('polls:index'))
self.assertEqual(response.status_code, 200)
self.assertContains(response, "No polls are available.")
self.assertQuerysetEqual(response.context['latest_question_list'], [])
def test_past_question(self):
"""
Questions with a pub_date in the past are displayed on the
index page.
"""
create_question(question_text="Past question.", days=-30)
response = self.client.get(reverse('polls:index'))
self.assertQuerysetEqual(
response.context['latest_question_list'],
['<Question: Past question.>']
)
def test_future_question(self):
"""
Questions with a pub_date in the future aren't displayed on
the index page.
"""
create_question(question_text="Future question.", days=30)
response = self.client.get(reverse('polls:index'))
self.assertContains(response, "No polls are available.")
self.assertQuerysetEqual(response.context['latest_question_list'], [])
def test_future_question_and_past_question(self):
"""
Even if both past and future questions exist, only past questions
are displayed.
"""
create_question(question_text="Past question.", days=-30)
create_question(question_text="Future question.", days=30)
response = self.client.get(reverse('polls:index'))
self.assertQuerysetEqual(
response.context['latest_question_list'],
['<Question: Past question.>']
)
def test_two_past_questions(self):
"""
The questions index page may display multiple questions.
"""
create_question(question_text="Past question 1.", days=-30)
create_question(question_text="Past question 2.", days=-5)
response = self.client.get(reverse('polls:index'))
self.assertQuerysetEqual(
response.context['latest_question_list'],
['<Question: Past question 2.>', '<Question: Past question 1.>']
)
class QuestionDetailViewTests(TestCase):
def test_future_question(self):
"""
The detail view of a question with a pub_date in the future
returns a 404 not found.
"""
future_question = create_question(question_text='Future question.', days=5)
url = reverse('polls:detail', args=(future_question.id,))
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
def test_past_question(self):
"""
The detail view of a question with a pub_date in the past
displays the question's text.
"""
past_question = create_question(question_text='Past Question.', days=-5)
url = reverse('polls:detail', args=(past_question.id,))
response = self.client.get(url)
self.assertContains(response, past_question.question_text)
我们创建了一个 django.test.TestCase 的子类,并添加了一个方法,此方法创建一个 pub_date
时未来某天的 Question 实例。然后检查它的 was_published_recently()
方法的返回值——它 应该 是 False。
运行测试代码
python manage.py test polls
运行结果如下:
E:\Python_Selenium\project\DjangoProject\mysite>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.QuestionMode
lTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "E:\Python_Selenium\project\DjangoProject\mysite\polls\tests.py", line 24
, in test_was_published_recently_with_future_question
self.assertIs(future_question.was_published_recently(), True)
AssertionError: False is not True
----------------------------------------------------------------------
Ran 10 tests in 0.703s
FAILED (failures=1)
Destroying test database for alias 'default'...
polls/static/polls/style.css
/*li表示列表li的样式*/
li a {
color: green;
}
/*body表示页面主体部分的样式,images/background.gif表示相对于当前文件路径的相对路径,静态文件之间的相互引用,使用相对于当前文件路径的相对路径即可*/
body {
background: white url("images/background.gif") no-repeat;
}
from django.contrib import admin
# Register your models here.
from .models import Question
# admin.site.register(Question)
# class QuestionAdmin(admin.ModelAdmin):
# # fields:控制要显示的字段,可用小括号,也可用中括号
# fields = ['pub_date', 'question_text']
# fieldsets 元组中的第一个元素是字段集的标题,第二个元素是该标题下要显示的字段
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['question_text']}),
('Date information', {'fields': ['pub_date']}),
]
# 注册模型Question,并且按QuestionAdmin定义的样式显示(这里是按里面定义的次序排列模型字段)
# 这里为:注册Question模型,并按QuestionAdmin规定的样式显示
admin.site.register(Question, QuestionAdmin)
# 第二种添加关联对象,两个类的普通关联使用StackedInline;TabularInline 是表格的方式显示;这里通过 TabularInline(替代 StackedInline ),关联对象以一种表格式的方式展示,显得更加紧凑
class ChoiceInline(admin.TabularInline):
# 需要将哪个类嵌入,这里为嵌入3个Choice类
model = Choice
# 嵌入的数量
extra = 3
# fieldsets 元组中的第一个元素是字段集的标题,第二个元素是该标题下要显示的字段
# classes,一个列表包含额外的CSS classes 应用到 fieldset;通过默认的管理站点样式表定义的两个有用的classes 是 collapse 和 wide. Fieldsets 使用 collapse(默认展开) 样式将会在初始化时展开并且替换掉一个 “click to expand” 链接. Fieldsets 使用 wide 样式将会有额外的水平空格.
class QuestionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['question_text']}),
('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
]
# inlines内联,关联上面定义的类;这里是本类内联ChoiceInline类;这样,每次进入Question这个界面的时候,都会伴随着三个新增的Choice插槽
inlines = [ChoiceInline]
# list_display,控制在管理员的更改列表页面上显示哪些字段
list_display = ('question_text', 'pub_date', 'was_published_recently')
# list_filter,列表过滤器,这里表示按pub_date字段过滤列表
list_filter = ['pub_date']
# search_fields,搜索框,这里表示针对question_text字段的搜索框
search_fields = ['question_text']
# 注册模型Question,并且按QuestionAdmin定义的样式显示(这里是按里面定义的次序排列模型字段)
# 这里为:注册Question模型,并按QuestionAdmin规定的样式显示
admin.site.register(Question, QuestionAdmin)
# # 第一种添加关联对象Choice,仿照我们向后台注册 Question 一样注册 Choice
# admin.site.register(Choice)
在你的工程目录(指包含 manage.py 的那个文件夹)内创建一个名为 templates 的目录。模板可放在你系统中任何 Django 能找到的位置。(谁启动了 Django,Django 就以他的用户身份运行。)不过,把你的模板放在工程内会带来很大便利,推荐你这样做。
然后在 templates 目录内创建名为 admin 的目录,随后,将存放 Django 默认模板的目录(django/contrib/admin/templates)内的模板文件 admin/base_site.html 复制到这个目录内。最后的项目路径如下:
\mysite\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 %}
# 将 APP_DIRS 设置成了 True。这一选项将会让 DjangoTemplates 在每个 INSTALLED_APPS 文件夹中寻找 "templates" 子目录
# DIRS默认是空的,Django是怎么找到默认的后台模板的?因为APP_DIRS被置为True,Django会自动在每个应用包内递归查找templates / 子目录(不要忘了
# django.contrib.admin也是一个应用)
# DIRS 是一个包含多个系统目录的文件列表,用于在载入 Django 模板时使用,是一个待搜索路径
# BASE_DIR是你的工程目录(指包含 manage.py 的那个文件夹)
# 组织模板:就像静态文件一样,我们 可以 把所有的模板文件放在一个大模板目录内,这样它也能工作的很好。
# 但是,属于特定应用的模板文件最好放在应用所属的模板目录(例如 polls/templates),而不是工程的模板目录(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',
],
},
},
]
注意,所有的 Django 默认后台模板均可被复写。若要复写模板,像你修改 base_site.html 一样修改其它文件——先将其从默认目录中拷贝到你的自定义目录,再做修改。
目录内)。这将创建一个名为
dist 的目录并构建你自己的应用包, django-polls-0.1.tar.gz。安装打包程序
pip install --user django-polls/dist/django-polls-0.1.tar.gz
\mysite\mysite\settings.py
文件中,添加安装应用,如下将访问该应用的URI添加到django的URLconf中,在\mysite\mysite\urls.py文件中如下:
重启django平台,访问该URI即可打开该应用。
原文:https://www.cnblogs.com/jun-zi/p/12114432.html