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学习5-用户账户:注册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学习5-用户账户:注册

注册时添加邮箱字段

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。并在视图函数中使用新的注册表单类。结果如下
Django学习5-用户账户:注册尝试登录到admin site查看创建的用户:
Django学习5-用户账户:注册新创建的用户都没有赋予其staff属性,这些账户都不能访问管理界面。
使用超级用户进行访问,查看当前的User。
Django学习5-用户账户:注册

用户与数据

修改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时会提示先进行登录。
Django学习5-用户账户:注册登录之后根据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的所有者为同一个),用其他用户登录时,没有任何主题。
Django学习5-用户账户:注册类似的方法对topic,new_post, edit_post 进行保护。