Python學習之路18-使用者賬戶

VPointer發表於2018-05-29

《Python程式設計:從入門到實踐》筆記。

本篇記錄如何建立使用者註冊系統,如何實現使用者輸入自己的資料。

1. 前言

在本篇中,我們將:

  • 建立一些表單,讓使用者能夠新增主題和條目,以及編輯既有的條目;
  • 實現一個身份驗證系統。

2. 讓使用者能夠輸入資料

先新增幾個頁面,讓使用者能夠新增新主題,新條目以及編輯條目。

2.1 新增新主題

和之前建立網頁的步驟一樣:定義URL,編寫檢視函式,編寫模板。主要區別是,這裡需要一個包含表單的模組forms.py

2.1.1 建立forms.py模組

使用者輸入資訊時,需要進行驗證,確保提交的資訊是正確的資料型別,且不是惡意資訊,如中斷伺服器的程式碼。然後再處理資訊,並儲存到資料庫中。當然,這些工作很多都由Django自動完成。

models.py所在的目錄中新建forms.py模組。建立表單的最簡單方法是繼承Django的ModelForm類:

from django import forms
from .models import Topic

class TopicForm(forms.ModelForm):
    class Meta:
        model = Topic
        fields = ["text"]
        labels = {"text": ""}
複製程式碼

最簡單的ModelForm版本只包含一個內嵌的Meta類,它告訴Django根據哪個模型建立表單,以及在表單中包含哪些欄位。第6行,我們根據Topic建立一個表單,該表單只包含欄位text(第7行),並不為該欄位生成標籤(第8行)。

2.1.2 URL模式new_topic

當使用者要新增新主題時,將切換到http://localhost:8000/new_topic/ 。在learning_logs/urls.py中新增如下程式碼:

urlpatterns = [
    -- snip --
    # 用於新增新主題的網站
    path("new_topic/", views.new_topic, name="new_topic"),
]
複製程式碼

2.1.3 檢視函式new_topic()

該函式需要處理兩種情形:①剛進入new_topic網頁,顯示一個空表單;②對提交的表單資料進行處理,並將使用者重定向到網頁topics。修改views.py檔案:

from django.http import HttpResponseRedirect
from django.urls import reverse
from .forms import TopicForm

def new_topic(request):
    """新增新主題"""
    if request.method != "POST":
        # 為提價資料:建立一個新表單
        form = TopicForm()
    else:
        # POST提交的資料,對資料進行處理
        form = TopicForm(request.POST)
        if form.is_valid():
            form.save()
            # 該類將使用者重定向到網頁topics,函式reverse()根據指定的URL模型確定URL
            return HttpResponseRedirect(reverse("learning_logs:topics"))

    context = {"form": form}
    return render(request, "learning_logs/new_topic.html", context)
複製程式碼

2.1.4 GET請求和POST請求

建立Web應用程式時,將用到兩種主要資料請求型別:GET請求和POST請求。從這倆英文單詞可以看出,如果只從伺服器讀取資料頁面,則使用GET請求;如果要提交使用者填寫的表單,通常使用POST請求。當然還有一些其他的請求型別,但這個專案中沒有使用。本專案中處理表單都使用POST方法。

request.method儲存了請求的型別(第7行程式碼)。

當不是POST請求時,我們生成一個空表單傳遞給模板new_topic.html,然後返回給使用者;當請求是POST時,我們從request.POST這個變數中獲取使用者提交的資料,並暫存到form變數中。

通過is_valid()方法驗證表單資料是否滿足要求:使用者是否填寫了所有必不可少的欄位(表單欄位預設都是必填的),且輸入的資料與欄位型別是否一致。當然這些驗證都是Django自動進行的。如果表單有效,在通過formsave()方法儲存到資料庫,然後通過reverse()函式獲取頁面topics的URL,並將其傳遞給HTTPResponseRedirect()以重定向到topics頁面。如果表單無效,把這些資料重新傳回給使用者。

2.1.5 模板new_topic.html

{% extends "learning_logs/base.html" %}

{% block content %}
  <p>Add a new topic:</p>

  <form action="{% url 'learning_logs:new_topic' %}" method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button name="submit">add topic</button>
  </form>
  
{% endblock content %}
複製程式碼

