20-1 其他表单 :我们对登录页面和 add_topic 页面应用了 Bootstrap 样式。请对其他基于表单的页面做类似的修改: new_entry 页面、 edit_entry 页面和注册页面。
20-2 设置博客的样式 :对于你在第 19 章创建的项目 Blog ,使用 Bootstrap 来设置其样式。
20-3 在线博客 :将你一直在开发的项目 Blog 部署到 Heroku 。确保将 DEBUG 设置为 False ,并修改设置 ALLOWED_HOSTS ,让部署相当安全。
20-4 在更多的情况下显示 404 错误页面 :在视图函数 new_entry() 和 edit_entry() 中,也使用函数 get_object_or_404() 。完成这些修改后进行测试:输入类似于 http://localhost:8000/new_entry/99999/ 的 URL ,确认你能够看到 404 错误页面。
20-5 扩展 “ 学习笔记 ” :在 “ 学习笔记 ” 中添加一项功能,将修改推送到在线部署。尝试做一项简单的修改,如在主页中对项目作更详细的描述;再尝试添加一项更高级的功能,如让用户能够将主题设置为公开的。为此,需要在模型 Topic 中添加一个名为 public 的属性(其默认值为 False ),并在 new_topic 页面中添加一个表单元素,让用户能够将私有主题改为公开的。然后,你需要迁移项目,并修改 views.py ,让未登录的用户也可以看到所有公开的主题。将修改推送到 Heroku 后,别忘了迁移在线数据库。
优化第十七章项目“pizzeria ”,实现过程(部署Heroku未实现):
{% load bootstrap3 %}
Pizzas
{% bootstrap_css %}
{% bootstrap_javascript %}
{% block header %}{% endblock header %}
{% block content %}{% endblock content %}
{% extends "pizzas/base.html" %}
{% block header %}
Track your message.
{% endblock header %}
{% block content %}
Register an account to make your own pizzeria, and list the pizzas you're learning about.
Whenever you learn something new about a pizza, make an entry
summarizing what you've learned.
{% endblock content %}
{% extends "pizzas/base.html" %}
{% load bootstrap3 %}
{% block header %}
Log in to your account.
{% endblock header %}
{% block content %}
{% endblock content %}
{% extends "pizzas/base.html" %}
{% load bootstrap3 %}
{% block header %}
Register an account.
{% endblock header %}
{% block content %}
{% endblock content %}
{% extends "pizzas/base.html" %}
{% block header %}
Pizzas
Add new pizza
{% endblock header %}
{% block content %}
{% for pizza in pizzas %}
-
{
{ pizza }}
{
{ pizza.date_added|date:'M d, Y H:i' }}
{% empty %}
- No pizzas have been added yet.
{% endfor %}
{% endblock content %}
{% extends 'pizzas/base.html' %}
{% block header %}
{
{ pizza }}
Add new topping
{% endblock header %}
{% block content %}
{% for topping in toppings %}
{
{ topping.date_added|date:'M d, Y H:i' }}
edit topping
{
{ topping.name|linebreaks }}
{% empty %}
There are no toppings for this pizza yet.
{% endfor %}
{% endblock content %}
{% extends "pizzas/base.html" %}
{% load bootstrap3 %}
{% block header %}
Add a new pizza:
{% endblock header %}
{% block content %}
{% endblock content %}
{% extends "pizzas/base.html" %}
{% load bootstrap3 %}
{% block header %}
Add a new topping:
{% endblock header %}
{% block content %}
{% endblock content %}
{% extends "pizzas/base.html" %}
{% load bootstrap3 %}
{% block header %}
Edit topping:
{% endblock header %}
{% block content %}
{% endblock content %}
{% extends "pizzas/base.html" %}
{% block header %}
The item you requested is not available. (404)
{% endblock header %}
{% extends "pizzas/base.html" %}
{% block header %}
There has been an internal error. (500)
{% endblock header %}
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponseRedirect, Http404
# django2.0后把原来的django.core.urlresolvers包更改为了django.urls包
from django.urls import reverse
from django.contrib.auth.decorators import login_required
from .models import Pizza, Topping
from .forms import PizzaForm, ToppingForm
# Create your views here.
def index(request):
""" 披萨的主页 """
return render(request, 'pizzas/index.html')
# login_required() 的代码检查用户是否已登录,仅当用户已登录时, Django 才运行 topics() 的代码。如果用户未登录,就重定向到登录页面。
# @login_required
def pizzas(request):
""" 显示所有的配料 """
if request.user.is_authenticated:
# 登录状态下,只显示自己的数据
pizzas = Pizza.objects.filter(owner=request.user).order_by('date_added')
else:
# 未登录状态下,显示公开的数据
pizzas = list(Pizza.objects.all())
for pizza in pizzas[:]:
if pizza.public == False:
pizzas.remove(pizza)
context = {'pizzas': pizzas}
return render(request, 'pizzas/pizzas.html', context)
@login_required
def pizza(request, pizza_id):
""" 显示单个披萨及其所有的配料 """
# 获取不到pizza条目改成显示404
pizza = get_object_or_404(Pizza, id=pizza_id)
# 请求的披萨不归当前用户所有,我们就引发 Http404 异常,让 Django 返回一个 404 错误页面
check_topic_owner(pizza, request)
# date_added 前面的减号指定按降序排列
toppings = pizza.topping_set.order_by('-date_added')
context = {'pizza': pizza, 'toppings': toppings}
return render(request, 'pizzas/pizza.html', context)
@login_required
def new_pizza(request):
""" 添加新披萨 """
if request.method != 'POST':
# 未提交数据:创建一个新表单
form = PizzaForm()
else:
# POST 提交的数据 , 对数据进行处理
form = PizzaForm(request.POST)
# is_valid校验填写了必填信息和符合数据类型及长度
if form.is_valid():
new_pizza = form.save(commit=False)
new_pizza.owner = request.user
new_pizza.save()
# 将用户重定向到网页 pizzas
return HttpResponseRedirect(reverse('pizzas:pizzas'))
context = {'form': form}
return render(request, 'pizzas/new_pizza.html', context)
@login_required
def new_topping(request, pizza_id):
""" 在特定的披萨中添加新配料 """
# 获取不到配料条目显示404
pizza = get_object_or_404(Pizza, id=pizza_id)
# 解决一个用户可在另一个用户的学习笔记中添加条目问题
check_topic_owner(pizza, request)
if request.method != 'POST':
# 未提交数据 , 创建一个空表单
form = ToppingForm()
else:
# POST 提交的数据 , 对数据进行处理
form = ToppingForm(data=request.POST)
if form.is_valid():
# 传递了实参commit=False,让Django创建一个新的配料对象,并将其存储到new_topping中,但不将它保存到数据库中
new_topping = form.save(commit=False)
new_topping.pizza = pizza
# 把配料保存到数据库,并将其与正确的披萨相关联
new_topping.save()
return HttpResponseRedirect(reverse('pizzas:pizza', args=[pizza_id]))
context = {'pizza': pizza, 'form': form}
return render(request, 'pizzas/new_topping.html', context)
@login_required
def edit_topping(request, topping_id):
""" 编辑既有配料 """
# 获取不到配料编辑条目显示404
topping = get_object_or_404(Topping, id=topping_id)
pizza = topping.pizza
check_topic_owner(pizza, request)
if request.method != 'POST':
# 初次请求,使用当前条目填充表单(instance=topping)
form = ToppingForm(instance=topping)
else:
# POST 提交的数据,对数据进行处理
form = ToppingForm(instance=topping, data=request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('pizzas:pizza', args=[pizza.id]))
context = {'topping': topping, 'pizza': pizza, 'form': form}
return render(request, 'pizzas/edit_topping.html', context)
def check_topic_owner(pizza, request):
"""校验关联到的用户是否为当前登录的用户"""
if pizza.owner != request.user:
raise Http404
总结:到这里全部练习题更新完毕。