Django之注册登录注销

阅读量(4095)  |  发表 于 2010-06-05 19:02:59

一 实例前言:
Django内置认证模块auth拥有丰富的常用方法,今天就利用auth部分内置方法和User实体做一个小实例,来实现我们平常最常用的 注册,登陆 ,注销的功能。

二 实例环境:
实例的环境是windows下的Django1.2.1,数据库是sqlite3,项目的环境配置不再详细介绍,读者可以参考实例代码或者上次写的《Windows下配置Django开发环境》。

三 代码编写
建立level_1项目:
django-admin.py startproject level_1
新建Accounts App: 
cd level_1
manage.py startapp accounts
仅仅两行代码,Django能为我们配置了大部分的东西(当然还需要配置点东西,如指定数据库等,参照实例源码吧)。接下来就是实例的核心部分了,我们集中精力编写好它们。

因为本人喜欢先从urls入手,因为从urls入手最能整体把握整个实例。实例有4个功能点:首页的欢迎体验;注册新的用户;用户登录,最后登陆后的注销。思路明确了,所以urls可以写成:
1 url(r'^$', 'accounts.views.index',name="index"),
2 url(r'^accounts/index$', 'accounts.views.index',name="accounts_index"),
3 url(r'^accounts/register$', 'accounts.views.register',name="register"),
4 url(r'^accounts/login$', 'accounts.views.login',name="login"),
5 url(r'^accounts/logout$', 'accounts.views.logout',name="logout"),

第一行代码是附带的,相当直接输入域名(不指定页面或者action)的时候,默认指向到一个页面。它和第二行代码都是指向到首页。
这几行代码的的url函数,第一个参数代表在浏览器上url地址,第二个参数代表在浏览器上输入url地址后,指向views上的某个action;第三个参数是别名,供template模板使用。
比如浏览器上输入:http://域名/accounts/index  的时候,django会告诉浏览器 这个地址应该指向到 accounts app视图views的index 的action上。

接下来就是编写accounts app 的 views部分了;编好urls后,基本就知道views有几个action(方法)了;我们统筹一下如下:
def index(request):
    pass

def register(request):
    pass

def login(request):
    pass

def logout(request):
    pass

每个具体action实现思路没有清晰之前,我们可以像上面那样 把骨架定下来。然后就是对每个action构思了。
首页我们做个简单的东西,当用户没有登录时,显示:欢迎您 游客!  但当用户登录后就要显示:欢迎您 ***(用户昵称)了。
template_var={"w":_(u"欢迎您 游客!")}
    if request.user.is_authenticated():
        template_var["w"]=_(u"欢迎您 %s!")%request.user.username
    return render_to_response("accounts/welcome.html",template_var,\
                                        context_instance=RequestContext(request))

 我们把一个字典变量template_var传递给模板welcome.html,默认欢迎辞为:欢迎您游客! 如果用户登录了,把用户名加上,request.user.is_authenticated()是request里的一个方法,用于判断用户是否登录。刚开始,是没有任何用户的,所以request.user.is_authenticated()将返回一个False,等我们注册了一个用户,登录了,便能认证了。

接下来我们写注册的功能。注册涉及到表单的提交,很自然我们就想到了django 的form,使用form生成表单,才能把表单的东西提交给views。
注册表单我们简单点,只有邮件,昵称和密码这三个输入框,form这样写:
class RegisterForm(forms.Form):
    email=forms.EmailField(label=_(u"邮件"),max_length=30,widget=forms.TextInput(attrs={'size': 30,}))    
    password=forms.CharField(label=_(u"密码"),max_length=30,widget=forms.PasswordInput(attrs={'size': 20,}))
    username=forms.CharField(label=_(u"昵称"),max_length=30,widget=forms.TextInput(attrs={'size': 20,}))
    
    def clean_username(self):
        '''验证重复昵称'''
        users = User.objects.filter(username__iexact=self.cleaned_data["username"])
        if not users:
            return self.cleaned_data["username"]
        raise forms.ValidationError(_(u"该昵称已经被使用请使用其他的昵称"))
        
    def clean_email(self):
        '''验证重复email'''
        emails = User.objects.filter(email__iexact=self.cleaned_data["email"])
        if not emails:
            return self.cleaned_data["email"]
        raise forms.ValidationError(_(u"该邮箱已经被使用请使用其他的"))

表单的三个字段,forms.EmailField和forms.CharField都解析成html的input类型,其中EmailField字段会验证email格式的字符;widget=forms.PasswordInput顾名思义,会额外解析成type类型为密码型的input。

RegisterForm有两个子方法,clean_username会到数据库找输入的昵称,检查是否有重复的昵称,起到验证的作用;clean_email同样验证是否有重复的邮件账号。两个子方法里使用到User实体,这是Django内置的用户实体,对应一个用户表。filter是model的过滤方法,字段_iexact 相当 where 字段 like '%参数%' 只是不区分大小写。