模板繼承了base.html,因此其基本結構和專案中的其他頁面相同。第6行中,引數action告訴伺服器將提交的表單資料送到什麼位置去處理,引數method瀏覽器POST請求的方式提交資料。

Django使用模板標籤csrf_token(第7行)來防止攻擊者利用表單獲得對伺服器未經授權的訪問(跨站請求偽造)。

Django顯示錶單非常方便:只需要使用模板變數form.as_p,修飾符as_p讓Django以段落格式渲染所有表單元素,這是一種整潔地顯示錶單的簡單方法。

Django不自動建立提交表單的按鈕,需自行建立。

2.1.6 連結到頁面new_topic

在頁面topics.html中新增一個到頁面new_topic的連結:

{% extends "learning_logs/base.html" %}

{% block content %}
  -- snip --
  <a href="{% url 'learning_logs:new_topic' %}">Add a new topic:</a>
{% endblock content %}
複製程式碼

2.1.7 效果

以下是實際效果圖:

Python學習之路18-使用者賬戶

通過這個頁面,隨意新增幾個主題,如下:

Python學習之路18-使用者賬戶

2.2 新增新條目

和前面的步驟相似:建立條目表單,新增URL,新增檢視,新增模板,連結到頁面

2.2.1 建立條目表單

建立一個與模型Entry相關聯的表單,但這個表單的自定義程度比TopicForm要高些,依然是在剛才建立的forms.py中新增:

from .models import Topic, Entry

class EntryForm(forms.ModelForm):
    class Meta:
        model = Entry
        fields = ["text"]
        labels = {"text": ""}
        widgets = {"text": forms.Textarea(attrs={"cols": 80})}
複製程式碼

程式碼中定義了屬性widgets。小部件(widget)是一個HTML表單元素,如單行文字框、多行文字框或下拉選單。通過設定屬性widgets可以覆蓋Django選擇的預設小部件。通過Django的forms.Textarea定製欄位“text"的輸入小部件,將文字框的寬度設定為80列,而不是預設的40列。

2.2.2 新增URL模式new_entry

修改learning_logs/urls.py

urlpatterns = [
    -- snip --
    path("new_entry/<int:topic_id>/", views.new_entry, name="new_entry"),
]
複製程式碼

該URL模式與形式為http://localhost:8000/new_entry/topi_id/ 的URL匹配,其中topic_id是主題的ID。

2.2.3 檢視函式new_entry()

與函式new_topic()很像:

from .forms import TopicForm, EntryForm

def new_entry(request, topic_id):
    """在特定的主題中新增新條目"""
    topic = Topic.objects.get(id=topic_id)

    if request.method != "POST":
        # 未提交資料,建立一個空表單
        form = EntryForm()
    else:
        # POST提交的資料,對資料進行處理
        form = EntryForm(data=request.POST)
        if form.is_valid():
            new_entry = form.save(commit=False)
            new_entry.topic = topic
            new_entry.save()
            return HttpResponseRedirect(reverse("learning_logs:topic", args=[topic_id]))

    context = {"topic": topic, "form": form}
    return render(request, "learning_logs/new_entry.html", context)
複製程式碼

new_entry()的定義包含形參topic_id,用於儲存從URL中獲得的值。

在呼叫save()時傳遞了引數commit=False(第14行),它讓Django建立一個新的條目物件,但並不立刻提交資料庫,而是暫時儲存在變數new_entry中,待為這個新條目物件新增了屬性topic之後再提交資料庫。

在重定向時,reverse()函式中傳遞了兩個引數,URL模式的名稱以及列表argsargs包含要包含在URL中的所有引數。

2.2.4 模板new_entry.html

類似於new_topic

{% extends "learning_logs/base.html" %}

{% block content %}
  <p><a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}</a></p>

  <p>Add a new entry:</p>
  <form action="{% url 'learning_logs:new_entry' topic.id %}" method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <button name="submit">add entry</button>

  </form>
{% endblock content %}
複製程式碼

注意第4行程式碼,改行程式碼返回到特定主題頁面。

2.2.5 連結到頁面new_entry

在顯示特定主題的頁面中新增到頁面new_entry的連結,修改topic.html

{% extends "learning_logs/base.html" %}

