close

Python Django 學習紀錄:架設個人部落格(七)

導航欄添加用戶操作

 

關於登錄登出還有許多不方便的操作尚未修正

也為了銜接之後的自定義用戶模型所以創建了用戶應用

並且將登錄登出的相關文件、方法遷移到用戶應用

而最後則是將上次撰寫的模態框登錄全局化

一.、功能設計

  1. 創建用戶應用,將登錄登出相關文件、方法移入其中
  2. 導航欄右側添加登錄、註冊與用戶訊息、登出
  3. 模態框登入全局化

二.、便捷登錄登出系統

1.創建用戶應用與遷移用戶相關文件、方法

(1)創建 user 應用

在虛擬環境下輸入以下指令:   

python manage.py startapp user

(2)註冊 user 應用

在<settings.py>註冊user應用:   

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'ckeditor',
    'ckeditor_uploader',
    'blog',
    'read_statistic',
    'comment',
    'likes',
    'user',
]

記得進行數據庫遷移 (makemigrations)與應用 (migrate)

(3)遷移用戶相關文件、方法

將用戶相關的文件、方法移至 user 應用

a.遷移相關方法

原先在mysite專案<views.py>的以下方法移至 user 應用的 <views.py>:

login、login_for_modal、 register

b.遷移方法引用的套件

把這些方法會引用到的套件添加至user應用<views.py>

mysite專案的<views.py>可移除多餘的套件

c.遷移相關表單

將 mysite 專案的 <forms.py> 移至 user應用(此表單文件只與登入、註冊相關)

blog應用有引用<forms.py>文件,所以需要修改導入的路徑

由 mysite.forms 改為 user.forms

(3)遷移相關模板

在 user 應用創建 templates目錄

並在templates目錄中創建user文件夾

將登入、註冊模板<login.html>、<register.html>

遷移至user應用templates目錄的user文件夾中

而由於更動模板頁面的位置,在login、register方法中的路徑也要更改:   

    return render(request, "user/login.html", context)
    return render(request, "user/register.html", context)

(4)設置url路徑

將用戶相關方法遷移後,尚未設置路徑

在user應用創建<urls.py>文件

把原先在mysite的用戶相關方法路徑移至user應用的<urls.py>:   

from django.urls import path
from . import views

urlpatterns = [
    path('login/', views.login, name='login'),
    path('login_for_modal/', views.login_for_modal, name='login_for_modal'),
    path('register/', views.register, name='register'),

]

在mysite專案<urls.py>添加user應用路徑:   

    path('user/',include('user.urls')),

2.登出功能與個人資料頁面

(1)撰寫登出、個人資料方法

在user應用的<views.py>撰寫登出方法:   

def logout(request):
    auth.logout(request)
    return redirect(request.GET.get('from',reverse('home'))) 

def user_info(request):
    context={}
    return render(request,'user/user_info.html',context)

(2)設置路徑

在user應用的<urls.py>添加登出路徑:   

    path('logout/', views.logout, name='logout'),
    path('user_info/', views.user_info, name='user_info'), 

(3)個人資料頁面

由於用戶系統使用django內建的User.model

可以參考 Django2.0文件 auth套件的部分,來顯示用戶資料

並且使用javascript判斷是否登入,如果未登入就會跳轉到首頁:   

{% extends "base.html" %}

{% block title %}
個人資料
{% endblock %}

{% block nav_blog_active %}active{% endblock %}

{% block content %}
    <div class="container">
        <div class="row">
            <!-- 響應式 -->
            <div class="col-xs-10 col-xs-offset-1">
                {% if user.is_authenticated %}
                    <h2>{{ user.username }}</h2>
                    <ul>
                        <li>暱稱:<a href="#">修改暱稱</a></li>
                        <li>信箱:{% if user.email %}{{ user.email }}{% else %}未綁定 <a href="#">綁定信箱</a>{% endif %}</li>
                        <li>上一次登錄時間:{{ user.last_login|date:"Y-m-d H:i:s" }}</li>
                        <li><a href="#">修改密碼</a></li>
                    </ul>
                {% else %}
                    <span>未登錄,跳轉到首頁...</span>
                    <script type="text/javascript">
                        window.location.href =  '/';
                    </script>
                {% endif %}
            </div>
        </div>
    </div>

{% endblock %}

(4)已登錄頁面跳轉

如果用戶已經登錄,例如<login.html>的上一頁是<register.html>就會產生錯誤