form写好了,相当django生成表单的html,到views了,views将和form紧密结合,根据表单提交的数据做一些必要操作。请看代码:
def register(request):
    '''注册视图'''
    template_var={}
    form = RegisterForm()    
    if request.method=="POST":
        form=RegisterForm(request.POST.copy())
        if form.is_valid():
            username=form.cleaned_data["username"]
            email=form.cleaned_data["email"]
            password=form.cleaned_data["password"]
            user=User.objects.create_user(username,email,password)
            user.save()
            _login(request,username,password)#注册完毕 直接登陆
            return HttpResponseRedirect(reverse("index"))    
    template_var["form"]=form        
    return render_to_response("accounts/register.html",template_var,\
                        context_instance=RequestContext(request))

form初始的状态是未绑定数据的html代码,我们传给模板egister.html ,结果是三个空的input表单。当点击注册按钮提交数据时,form有个函数is_valid,是验证数据是否都符合条件。如果符合,直接填充给User实体,User.objects.create_user(username,email,password)就是构造出一个完整的实体,该方法便是User类内置的,很方便吧,不用再造轮子,重写注册的方法。user.save 就是把构造好的实体 保存到数据库里了。

注册action里,除了现实注册功能外,紧接着还进行了登录功能。
_login(request,username,password)是我们写的可重用方法,因为在login里会用得到,所以我们抽象出来了,其中里面实现的东西,大多是应用auth模块的内置函数。代码:
def _login(request,username,password):
    '''登陆核心方法'''
    ret=False
    user=authenticate(username=username,password=password)
    if user:
        if user.is_active:
            auth_login(request,user)
            ret=True
        else:
            messages.add_message(request, messages.INFO, _(u'用户没有激活'))
    else:
        messages.add_message(request, messages.INFO, _(u'用户不存在'))
    return ret

其中authenticate和auth_login事关键的函数,需要在auth模块里import出来:
from django.contrib.auth import authenticate, login as auth_login ,logout as auth_logout
auth_login是我们起的一个别名,原名是login,我们起别名是防止命名冲突,再者更好理解点。authenticate顾名思义,是通过昵称和密码 验证该用户,返回一个User实体;login内部进行不少的操作(初始化必要的request字段),但我们暂且不用关心这些,我们只需知道传一个用户实体,它会帮我们做好登录操作即可。
_login(request,username,password)抽象出来,我们接下来做登录视图的时候,用到它。代码如下:
def login(request):
    '''登陆视图'''
    template_var={}
    form = LoginForm()    
    if request.method == 'POST':
        form=LoginForm(request.POST.copy())
        if form.is_valid():
            _login(request,form.cleaned_data["username"],form.cleaned_data["password"])
            return HttpResponseRedirect(reverse("index"))
    template_var["form"]=form        
    return render_to_response("accounts/login.html",template_var,\
                                   context_instance=RequestContext(request))
这个视图流程和注册流程相似,首先初始化未绑定的相应的表单,当点击登录按钮的时候,views会验证输入的数据是否合格,合格后才做相应的操作,也就是登录操作,我们上面已经写好了登录的方法_login,直接引用即可。
其中LoginForm 是登录的form,这是我们在写views时预先做好的,仅仅两个字段:昵称和密码:
class LoginForm(forms.Form):
    username=forms.CharField(label=_(u"昵称"),max_length=30,widget=forms.TextInput(attrs={'size': 20,}))
    password=forms.CharField(label=_(u"密码"),max_length=30,widget=forms.PasswordInput(attrs={'size': 20,}))

这个比注册的form简单些,没有做手动的验证,只是生成表单用。

这样登录功能就算完成了。

最后便是注销功能了,这个来的简单些,不需要任何表单的东西,只需一个连接指向到views的action中执行里面的代码即可,views里都是auth模块帮我完成的。
我们刚才导出的函数:
from django.contrib.auth import authenticate, login as auth_login ,logout as auth_logout
logout 就是主角,我们给的别名是auth_logout。 views完整代码:
def logout(request):
    '''注销视图'''
    auth_logout(request)
    return HttpResponseRedirect(reverse('index'))
logout函数再简单不过了,我们只需要传入request,它都帮我们做完一切的工作。注销完成后,我们重指向到首页上。

有兴趣的童鞋,把整个实例的代码  下载  下来参考吧!






标签: Python Django开发环境 Django

评论 (25)  •  发表博客  • 

25条评论

user

panjj 说:
2010-06-05 11:46:55

通过这个实例发现Django内置用户名可以是中文的了,官方文档只说增加了一些如@等之类的字符,并没有说支持中文。