{% block content %}

  <p>Topic: {{ topic }}</p>

  <p>Entries:</p>
  <p>
    <a href="{% url 'learning_logs:new_entry' topic.id %}">add new entry</a>
  </p>
  -- snip --

{% endblock content %}
複製程式碼

2.2.6 效果

下圖是實際效果,請隨意新增一些條目:

Python學習之路18-使用者賬戶

2.3 編輯條目

建立一個頁面,讓使用者能編輯既有條目。順序是:新增URL,新增檢視,新增模板,連結到頁面。

2.3.1 URL模式edit_entry

修改learning_logs/urls.py

urlpatterns = [
    -- snip --
    path("edit_entry/<int:entry_id>/", views.edit_entry, name="edit_entry"),
]
複製程式碼

2.3.2 檢視函式edit_entry()

from .models import Topic, Entry

def edit_entry(request, entry_id):
    """編輯既有條目"""
    entry = Entry.objects.get(id=entry_id)
    topic = entry.topic

    if request.method != "POST":
        # 初次請求,使用當前條目填充表單
        form = EntryForm(instance=entry)
    else:
        # POST提交的資料,對資料進行處理
        form = EntryForm(instance=entry, data=request.POST)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect(reverse('learning_logs:topic', args=[topic.id]))

    context = {"entry": entry, "topic": topic, "form": form}
    return render(request, "learning_logs/edit_entry.html", context)
複製程式碼

首先獲取要被修改的entry以及與該條目相關的主題。處理GET請求時,通過引數instance=entry建立EntryForm例項,該引數讓Django建立一個表單,並使用既有條目物件中的資訊填充它。處理POST請求時,還傳入了data=request.POST引數,Django根據POST中的相關資料對entry進行修改。

2.3.3 模板edit_entry.html

{% extends "learning_logs/base.html" %}

{% block content %}
  <p><a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}</a></p>

  <p>Edit entry:</p>

  <form action="{% url 'learning_logs:edit_entry' entry.id %}">
    {% csrf_token %}
    {{ form.as_p }}
    <button name="submit">save changes</button>
  </form>

{% endblock content %}
複製程式碼

2.3.4 連結到頁面edit_entry.html

在顯示特定主題的頁面中,需要給每個條目新增到頁面edit_entry.html的連結,為此,修改topic.html

-- snip --
    {% for entry in entries %}
      <li>
        <p>{{ entry.date_added|date:'M d, Y H:i' }}</p>
        <p>{{ entry.text|linebreaks }}</p>
        <p>
          <a href="{% url 'learning_logs:edit_entry' entry.id %}">edit entry</a>
        </p>
      </li>
-- snip --
複製程式碼

2.3.5 效果

以下是實際效果圖:

Python學習之路18-使用者賬戶

3. 建立使用者賬戶

現在開始建立一個使用者註冊和身份驗證系統。為此將建立一個新的應用程式,其中包含處理使用者賬戶相關的所有功能。對Topic模型也要做稍許修改,讓每個主題都歸屬於特定使用者。

3.1 建立應用程式users

希望大家還記得如何使用startapp命令還建立APP:

python manage.py startapp users
複製程式碼

將APP新增到settings.py中

INSTALLED_APPS = [
    -- snip --
    "users.apps.UsersConfig",
]
複製程式碼

在APP根目錄下建立urls.py檔案,並新增名稱空間:

app_name = "users"
複製程式碼

為APP定義URL,修改專案根目錄中的urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    path("", include("learning_logs.urls")),
    path("users/", include("users.urls")),
]
複製程式碼

3.2 登陸頁面

使用Django提供的預設登陸檢視,URL模式會有所不同。在users中的urls.py中新增如下程式碼:

"""為應用程式users定義URL模式"""
from django.contrib.auth.views import login
from django.urls import path

app_name = "users"

urlpatterns = [
    # 登陸頁面
    path("login/", login, {"template_name": "users/login.html"}, name="login"),
]
複製程式碼

程式碼中,我們使用Django自帶的login檢視函式(注意,引數是login,而不是views.login)。從之前的例子可以看出,我們渲染模板的程式碼都是在自己寫的檢視函式中。但這裡使用了自帶的檢視函式,無法自行編寫進行渲染的程式碼。所以,我們還傳了一個字典給path,告訴Django到哪裡查詢我們要用到的模板。注意,該模板在users中,而不是在learning_logs中。

