用户注册就是创建用户对象,将用户的个人信息保存到数据库里。回顾一下 Django 的 MVT 经典开发流程,对用户注册功能来说,首先创建用户模型(M),这一步我们已经完成了。编写注册视图函数(V),并将为视图函数绑定对应的 URL。编写注册模板(T),模板中提供一个注册表单给用户。Django 用户系统内置了登录、修改密码、找回密码等视图,但是唯独用户注册的视图函数没有提供,这一部分需要我们自己来写。
编写用户注册表单
Django 已经内置了一个用户注册表单:django.contrib.auth.forms.UserCreationForm,不过这个表单的一个小问题是它关联的是 django 内置的 User 模型,从它的源码中可以看出:
class UserCreationForm(forms.ModelForm):
...
class Meta:
model = User
fields = ("username",)
field_classes = {'username': UsernameField}
问题就出在内部类 Meta
的 model
属性,它的值对应的是 auth.User,因此无法用于我们自定义的 User 模型。好在表单实际上就是一个 Python 类,因此我们可以继承它,对它做一点小小的修改就可以了。
表单的代码通常写在 forms.py 文件里,因此在 users 应用下新建一个 forms.py 文件用于存放表单代码,然后写上如下代码:
users/forms.py
from django.contrib.auth.forms import UserCreationForm
from .models import User
class RegisterForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = User
fields = ("username", "email")
UserCreationForm
的 Meta
内部类下的 model
属性对应的是 auth.User 模型。而 RegisterForm
通过覆写父类 model
属性的值,将其改为 users.User。
此外 fields
用于指定表单的字段,这些指定的字段在模板中会被渲染成表单控件(即一些 <input>
等表单控件)。 UserCreationForm
中只指定了 fields = ("username",)
,即用户名,此外还有两个字段密码和确认密码在 UserCreationForm
的属性中指定。所以默认的表单渲染后只有用户名(username)、密码、确认密码三个表单控件。我们还希望用户注册时提供邮箱地址,所以在 fields
中增加了 email 字段。
注意:虽然 model
属性的值都被指定为 User,但一个是 auth.User,另一个是 users.User。
编写用户注册视图函数
首先来分析一下注册函数的逻辑。用户在注册表单里填写注册信息,然后通过表单将这些信息提交给服务器。视图函数从用户提交的数据提取用户的注册信息,然后验证这些数据的合法性。如果数据合法,就新建一个用户对象,将用户的数据保存到数据库,否则就将错误信息返回给用户,提示用户对提交的信息进行修改。过程就是这么简单,下面是对应的代码(视图函数的代码通常写在 views.py 文件里):
users/views.py
from django.shortcuts import render, redirect
from .forms import RegisterForm
def register(request):
# 只有当请求为 POST 时,才表示用户提交了注册信息
if request.method == 'POST':
# request.POST 是一个类字典数据结构,记录了用户提交的注册信息
# 这里提交的就是用户名(username)、密码(password)、邮箱(email)
# 用这些数据实例化一个用户注册表单
form = RegisterForm(request.POST)
# 验证数据的合法性
if form.is_valid():
# 如果提交数据合法,调用表单的 save 方法将用户数据保存到数据库
form.save()
# 注册成功,跳转回首页
return redirect('/')
else:
# 请求不是 POST,表明用户正在访问注册页面,展示一个空的注册表单给用户
form = RegisterForm()
# 渲染模板
# 如果用户正在访问注册页面,则渲染的是一个空的注册表单
# 如果用户通过表单提交注册信息,但是数据验证不合法,则渲染的是一个带有错误信息的表单
return render(request, 'users/register.html', context={'form': form})
注意以上视图是处理表单的经典流程,即:
def form_process_view(request):
if request.method == 'POST':
# 请求为 POST,利用用户提交的数据构造一个绑定了数据的表单
form = Form(request.POST)
if form.is_valid():
# 表单数据合法
# 进行其它处理...
# 跳转
return redirect('/')
else:
# 请求不是 POST,构造一个空表单
form = Form()
# 渲染模板
# 如果不是 POST 请求,则渲染的是一个空的表单
# 如果用户通过表单提交数据,但是数据验证不合法,则渲染的是一个带有错误信息的表单
return render(request, 'template.html', context={'form': form})
以上逻辑代码稍加修改就可以应用于各种表单处理。
设置 URL 模式
视图函数需要和对应的 URL 绑定,这样当用户访问某个 URL 时,Django 才知道调用哪个视图函数处理用户请求。首先在 users 应用下新建一个 urls.py 文件用于设置注册视图函数的 URL 模式。
users/urls.py
from django.conf.urls import url
from . import views
app_name = 'users'
urlpatterns = [
url(r'^register/', views.register, name='register'),
]
app_name = 'users'
为这个 urls 模块设置命名空间。关于 URL 模式的设置如果不明白的话请参考相关基础教程,这里不再赘述。
接下来需要在工程的 urls.py 文件里包含 users 应用的 URL 模式。打开 django_auth_example/ 目录下的 urls.py 文件,将 users.urls.py 包含进来:
django_auth_example/urls.py
from django.conf.urls import url, include
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
# 别忘记在顶部引入 include 函数
url(r'^users/', include('users.urls')),
]
编写注册页面模板
我们在视图函数中渲染了 users/register.html,不过目前这个模板文件还不存在,我们这就来创建它。我习惯喜欢将模板文件放在项目根目录(manage.py 所在目录)的 templates/ 目录下,然后在 templates/ 目录下再新建各个和应用同名的文件夹,用于存放该应用下的模板文件。当然模板放在哪里是无关紧要的,具体视项目而定,只要通过配置模板路径使 Django 能够找到模板文件即可。
设置模板目录结构
按照我的习惯,先在项目根目录(manage.py 所在目录)新建一个 templates/ 目录,然后在 templates/ 目录下新建一个 users 目录,用于存放 users 应用的相关模板文件。然后在 users/ 目录下新建一个 register.html 模板文件(注意是 templates/ 下的 users/ 目录,不是 users 应用目录)。此时目录结构变为:
django_auth_example/
manage.py
django_auth_example/
__init__.py
settings.py
urls.py
wsgi.py
templates/
users/
register.html
配置模板路径
接着需要在 settings.py 里设置 templates/ 所在路径,在 settings.py 找到 TEMPLATES
选项,它的内容是这样的:
django_auth_example/settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'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',
],
},
},
]
其中 DIRS
就是设置模板的路径,在 [] 中写入 os.path.join(BASE_DIR, 'templates')
,即像下面这样:
django_auth_example/settings.py
TEMPLATES = [
{
...
'DIRS': [os.path.join(BASE_DIR, 'templates')],
...
},
]
这里 BASE_DIR
是 settings.py 在配置开头前面定义的变量,记录的是工程根目录 django_auth_example/ 的值(注意是最外层的 django_auth_example/ 目录)。在这个目录下有模板文件所在的目录 templates/,于是利用os.path.join
把这两个路径连起来,构成完整的模板路径,Django 就知道去这个路径下面找我们的模板了。
渲染注册表单
接下来就是在 register.html 模板中渲染表单了,具体代码如下:
templates/users/register.html
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>注册</title>
<link rel="stylesheet" href="https://unpkg.com/mobi.css/dist/mobi.min.css">
<style>
.errorlist {
color: red;
}
</style>
</head>
<body>
<div class="flex-center">
<div class="container">
<div class="flex-center">
<div class="unit-1-2 unit-1-on-mobile">
<h3>注册</h3>
<form class="form" action="{% url 'users:register' %}" method="post">
{% csrf_token %}
{% for field in form %}
{{ field.label_tag }}
{{ field }}
{{ field.errors }}
{% if field.help_text %}
<p class="help text-small text-muted">{{ field.help_text|safe }}</p>
{% endif %}
{% endfor %}
<button type="submit" class="btn btn-primary btn-block">注册</button>
</form>
<div class="flex-center top-gap text-small">
<a href="login.html">已有账号登录</a>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
为了使注册页面更加美观,我引入了 mobi.css 提供样式支持。其它的代码请忽略,重点只关注表单部分:
<form class="form" action="{% url 'users:register' %}" method="post">
{% csrf_token %}
{% for field in form %}
{{ field.label_tag }}
{{ field }}
{{ field.errors }}
{% if field.help_text %}
<p class="help text-small text-muted">{{ field.help_text|safe }}</p>
{% endif %}
{% endfor %}
<button type="submit" class="btn btn-primary btn-block">注册</button>
</form>
在 Django 中使用表单,必须注意以下几点:
- 设置表单的 action 属性。这个例子中,表单的数据将提交给 URL /users/register/,然后 Django 调用对应的视图函数
register
进行处理。这里我们使用了 {% url %} 模板标签,防止 URL 硬编码。关于 {% url %} 模板标签,可以看这篇文章中的介绍 博客文章详情页。 - 设置表单的 method 属性,通常提交 表单数据都是通过 post 方法提交。
- 在表单中加入 {% csrf_token %} 模板标签。这个模板标签的用途就是用于防止跨站请求伪造攻击,提高网站的安全性。至于什么是跨站请求伪造,感兴趣的可以搜索相关资料查阅。这里只需记住只要使用了表单,一定要在表单中加 {% csrf_token %} 模板标签,否则 Django 将不允许你提交表单数据。
接下来就是表单的控件部分。对表单 form(这是一个模板变量,是 RegisterForm
的一个实例,我们在 register
视图函数中将它传递给模板的。)进行循环就可以得到表单的各个控件:
- {{ field.label_tag }} 是相应控件的 label 标签
- {{ field }} 是相应的表单控件
- {{ field.errors }} 是表单的错误(如果有的话)
- {{ field.help_text|safe }} 是控件相关的帮助信息
例如 RegisterForm
表单有用户名字段,渲染的表单控件为:
<label for="id_username">用户名:</label><!-- 对应 {{ field.label_tag }} -->
<input type="text" name="username" id="id_username" autofocus required maxlength="150" /><!-- 对应 {{ field }} -->
<p class="help text-small text-muted">必填。150个字符或者更少。包含字母,数字和仅有的@/./+/-/_符号。</p><!-- 对应 {{ field.help_text }} -->
你可以按 F12 看看表单的源代码,对比一下表单控件是哪一部分渲染而成的。这种表单渲染方式是一种比较通用的做法,你可以把它当做一个模板,稍作修改就可以应用与其它需要渲染表单的模板中。
OK,运行开发服务器,访问 http://127.0.0.1:8000/users/register/,可以看到渲染的用户注册表单了。
你可以尝试注册一个用户,或者尝试故意输错一些信息,看看表单渲染的错误信息是什么样的,比如我故意输入两次不同的密码,得到一个错误信息提示:
在 Admin 后台查看用户是否注册成功
如果表单数据没有错误,提交表单后就会跳转到首页,由于我们没有写任何处理首页的视图函数,所以得到一个 404 错误。不过没有关系,我么你现在只关心用户是否注册成功。那么怎么查看用户是否已经注册成功呢?可以去 Django Admin 后台看看是否有用户新注册的数据。为了在 Admin 后台查看用户数据,首先需要注册用户模型。打开 users/admin.py 文件,在里面注册 users.User 模型:
users/admin.py
from django.contrib import admin
from .models import User
admin.site.register(User)
为了进入后台,还要创建一个超级管理员用户,使用 python manage.py createsuperuser
创建一个管理员账户即可。如果你不知道怎么创建,请参照 在 Django Admin 后台发布文章 中的说明。
浏览器输入 http://127.0.0.1:8000/admin/,登录管理员账户,可以查看到注册的用户信息了,比如在我的后台可以看到三个用户:
其中有一个是使用 createsuperuser 命令创建的管理员账户,另外两个是注册的新用户。
至此,注册功能已经完成了。用户注册后就要登录,接下来就是如何提供用户登录功能了。
总结
本教程的示例项目代码位于 GitHub:Django Auth Example。
如果遇到问题,请通过下面的方式寻求帮助。
- 在下方评论区留言。
- 将问题的详细描述通过邮件发送到 djangostudyteam@163.com,一般会在 24 小时内回复。
- 在 Pythonzhcn 社区的新手问答版块 发布帖子。
更多 Django 相关教程,请访问我的个人博客:追梦人物的博客。
-- EOF --
博主,点击注册这个button按钮没有反应既不跳转页面也没注册成功,还有那个css链接现在好像不能用了
找到原因了是html的form标签漏了导致不能提交
在后台看不到新建的普通用户啊
为什么最后是404
说没匹配狂这两个...
1.^admin/
2.^users/
The empty path didn't match any of these.
如何把注册页面的如下提示信息给删掉 ?
你的密码不能与其他个人信息太相似
你的密码必须包含至少 8 个字符。
你的密码不能是大家都爱用的常见密码。
你的密码不能全部为数字。
我在html上没看着这些文案呢
这个要重写注册模板,手动渲染表单。
发现个BUG,邮件不是必须的
RegisterForm类中添加 email 的目的应该是想让它成为必须的字段,但是实际如果不写 email 也可以注册成功
您好,这一段是不是写错了
这里 RegisterForm.Meta.fields 是一个duple ,上个教程CommentForm.Meta.fields 是一个list (虽然可以互相转换,)
test
博主你好,我能正常显示注册页面,但是提交后出现了这个问题:
(1146, "Table 'auth.auth_user' doesn't exist")
我基本参照你的教程实现,项目是新开的,配置好
AUTH_USER_MODEL = 'users.User'
后才创建数据库,在报错页面能看到使用的设置是
AUTH_USER_MODEL = 'users.User'
但trackbacks中看到查询数据库的时候使用了auth.auth_user
我尝试在整个项目和库中寻找AUTH_USER_MODEL,在django全局配置
Lib/site-packages/Django-1.7.11-py2.7.egg/django/conf/global_settings.py:515:AUTH_USER_MODEL = 'auth.User'
看到这个,但是这个修改后依然失败。
另外,通过python manage.py createsuperuser 命令可以成功在user_user表创建用户
想请教您怎么对问题进行排错。
我使用的环境:django1.7.11,python 2.7.8,mysql 5.7.21
有没有把你的users应用加入到 installed apps 设置里?
有的,我最后通过跟踪trackbacks发现UserCreationForm类的clean_username函数直接里面直接调用了User,而该User是由from django.contrib.auth.models import User而来,UserCreationForm类的源码在下面的文件
我最后通过在forms中重写clean_username函数,使代码生效,差别在于
差别在于
嗯!可能是 django 修改了代码,我在教程中使用的 django 版本没有这个问题,而且我们已经重写了 UserCreationForm
嗯..可惜内部指定了开发环境,我根据你的教程创建了自定义User,我现在想在注册页加入用户组的选项,在view把新用户加入组,方便以后根据不同的组权限去划分用户能看哪些页面,遇到两个问题想请教:
1、创建group如果不想在命令行创建,应该怎么在代码里面写才不会重复创建,函数放在哪个文件里面比较好
2、自定义的User好像不能直接调用权限,还需要继承PermissionsMixin,但是文档没有详细说明,不知道有没有好的方法或者思路去解决这个问题1
1. group 就是 django 的一个 model,和 User 通过 ManyToManyField 关联,因此你可以在视图中创建新的 group,将 user add 进来就可,即建立两个model 的多对多关系。
2. 如果你继承 AbstracUser,PermissionsMixin 已经继承。否则自己继承。
请教博主,管理界面新增用户,原始的和使用了定制User 后的界面不一样,大家都是这样吗,什么原因,哪个地方控制的?
原生的管理界面是:用户、密码、确认密码。
定制后的管理界面是:密码,没有确认密码,用户名在后面。
你这个是重写了表单么?表单字段的顺序对表单的显示有影响,而且你可能表单中移除了确认密码这个字段。
并没有重写表单,而且我从你的仓库拉下来的代码,未修改,运行就是这样。
Windows 10,Python 3.6.1,Django 1.11.3,PyCharm Community Edition 2017.1.5 。
这个很奇怪,我本地测试是没有这个问题的。
博主大大,我遇到了一个问题,在注册界面注册账号后,弹出报错 no such table:auth_user
我仔细检查了form.py,也没发现问题.
你好,请问你的问题解决了吗,我也遇到了同样的问题。
可以在控制台使用`python manage.py dbshell` `.tables`查看目前数据库中的表,有可能就是`auth_user `表没有,我刚查看的我的数据库里面也没有,但是我以前是有这个表的,我猜原因大概是我之前把数据库整个都删了,重新迁移之后就没有了,暂时还没有找到解决的办法,先占个坑
找到问题l,就是自己的form.py文件中model写成了models,
同时,如果出现:没有 auth-user表的时候,其实不需要这个表,因为我们已经在setting.py中设置了:
AUTH_USER_MODEL = 'users.User'
将auth_user表换成了users-user表,可是使用python manage.py dbshell 查看数据表,同时记得要先创建超级管理员
https://zhidao.baidu.com/question/422724707.html
如何打出` 我找了半天我的键盘,都不知道怎么打``` 你们一个个也不解释下,备注下 省的遇到和我一样的
``` 打成'''的笨蛋~~~~![pic](https://gss0.baidu.com/94o3dSag_xI4khGko9WTAnF6hhy/zhidao/wh%3D600%2C800/sign=ef767b31462309f7e73aa514423e20cb/aec379310a55b3194bf8ecd043a98226cffc1715.jpg)
输入python manage.py createsuperuser后
要输入user
我输入完user后出现
django.db.utils.OperationalError: no such column: users_user.last_login错误
求大大解答
为什么我填写了表单以后,点击注册也跳转了首页,说明注册成功,在后台查看却没有注册的用户,只有一个空的用户模型在后台。进入python shell中使用User.objects.all()查看,显示没有此模型?可我数据库都迁移了的啊,求博主解答?
进python django shell试试吧
我也是啊QAQ不知道是怎么回事
请问你有没有继承那个usercreationform呢?
请问你是怎么解决的呢 我也遇到了这个问题
表单控件里面对于字符串长度的设置在哪里
这个是继承系统的那个usercreationform,注意,这是个model.form,字段的限制也就是在系统的那个user 模型model里面指定的
为何我的注册页面的提示都是英文呢?我看博主的都是汉语
settings 里面有一个语言设置 LANGUAGE_CODE = 'zh-hans'就行了
是的,谢谢
博主你好,出现了一个问题,我直接从你的github上拷下来也出现这个错误。
错误是django.db.utils.OperationalError: no such table: users_user
错误指向"D:\WorkSpace\django-auth-example-master\users\views.py", line 20, in register
if form.is_valid():
先要创建数据库,python manage.py migrate
博主,你好.
你的register函数好像有一个bug.
如果用户post提交表单, 但是表单不合法,
会报错
The view users.views.register didn't return an HttpResponse object. It returned None instead.
bug出现的环境:
同一个用户名, 注册就会有这种情况出现
满足条件, 用post提交, 但是表单不合法
错误信息:
The view users.views.register didn't return an HttpResponse object. It returned None instead.
谢谢指出,我测试一下看看。
没有返回值,检查下方法中是否存在没有return的分支,或者忘记写return的方法
因为博主的代码中,当满足表单是post且表单不合法时,没有任何的逻辑判断,可以自己在那个if is_valid()语句加个else语句,然后return 错误信息,或者返回某个页面
博主你好,请问密码复杂度检查是模板自身的功能吗?如果是,请问能否自定义,如果能,请问如何自定义。
谢谢。ps.我应该用伪代码写这个问题的
复杂度检查是表单实现的,具体需要看一遍这一块的源码,django的文档也没有详细说明。
默认用户的密码约束条件在setting.py中设计
AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', },]
其中
UserAttributeSimilarityValidator :检测密码和用户名之间的相似性
MinimumLengthValidator:检测密码长度 可以通过添加参数修改
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 'OPTIONS': { 'min_length': 9, }
CommonPasswordValidator:密码与通用密码的相似度 内置了1000个常用密码 无法被设置
NumericPasswordValidator:密码是否是纯数字检测
参考的官方文档在:https://docs.djangoproject.com/en/2.0/topics/auth/passwords/#password-validation
博主,我的代码是没有错的,register.html 也是直接copy出来的,但为什么加载出来的页面格式是没有经过渲染的格式,我后退一下再前进一下,页面格式就和你的一样了,这是怎么回事?
已经解决了,博主不用费心了,原来是有网才行......
博主的css 是网络的路径
博主- - 我发现一个bug 当我注册 密码输入123123 确认密码也输123123
出来的password2.help_text 的错误信息为 :密码必须全部为数字。
我觉得应该是密码不能全为数字吧(毕竟我用字母也是可以注册的)
顺便问一下这个help_text 错误信息的源码在哪- - 我点模板里的help_text 进去没找到= =
说错了 是password2.errors
找到源码了- - 是英文是说密码全为数字 而翻译是必须是数字 是zh-Hans翻译的问题
控件的相关帮助信息为英文,这是版本的问题吗,我用的django1.10
可能部分翻译不全,但是我的版本下都是中文的,1.10.
博主,如何实现邮箱验证注册呢?
看看 django-allauth 项目的文档,默认就是使用邮箱激活注册的。
我想请教下博主,如何实现操作提示啊,现在我弄了个登录页面,想实现密码错误时提示“账户密码不正确“并在表单中回显我填写的数据,登录成功时会有提示“登录成功”,而不是直接跳转到一个页面。
原来Django有messages模块
可以自己简单写个ajax啊!
另外想知道,博主的第三方登录,用什么实现?可否提供思路。
第三方登录使用的 django allauth。可以去 github 搜到这个项目。
好的,非常谢谢
学习了。