user

renwofei423 说:
2010-06-18 11:29:29

支持!呵呵 看来又找到了个可以好好学习django的地方了!
老师好!

user

游客 FeiFei 说:
2010-06-25 12:00:20

参考http://djangobook.py3k.cn/2.0/chapter14/
不用写代码的。

user

baoyalv 说:
2010-06-25 12:30:30

本文是一个简单的实例,旨在初学者,非初学者就免得看了...djangobook.py3k.cn 写的东西很广很详细,耐心看收获也不少。龅牙驴上写点简单的实例,只想让刚刚接触Django的朋友快速清晰django的学习方法。再有
djangobook.py3k.cn的东西,应该没有建立在Django1.2上,用上Django的一些内置方法,可以发现点新的东西,比如Message的使用,注册username的约束等等这些。
谢谢FeiFei的提醒:-)

user

游客 bubu 说:
2010-08-04 10:36:54

你好,我现在试着用django做一个个人的博客,读者发表评论成功后,页面中的form还保存上次用户的数据,如何清空form!!!

user

游客 bubu 说:
2010-08-04 10:37:33

你的评论好像也存在同样的问题......

user

游客 bubu 说:
2010-08-04 10:43:54

你的评论好像也存在同样的问题......

user

panjj 说:
2010-08-08 03:08:04

这个好控制的;把为绑定的form传到页面即可!

user

panjj 说:
2010-08-08 03:09:32

现在我的做法是,清除内容,保留用户的信息,这样就不用重新输入用户信息了,再有就是登陆的用户自动填充昵称和email。

user

游客 csharp 说:
2010-08-09 01:27:22

这个好控制的;把未绑定的form传到页面即可!

user

hqhwell 说:
2010-12-20 03:09:52

utf8没问题了吧,怎么下载的代码启动后提示:

'utf8', 'F:\\Study\\Python\\\xbc\xd3\xb0\xe0\xc9\xea\xc7\xeb\xc9\xf3\xc5\xfa\xcf\xb5\xcd\xb3\\1220\\level_1\\templates', 16, 17, 'unexpected code byte')

user

baoyalv 说:
2010-12-20 04:29:04

检查你下载的路径 是不是带中文的文件夹!

user

baoyalv 说:
2010-12-20 04:30:00

因为实例使用到IDE开发,全部的文件都是UTF-8了!

user

baoyalv 说:
2010-12-20 11:09:34

如果发现那些文件不是utf-8告诉我吧!

user

hqhwell 说:
2010-12-21 11:52:50

噢,的确是路径问题.thx!

user

anly_love 说:
2010-12-25 11:36:58

为什么,我把admin管理后台也装上去了,这个也能登陆注册,成功后不会出现注销。我看了,静态页面的判断也正常,单独放在一个项目中一切和上面实例一样,正常。

user

baoyalv 说:
2010-12-26 12:29:18

不知道你出现了什么问题?既然一切正常,就行了啊?

user

anly_love 说:
2010-12-26 12:27:22

登陆成功后还可以点击注册登录,没有注销按钮

user

baoyalv 说:
2010-12-26 02:54:16

这种情况有可能是因为缓存作怪!我也遇到这样的问题!有时需要清理一下缓存,如果清理缓存都不行的话,说明logout没有成功!

user

baoyalv 说:
2010-12-26 02:58:11

参考:http://baoyalv.com/blog/baoyalv/Django/2010/09/23/122

user

游客 baoyalv 说:
2011-01-07 09:39:37

测试数据 测试数据!

user

游客 yuxcer 说:
2011-04-16 12:15:07

新手求助啊,下载了代码后运行,打开默认网页出现以下错误

DatabaseError at /
no such table: django_session

这是什么原因呢?数据库错误?

user

游客 2goo 说:
2011-04-16 11:33:39

提示说,没有django_session表,你在终端执行manage.py syncdb

user

游客 嗯哼 说:
2012-01-01 02:35:16

login()中:form = LoginForm( request.POST.copy() )这个语句不懂,LoginForm这个类不接受任何一个参数啊

user

游客 2goo 说:
2012-01-01 11:28:40

LoginForm继承forms.Form的,而forms.Form能接受字典的参数。request.POST.copy()就是一个字段,如{'username','..','password':'...','email':'...'}

user

游客 2goo 说:
2012-01-01 11:29:25

request.POST.copy()就是一个字典,如:{'username','..','password':'...','email':'...'}

user

游客 嗯哼 说:
2012-01-02 01:26:27

为什么要写成request.POST.copy()而不是request.POST

user

baoyalv 说:
2012-01-02 10:09:27

有些代码是本人一些坏毛病,未加考虑的强迫症。不拷贝也能正常运行的!

我要 评论