3.2.1 新建模板login.html

learning_log/users/templates/users中建立login.html

{% extends "learning_logs/base.html" %}

{% block content %}

  {% if form.errors %}
    <P>Your username and password didn't match. Please try again.</P>
  {% endif %}

  <form method="post" action="{% url 'users:login' %}">
    {% csrf_token %}
    {{ form.as_p }}

    <button name="submit">log in</button>
    <input type="hidden" name="next" value="{% url 'learning_logs:index' %}"/>
  </form>
  
{% endblock content %}
複製程式碼

如果表單的errors屬性被設定,則顯示一條提示賬號密碼錯誤的資訊。

3.2.2 連結到登陸頁面

base.html中新增到登陸頁面的連結,讓所有頁面都包含它。將這個連結巢狀在一個if標籤中,使用者已登入時隱藏掉該連結:

<p>
  <a href="{% url 'learning_logs:index' %}">Learning Log</a> -
  <a href="{% url 'learning_logs:topics' %}">Topics</a>
  {% if user.is_authenticated %}
    Hello, {{ user.username }}
  {% else %}
    <a href="{% url 'users:login' %}">log in</a>
  {% endif %}
</p>

{% block content %}{% endblock content %}
複製程式碼

在Django身份驗證系統中,每個模板都可使用變數user,這個變數有一個is_authenticated屬性:如果使用者已登入,該屬性將為True,否則為False

3.2.3 使用登陸頁面

首先訪問localhost:8000/admin登出超級使用者,再訪問localhost:8000/users/login/,得到如下頁面:

Python學習之路18-使用者賬戶

3.3 登出

並不為登出建立單獨的頁面,而是讓使用者單擊一個連線用於登出並返回主頁。因此,需要做如下工作:登出URL模式,新建檢視,連結到登出檢視

users/urls.py中新增與http://localhost:8000/users/logout/ 匹配的URL模式:

-- snip --
urlpatterns = [
    -- snip --
    path("logout/", views.logout_view, name="logout"),
]
複製程式碼

編寫檢視函式logout_view(),其中,直接呼叫Django自帶的logout()函式,該函式要求request作為引數:

from django.contrib.auth import logout
from django.http import HttpResponseRedirect
from django.urls import reverse

def logout_view(request):
    """登出使用者"""
    logout(request)
    return HttpResponseRedirect(reverse("learning_logs:index"))
複製程式碼

base.html中新增登出連結:

-- snip --
  {% if user.is_authenticated %}
    Hello, {{ user.username }}
    <a href="{% url 'users:logout' %}">log out</a>
  {% else %}
    <a href="{% url 'users:login' %}">log in</a>
  {% endif %}
-- snip --
複製程式碼

3.4 註冊頁面

使用Django提供的表單UserCreationFrom,但編寫自己的檢視函式和模板。URL->view->template->link

首先,建立註冊頁面的URL模式,修改users/urls.py

-- snip --
urlpatterns = [
    -- snip --
    path("register/", views.register, name="register"),
]
複製程式碼

其次,建立檢視register()

from django.contrib.auth import logout, authenticate, login
from django.contrib.auth.forms import UserCreationForm
from django.shortcuts import render
-- snip --

def register(request):
    """註冊新使用者"""
    if request.method != "POST":
        # 顯示空的登錄檔單
        form = UserCreationForm()
    else:
        # 處理填寫好的表單
        form = UserCreationForm(data=request.POST)

        if form.is_valid():
            new_user = form.save()
            # 讓使用者自動登陸,再重定向到主頁
            # 註冊是要求輸入兩次密碼,所以有password1和password2
            authenticated_user = authenticate(username=new_user.username,
                                              password=request.POST["password1"])
            login(request, authenticated_user)
            return HttpResponseRedirect(reverse("learning_logs:index"))

    context = {"form": form}
    return render(request, "users/register.html", context)
複製程式碼

以上程式碼在使用者成功建立了使用者後會自動登陸,該功能由login()函式實現。該函式將會為通過了身份驗證的使用者物件建立會話(session)。最後上述程式碼重定向到主頁。

然後,編寫註冊頁面的模板register.html

{% extends "learning_logs/base.html" %}