所以加入一個判斷用戶是否登錄,如果已經登錄就會跳轉至首頁

以下為<login.html>的代碼:   

            <div class="col-xs-12 col-md-4 col-md-offset-4">
                {% if not user.is_authenticated %}
                    <!-- 添加面板 -->
                    <div class="panel panel-default">
                        <!-- 面板標題 -->
                        <div class="panel-heading">登錄</div>
                        <div class="panel-body">
                            <!-- 表單 -->
                            <form action="" method="POST">
                                {% csrf_token %}
                                <!-- 進行表單細部調整 -->
                                {% for field in login_form %}
                                    <!-- 移除冒號 -->
                                    <label for="{{ field.id_for_label }}">{{field.label}}</label>
                                    {{ field }}
                                    <!-- 錯誤訊息紅字顯示 -->
                                    <p class="text-danger">{{ field.errors.as_text }}</p>
                                {% endfor %}
                                <span class="pull-left text-danger">{{ login_form.non_field_errors }}</span>
                                <input type="submit" value="登錄" class="btn btn-primary pull-right">
                            </form>
                        </div>
                    </div>
                {% else %}
                    <span>已登錄,跳轉到首頁...</span>
                    <script type="text/javascript">
                        window.location.href =  '/';
                    </script>
                {% endif %}
            </div>

以下為<register.html>的代碼:   

            <div class="col-xs-12 col-md-4 col-md-offset-4">
                {% if not user.is_authenticated %}
                    <!-- 添加面板 -->
                    <div class="panel panel-default">
                        <!-- 面板標題 -->
                        <div class="panel-heading">註冊</div>
                        <div class="panel-body">
                            <!-- 表單 -->
                            <form action="" method="POST">
                                {% csrf_token %}
                                <!-- 進行表單細部調整 -->
                                {% for field in reg_form %}
                                    <!-- 移除冒號 -->
                                    <label for="{{ field.id_for_label }}">{{field.label}}</label>
                                    {{ field }}
                                    <!-- 錯誤訊息紅字顯示 -->
                                    <p class="text-danger">{{ field.errors.as_text }}</p>
                                {% endfor %}
                                <span class="pull-left text-danger">{{ reg_form.non_field_errors }}</span>
                                <input type="submit" value="註冊" class="btn btn-primary pull-right">
                            </form>
                        </div>
                    </div>
                {% else %}
                    <span>已登錄,跳轉到首頁...</span>
                    <script type="text/javascript">
                        window.location.href =  '/';
                    </script>
                {% endif %}
            </div>

3.導航欄添加用戶相關功能

(1)導入Bootstrap語法 

參考Bootstrap中文文檔的導航條

在<base.html>頁面的導航條右側添加登入、註冊

使用者登入後則出現下拉選單(個人資料、登出)

以下為<base.html>添加的代碼:   

<ul class="nav navbar-nav navbar-right">
    {% if not user.is_authenticated %}
        <li><a href="{% url 'login' %}?from={{ request.get_full_path }}">登入</a></li>
        <li><a href="{% url 'register' %}?from={{ request.get_full_path }}">註冊</a></li>
    {% else %}
        <li class="dropdown">
            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ user.username }}<span class="caret"></span></a>
            <ul class="dropdown-menu">
                <li><a href="{% url 'user_info' %}">個人資料</a></li>
                <li><a href="{% url 'logout' %}?from={{ request.get_full_path }}">登出</a></li>
            </ul>
        </li>
    {% endif %}
</ul>

4.模態框登入全局化

(1)添加TEMPLATES全局變量

在user應用中創建<context_processors.py>,並撰寫以下代碼:   

from .forms import LoginForm

def login_modal_form(request):
    return {'login_modal_form' : LoginForm()}

在<settings.py>的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',
                'user.context_processors.login_modal_form',  #模態框登入
            ],
        },
    },
]

將blog應用<views.py>使用到LoginForm的部分移除

把<blog_detail.html>中引用'login_form'的地方改為'login_modal_form'

(2)模態框登入遷移至<base.html>

將<blog_detail.html>模態框登入的代碼遷移至<base.html>

{% content block %}{% endblock %}的下方

並且將對應的js代碼也遷移至<base.html>

以下為最後<base.html>的完整代碼:   

