【Python编程:从入门到实践】第二十章练习题

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未实现):

七.设置项目样式

  1. 执行命令“pip install django-bootstrap3”安装 django-bootstrap3程序,把该程序加入settings.py(图1);为了能使用Bootstrap提供的交互式元素,需要在settings.py加入代码(图2):

  2. 修改模板base.html(代码1)、index.html(代码2)、login.html(代码3)、register.html(代码4):
    {% load bootstrap3 %}
    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Pizzas</title>
        <!-- 引入css和js -->
        {% bootstrap_css %}
        {% bootstrap_javascript %}
    </head>
    
    <body>
        <!-- 导航栏 -->
        <nav class="navbar navbar-default navbar-static-top">
            <div class="container">
                <div class="navbar-header">
                    <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                    </button>
                    <a class="navbar-brand" href="{% url 'pizzas:index' %}">
    Pizzas</a>
                </div>
                <div id="navbar" class="navbar-collapse collapse">
                    <ul class="nav navbar-nav">
                        <li><a href="{% url 'pizzas:pizzas' %}">Pizzas</a></li>
                    </ul>
                    <ul class="nav navbar-nav navbar-right">
                        {% if user.is_authenticated %}
                        <li><a>Hello, {{ user.username }}.</a></li>
                        <li><a href="{% url 'users:logout' %}">log out</a></li>
                        {% else %}
                        <li><a href="{% url 'users:register' %}">register</a></li>
                        <li><a href="{% url 'users:login' %}">log in</a></li>
                        {% endif %}
                    </ul>
                </div>
                <!--/.nav-collapse -->
            </div>
        </nav>
        <div class="container">
            <div class="page-header">
                {% block header %}{% endblock header %}
            </div>
            <div>
                {% block content %}{% endblock content %}
            </div>
        </div> <!-- /container -->
    </body>
    
    </html>
    {% extends "pizzas/base.html" %}
    {% block header %}
    <div class='jumbotron'>
        <h1>Track your message.</h1>
    </div>
    {% endblock header %}
    {% block content %}
    <h2>
    	<a href="{% url 'users:register' %}">Register an account</a> to make your own pizzeria, and list the pizzas you're learning about.
    </h2>
    <h2>
    	Whenever you learn something new about a pizza, make an entry
    	summarizing what you've learned.
    </h2>
    {% endblock content %}
    {% extends "pizzas/base.html" %}
    {% load bootstrap3 %}
    {% block header %}
    <h2>Log in to your account.</h2>
    {% endblock header %}
    {% block content %}
    <form method="post" action="{% url 'users:login' %}" class="form">
        {% csrf_token %}
        {% bootstrap_form form %}
        {% buttons %}
        <button name="submit" class="btn btn-primary">log in</button>
        {% endbuttons %}
        <!-- 实参value告诉Django在用户成功登录后将其重定向到什么地方 —— 在这里是主页 -->
        <input type="hidden" name="next" value="{% url 'pizzas:index' %}" />
    </form>
    {% endblock content %}
    {% extends "pizzas/base.html" %}
    {% load bootstrap3 %}
    {% block header %}
    <h2>Register an account.</h2>
    {% endblock header %}
    {% block content %}
    <form method="post" action="{% url 'users:register' %}">
        {% csrf_token %}
        {% bootstrap_form form %}
        {% buttons %}
        <button name="submit" class="btn btn-primary">register</button>
        {% endbuttons %}
        <!-- 实参value告诉Django在用户成功登录后将其重定向到什么地方 —— 在这里是主页 -->
        <input type="hidden" name="next" value="{% url 'pizzas:index' %}" />
    </form>
    {% endblock content %}
  3. 修改模板pizzas.html(代码1)、pizza.html(代码2)、new_pizza.html(代码3)、new_topping.html(代码4)、edit_topping.html(代码5):
    {% extends "pizzas/base.html" %}
    {% block header %}
    <h1>Pizzas</h1>
    <h4 class="text-right"><a href="{% url 'pizzas:new_pizza' %}">Add new pizza</a></h4>
    {% endblock header %}
    {% block content %}
    <ul>
        {% for pizza in pizzas %}
        <li>
            <h4>
    			<a href="{% url 'pizzas:pizza' pizza.id %}">{{ pizza }}</a>
    			<span class="pull-right">{{ pizza.date_added|date:'M d, Y H:i' }}</span>
    		</h4>
        </li>
        {% empty %}
        <li>No pizzas have been added yet.</li>
        {% endfor %}
    </ul>
    {% endblock content %}
    {% extends 'pizzas/base.html' %}
    {% block header %}
    <h2>{{ pizza }}</h2>
    <h4 class="text-right"><a href="{% url 'pizzas:new_topping' pizza.id %}">Add new topping</a></h4>
    {% endblock header %}
    {% block content %}
    {% for topping in toppings %}
    <div class="panel panel-default">
        <div class="panel-heading">
            <h3>
                {{ topping.date_added|date:'M d, Y H:i' }}
                <small>
                    <a class="pull-right" href="{% url 'pizzas:edit_topping' topping.id %}">
                    edit topping</a>
                </small>
            </h3>
        </div>
        <div class="panel-body">
            {{ topping.name|linebreaks }}
        </div>
    </div> <!-- panel -->
    {% empty %}
    There are no toppings for this pizza yet.
    {% endfor %}
    {% endblock content %}
    {% extends "pizzas/base.html" %}
    {% load bootstrap3 %}
    {% block header %}
    <h2>Add a new pizza:</h2>
    {% endblock header %}
    {% block content %}
    <form action="{% url 'pizzas:new_pizza' %}" method='post' class="form">
        {% csrf_token %}
        {% bootstrap_form form %}
        {% buttons %}
        <button name="submit" class="btn btn-primary">add pizza</button>
        {% endbuttons %}
    </form>
    {% endblock content %}
    {% extends "pizzas/base.html" %}
    {% load bootstrap3 %}
    {% block header %}
    <h2>Add a new topping:</h2>
    {% endblock header %}
    {% block content %}
    <form action="{% url 'pizzas:new_topping' pizza.id %}" method='post'>
        {% csrf_token %}
        {% bootstrap_form form %}
        {% buttons %}
        <button name="submit" class="btn btn-primary">add topping</button>
        {% endbuttons %}
    </form>
    {% endblock content %}
    {% extends "pizzas/base.html" %}
    {% load bootstrap3 %}
    {% block header %}
    <h2>Edit topping:</h2>
    {% endblock header %}
    {% block content %}
    <form action="{% url 'pizzas:edit_topping' topping.id %}" method='post'>
        {% csrf_token %}
        {% bootstrap_form form %}
        {% buttons %}
        <button name="submit" class="btn btn-primary">save changes</button>
        {% endbuttons %}
    </form>
    {% endblock content %}
  4. 全部页面效果如下(分别index、register、login、pizzas、pizza、new_pizza、new_topping、edit_topping):

  5. 创建自定义错误页面,路径pizzeria/pizzeria下新建文件夹templates,文件夹新建模板404.html(代码1)、500.html(代码2):
    {% extends "pizzas/base.html" %}
    {% block header %}
    <h2>The item you requested is not available. (404)</h2>
    {% endblock header %}
    {% extends "pizzas/base.html" %}
    {% block header %}
    <h2>There has been an internal error. (500)</h2>
    {% endblock header %}
  6. 修改settings.py,使用自定义错误页面模板(图1),禁止默认的Django调试页面(图2,DEBUG设置False时,必须设置ALLOWED_HOSTS):

  7. 手工请求不存在的pizza或topping时,显示500错误(服务端错误),不符合实际情况;现把这两种情况改成显示404错误,修改pizzas--views.py(使用get_object_or_404):
    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
  8. 给pizza增加设置是否公开的功能,实现当登录状态下,只显示登录用户自己的数据;而未登录状态下,则显示公开的数据;先修改模型Pizza(pizzas--models.py)增加public属性(图1)、修改pizzas--forms.py(图2,增加字段“is public”和显示字段名)、修改pizzas--views.py(图3,判断登录和未登录情况,并注释“@login_required”),最后进行数据迁移(先执行命令“python manage.py makemigrations pizzas”,后执行命令“python manage.py migrate”);登录和未登录显示数据效果图(图4、图5):

    总结:到这里全部练习题更新完毕。

发布了31 篇原创文章 · 获赞 17 · 访问量 9697

猜你喜欢

转载自blog.csdn.net/king0964/article/details/103767121
今日推荐