{% block content %}
  <form method="post" action="{% url 'users:register' %}">
  {% csrf_token %}
  {{ form.as_p }}

  <button name="sumbit">register</button>
  <input type="hidden" name="next" value="{% url 'learning_logs:index' %}"/>

  </form>
{% endblock content %}
複製程式碼

最後,在頁面中顯示註冊連結,修改base.html,在使用者沒有登入時顯示註冊連結:

-- snip --
  {% if user.is_authenticated %}
    Hello, {{ user.username }}
    <a href="{% url 'users:logout' %}">log out</a>
  {% else %}
    <a href="{% url 'users:register' %}">register</a> -
    <a href="{% url 'users:login' %}">log in</a>
  {% endif %}
-- snip --
複製程式碼

下面是實際效果:

Python學習之路18-使用者賬戶

這是直接點register按鈕時的反饋,不過這裡有點疑惑,從上面的register.html中看到,其實程式碼很簡單,但這裡有個浮動效果,而且在註冊模板中並沒有像前面那樣的form.errors模板標籤,但依然有未註冊成功時的反應,而且註冊的檢視函式也是自己寫的,並不是用的自帶的註冊函式,所以不知道是不是和form.as_p有關。之後再慢慢研究吧,

4. 讓使用者擁有自己的資料

使用者應該能夠輸入其專有的資料,所以應該建立一個系統,確定各項資料所屬的使用者,再限制對頁面的訪問,使得使用者只能使用自己的資料,即訪問控制。

4.1 使用@login_required限制訪問

Django提供了裝飾器@login_required,使得能輕鬆實現使用者只能訪問自己能訪問的頁面。

限制對topics.html的訪問

每個主題都歸特定使用者所有,所以需要加限制,修改learning_logs/views.py

from django.contrib.auth.decorators import login_required

@login_required
def topics(request):
    """顯示所有的主題"""
    topics = Topic.objects.order_by("date_added")
    # 一個上下文字典,傳遞給模板
    context = {"topics": topics}
    return render(request, "learning_logs/topics.html", context)
複製程式碼

裝飾器也是一個函式,python在執行topics()前會先執行login_required()的程式碼。

login_required()函式檢查使用者是否登入,僅當使用者已登入時,Django才執行topics()函式,若未登入,就重定向到登陸介面。而為了實現這個重定向,還需要修改專案settings.py檔案,在該檔案中新增這樣一個常量(其實也是變數),一般在檔案末尾新增:

-- snip --
LOGIN_URL = '/users/login/'
複製程式碼

全面限制對專案“學習筆記”的訪問

Django能輕鬆地限制對頁面的訪問,但得自己設計需要限制哪些頁面。一般先確定哪些頁面不需要保護,再限制對其他頁面的訪問。在該專案中,我們不限制對主頁、註冊頁面和登出連結的訪問,其他頁面均限制。在learning_logs/views.py中,除了index()外,每個檢視函式都加上@login_required

4.2 將資料關聯到使用者

為了禁止使用者訪問其他使用者的資料,需要將使用者與資料關聯。只需要將最高層的資料關聯到使用者,這樣更低層的資料將自動關聯到使用者。下面修改Topic模型和相關檢視:

from django.contrib.auth.models import User
from django.db import models

class Topic(models.Model):
    """使用者學習的主題"""
    text = models.CharField(max_length=200)
    date_added = models.DateTimeField(auto_now_add=True)
    owner = models.ForeignKey(User, on_delete=models.CASCADE)
    -- snip --
複製程式碼

修改模型後,還需要遷移資料庫。此時,需要將主題與使用者關聯。這裡並沒有通過程式碼進行關聯,我們在遷移資料庫時手動進行關聯。為此,我們需要先知道有哪些使用者,以及這些使用者的ID。我們通過Django shell查詢使用者資訊,當然也可以直接檢視資料庫,這裡不再演示。我們將主題都關聯到超級使用者ll_admin上,它的ID是1。現在我們執行資料遷移:

(venv)learning_logs$ python manage.py makeimgrations learning_logs
You are trying to add a non-nullable field 'owner' to topic without a default; 
we can't do that (the database needs something to populate existing rows).
Please select a fix:
 1) Provide a one-off default now (will be set on all existing rows with a null value for 
 this column)
 2) Quit, and let me add a default in models.py