{% load staticfiles %} <!-- 導入 staticfiles -->
<!DOCTYPE html>
<html lang="zh-TW">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">  <!-- 對應IE瀏覽器 -->
    <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 適應響應式設計 -->
    <!-- 將標題區塊化 -->
    <title>{% block title %}{% endblock %}</title>
    <link rel="stylesheet" href="{% static 'css/base.css' %}"> <!-- 導入 css 文件 -->
    <link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.min.css' %}">
    <link rel="shortcut icon" href="/static/favicon.ico">

    {% block header_extends %}{% endblock %}  <!-- 用於擴展額外 css -->

</head>
<body>
    <!-- bootstrap 響應式導航條 -->
    <div class="navbar navbar-default navbar-fixed-top" role="navigation">
        <!-- 選用寬度填滿頁面的容器 -->
        <div class="container-fluid">
            <!-- 導航條主標題 -->
            <div class="navbar-header">
                <a class="navbar-brand" href="{% url 'home' %}">個人部落格</a>
                <!-- 響應式下拉選單 -->
                <button class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse">
                    <!-- 按鈕圖案上的三條線 -->
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
            </div>
            <!-- 導航條列表-->
            <div class="collapse navbar-collapse" id="navbar-collapse">
                <ul class="nav navbar-nav">
                    <!-- 選取的列表加入active狀態-->
                    <li class="{% block nav_home_active %}{% endblock %}">
                        <a href="{% url 'home' %}">首頁</a>
                    </li>
                    <li class="{% block nav_blog_active %}{% endblock %}">
                        <a href="{% url 'blog_list' %}">部落格</a>
                    </li>
                </ul>
                <ul class="nav navbar-nav navbar-right">
                    {% if not user.is_authenticated %}
                        <li><a href="{% url 'login' %}?from={{ request.get_full_path }}">登入</a></li>
                        <li><a href="{% url 'register' %}?from={{ request.get_full_path }}">註冊</a></li>
                    {% else %}
                        <li class="dropdown">
                            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ user.username }}<span class="caret"></span></a>
                            <ul class="dropdown-menu">
                                <li><a href="{% url 'user_info' %}">個人資料</a></li>
                                <li><a href="{% url 'logout' %}?from={{ request.get_full_path }}">登出</a></li>
                            </ul>
                        </li>
                    {% endif %}
                </ul>
            </div>
        </div>
    </div>

    {% block content %}{% endblock %}
    <div class="modal fade" id="login_modal" tabindex="-1" role="dialog">
        <div class="modal-dialog modal-sm" role="document">
            <div class="modal-content">
                <form id="login_modal_form" action="" method="POST">
                    <div class="modal-header">
                        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                        <h4 class="modal-title">登錄</h4>
                    </div>
                    <div class="modal-body">
                            {% csrf_token %}
                            <!-- 進行表單細部調整 -->
                            {% for field in login_modal_form %}
                                <label for="{{ field.id_for_label }}">{{field.label}}</label>
                                {{ field }}
                            {% endfor %}
                            <!-- 錯誤訊息透過id由js傳入 -->
                            <span class="text-danger" id="login_modal_tip"></span>
                    </div>
                    <div class="modal-footer">
                        <button type="submit" class="btn btn-primary">登錄</button>
                        <button type="button" class="btn btn-default" data-dismiss="modal">關閉</button>
                    </div>
                </form>
            </div><!-- /.modal-content -->
        </div><!-- /.modal-dialog -->
    </div><!-- /.modal -->
    <!-- 導入jquery和bootstrap(jquery必須在前) -->
    <script type="text/javascript" src="{% static 'jquery-3.3.1.min.js' %}"></script>
    <script type="text/javascript" src="{% static 'bootstrap/js/bootstrap.min.js' %}"></script>
    <script type="text/javascript">
        $('#login_modal_form').submit(function(event){ //送出後阻止頁面刷新
            event.preventDefault(); //送出後阻止頁面刷新
            $.ajax({
                url: "{% url 'login_for_modal' %}",
                type: 'POST',
                data: $(this).serialize(), //序列化表單值
                cache: false,
                success: function(data){
                    console.log(data);
                    if(data['status']=='SUCCESS'){
                        window.location.reload(); //當前窗口重新加載
                    }else{
                        $('#login_modal_tip').text('用戶名稱或密碼錯誤');
                    }
                }
            });
        });
    </script>
    {% block script_extends %}{% endblock %}
</body>
</html>
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 ivankao 的頭像
    ivankao

    IvanKao的部落格

    ivankao 發表在 痞客邦 留言(0) 人氣()