Django學習5-用戶賬戶:註冊

創建用戶賬戶

註冊

Django沒有註冊用戶的視圖函數和URL模式,自定義URL模式,編寫視圖函數和模板。

註冊URL模式

在users/urls.py中導入註冊函數register

from django.urls import path, include
from django.contrib.auth import views as auth_views
from .views import register
app_name = 'users'
urlpatterns = [
    # path('', include('django.contrib.auth.urls')),
    path('login/', auth_views.LoginView.as_view(template_name='login.html'),  name='login'),
    path('logout/',auth_views.LogoutView.as_view(template_name='index.html'), name='logout'),
    path('register/', register, name='register'),
]

註冊視圖函數register

Django提供了註冊用的表單django.contrib.auth.forms.UserCreationForm
包含2個密碼輸入字段,內嵌元類指定Django的用戶模型User

class UserCreationForm(forms.ModelForm):
    """ A form that creates a user, with no privileges, from the given username and password. """
    error_messages = {
        'password_mismatch': _("The two password fields didn't match."),
    }
    password1 = forms.CharField(
        label=_("Password"),
        strip=False,
        widget=forms.PasswordInput,
        help_text=password_validation.password_validators_help_text_html(),
    )
    password2 = forms.CharField(
        label=_("Password confirmation"),
        widget=forms.PasswordInput,
        strip=False,
        help_text=_("Enter the same password as before, for verification."),
    )

    class Meta:
        model = User
        fields = ("username",)
        field_classes = {'username': UsernameField}

註冊函數的調用形式與之前的表單類似:POST請求時,根據表單提交的數據創建用戶對象,並直接登錄創建的用戶,接着重定向到learning_logs的首頁;GET請求時,生成空表單,不會有任何數據。

def register(request):
    """註冊用戶"""
    if request.method != 'POST':
        # 顯示空的註冊表單
        form = UserCreationForm()
    else:
        # 提交填好的註冊表
        form = UserCreationForm(data=request.POST)
        if form.is_valid():
            new_user = form.save()
            # 註冊後的用戶 直接登錄, 重定向到首頁
            authenticated_user = authenticate(username=new_user.username,
                                              password=request.POST.get('password1', ""))
            login(request, authenticated_user)
            return HttpResponseRedirect(reverse('learning_logs:index'))
    context = {'form': form}
    return render(request, 'register.html', context)

這裏使用用戶驗證函數authenticate驗證用戶的身份,一般情況下接受用戶名username和密碼password。驗證通過後,返回一個user對象,使用login登錄這個用戶。

註冊模板

{% extends 'base.html' %}
{% block title %}Register{% endblock %}
{% block content %}
  <form action="{% url 'users:register' %}" method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="Register"/>
    <input type="hidden" name="next" value="{% url 'learning_logs:index' %}"/>
  </form>

{% endblock %}

註冊成功後,返回到首頁。
在這裏插入圖片描述Django默認的註冊表單類生成表單,輸入註冊的用戶名後要輸入2次同喲的密碼。
在登錄界面添加註冊的鏈接

{% block content %}
  ...
  <form action="{% url 'users:login' %} " method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="Login"/>
    <input type="hidden" name='next' value="{% url 'learning_logs:index' %}"/>
  </form>
  <p><a href="{% url 'users:register' %}">register</a></p>
{% endblock %}

在這裏插入圖片描述

註冊時添加郵箱字段

Django默認的註冊表單類沒有郵箱(email)字段,自定義一個表單類繼承它,並添加email字段。
在users/forms.py創建註冊表單類RegisterForm

from django.forms import Form, CharField, PasswordInput, EmailField
from django.contrib.auth.forms import UserCreationForm, UsernameField
from django.contrib.auth.models import User

class RegisterForm(UserCreationForm):
    email = EmailField()

    class Meta:
        model = User
        fields = ("username", "email")
        field_classes = {'username': UsernameField}

添加了EmailField字段,其餘繼承了UserCreationForm。並在視圖函數中使用新的註冊表單類。結果如下
在這裏插入圖片描述嘗試登錄到admin site查看創建的用戶:
在這裏插入圖片描述新創建的用戶都沒有賦予其staff屬性,這些賬戶都不能訪問管理界面。
使用超級用戶進行訪問,查看當前的User。
在這裏插入圖片描述

用戶與數據

修改Topic和Post模型的屬性,讓每個post和topic都有其創建者。對頁面也進行限制,普通用戶只能訪問自己創建的數據。

限制訪問

使用@login_required裝飾器能限制只有驗證過的用戶才能訪問被其裝飾的視圖函數。當用戶沒有登錄,訪問被其裝飾的視圖函數時,會重定向到settings.LOGIN_URL指定的URL。使用方法:

from django.contrib.auth.decorators import login_required

@login_required
def my_view(request):
    ...

使用@login_required對項目內的視圖函數進行裝飾,並設置LOGIN_URL爲用戶登錄的URL。在用戶沒有進行登錄驗證時,訪問topic時會提示先進行登錄。
在這裏插入圖片描述登錄之後根據LOGIN_URL的設置返回到首頁。

數據與用戶關聯

需要將數據關聯到提交它們的用戶。只需要將高層數據關聯到用戶,這樣低層的數據將自動關聯到用戶。只要所有的topic都有其特定的用戶,那麼都能從數據庫中找到每條post的擁有者。
在 learning_logs/models.py 中爲Topic模型添加所有者字段。接着進行數據遷移, Django提示模型被修改了,而owner這個字段是必不可少的,且沒有默認值。

(venv) [email protected]:~/PycharmProjects/django_ulysses$ python3 manage.py makemigrations 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:

按照提示選擇提供一個默認的user用戶

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
>>> 3
Migrations for 'learning_logs':
  learning_logs/migrations/0004_auto_20181025_0850.py
    - Add field owner to topic
    - Alter field id on post
    - Alter field id on topic

輸入用戶的id(user.id)Django會使用這個用戶來遷移數據庫,生成遷移文件,之後執行數據遷移,可以查詢每個topic對於的用戶了。

>>> from learning_logs.models import Topic
>>> t = Topic.objects.get(id=10)
>>> print(t, t.owner)
Answer suffer leader public bad. Perhaps general resource perform perform enjoy system. Treatment soon green must least Democrat too.
When manage office state we best capital charge. Hanabi

用戶訪問自己的數據

將topic設置爲當前用戶爲自己的創建者時,才能被訪問。

@login_required
def topics(request):
    """全部的topics"""
    topics = Topic.objects.filter(owner=request.user).order_by('-date_added')

從數據庫中篩選出所有者爲當前登錄用戶的topics(目前所有topics的所有者爲同一個),用其他用戶登錄時,沒有任何主題。
在這裏插入圖片描述類似的方法對topic,new_post, edit_post 進行保護。