Select an option:  1
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
>>>  1
Migrations for 'learning_logs':
  learning_logs\migrations\0003_topic_owner.py
    - Add field owner to topic
複製程式碼

Django指出試圖給既有模型Topic新增一個必不可少(不可為空)的欄位,而該欄位沒有預設值,需要我們採取措施:要麼現在提供預設值,要麼退出並在models.py中新增預設值。我們選擇了直接輸入預設值。接下來,Django使用這個值來遷移資料庫,並生成了遷移檔案0003_topic_owner.py,它在模型Topic中新增欄位owner

現在執行遷移命令:

(venv)learning_logs$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, learning_logs, sessions
Running migrations:
  Applying learning_logs.0003_topic_owner... OK
複製程式碼

執行後可以在Django shell中驗證是否遷移成功,這裡不再驗證。

4.3 只允許使用者訪問自己的主題

目前不管以哪個使用者身份登入,都能看到所有主題。現在我們新增限制,讓使用者只能看到自己的主題。在views.py中,對topics()做如下修改:

-- snip --
@login_required
def topics(request):
    """顯示所有的主題"""
    topics = Topic.objects.filter(owner=request.user).order_by("date_added")
    # 一個上下文字典,傳遞給模板
    context = {"topics": topics}
    return render(request, "learning_logs/topics.html", context)
-- snip --
複製程式碼

使用者登入後,request物件將有一個user屬性,這個屬性儲存了有關該使用者的資訊。第5行程式碼讓Django只從資料庫中讀取特定使用者的資料。

4.4 保護使用者的主題

上述程式碼做到了登入後只顯示相應使用者的資料,但是,如果登入後直接通過URL訪問,如直接輸入http://localhost:8000/topics/1/ ,依然可以訪問不屬於自己的特定主題頁面。下面修改views.py中的topic()函式來加以限制:

from django.http import HttpResponseRedirect, Http404

@login_required
def topic(request, topic_id):
    """顯示單個主題及其所有的條目"""
    topic = Topic.objects.get(id=topic_id)
    # 確認請求的主題屬於當前使用者
    if topic.owner != request.user:
        raise Http404
    -- snip --
複製程式碼

4.5 保護頁面edit_entry

此時使用者也可以像上面一樣,登陸後直接通過URL來訪問edit_entry.html,現在我們對這個頁面也加以限制:

@login_required
def edit_entry(request, entry_id):
    """編輯既有條目"""
    entry = Entry.objects.get(id=entry_id)
    topic = entry.topic
    if topic.owner != request.user:
        raise Http404
    
    -- snip --
複製程式碼

4.6 最後一步:將新主題關聯到當前使用者

當前用於新增新主題的頁面存在問題,因為它沒有將新主題關聯到特定使用者。如果此時嘗試新增新主題,將看到錯誤資訊IntegrityError,指出learning_logs_topic.user_id不能為NULL,下面修改new_topic()函式:

@login_required
def new_topic(request):
    """新增新主題"""
    if request.method != "POST":
        # 為提價資料:建立一個新表單
        form = TopicForm()
    else:
        # POST提交的資料,對資料進行處理
        form = TopicForm(request.POST)
        if form.is_valid():
            # 新增新主題時關聯到特定使用者
            new_topic = form.save(commit=False)
            new_topic.owner = request.user
            new_topic.save()
            # 該類將使用者重定向到網頁topics,函式reverse()根據指定的URL模型確定URL
            return HttpResponseRedirect(reverse("learning_logs:topics"))

    context = {"form": form}
    return render(request, "learning_logs/new_topic.html", context)
複製程式碼

現在,這個專案允許任何使用者註冊,而每個使用者想新增多少新主題都可以,每個使用者只能訪問自己的資料,無論是檢視資料、輸入新資料還是修改舊資料時都是如此。

5. 小結

本篇主要講述瞭如何使用表單來讓使用者新增新主題、新增新條目和編輯既有條目;如何實現註冊,登入與登出,如何使用裝飾器來限制訪問,如何對使用者資料進行保護等。下一篇中,我們將使這個專案更漂亮,並且部署到伺服器上。


迎大家關注我的微信公眾號"程式碼港" & 個人網站 www.vpointer.net ~

Python學習之路18-使用者賬戶

相關文章