【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):

    總結:到這裏全部練習題更新完畢。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章