Django下的用户管理

1、数据库表

Django中有User模型 (django.contrib.auth.models.User),不过其中仅包含了有限的 7个固定字段 (username, first_name, last_name, email, is_staff, is_active, date_joined),其中is_staff(models.BooleanField)表示是 否允许此用户进入admin页面,is_active(models.BooleanField)表示该用户的帐号 是否为有效帐号,比如用户将自己注销(删除)了,其状态就可以由is_active=False表示。
扩展用户属性可以通过model的继承来实现,示例代码如下:
class CustomUser(User):
    age = models.PositiveIntegerField(blank=True)
    job = models.CharField(max_length=300, blank=True)
    location = models.CharField(max_length=300, blank=True)

    class Meta(User.Meta):
        db_table = 'customuser'
如上,CustomUser表示自定义用户,在基础字段上增加了age, job, location三个字段。如果执行python manage.py  syncdb,数据库会自动建好一下6个用户直接相关的表:
  • auth_group:用户分组表,初始为空,即默认没有分组
  • auth_group_permissions:用户分组权限表,初始为空
  • auth_permission:用户权限表,初始内容为对各个model的增、删、改的权限
  • auth_user:用户表,这里只包含7个固定字段,不包含我们自定义的字段
  • auth_user_groups:用户分组关系表,初始为空
  • auth_user_user_permissions:用户权限分配表
可见,我们期望的customuser并没有出现,看来我们需要手动建立了,建表语句如下所示:
CREATE TABLE auth_info
(
  user_ptr_id integer NOT NULL,
  age integer,
  job character varying,
  location character varying,
  CONSTRAINT auth_info_pkey PRIMARY KEY (user_ptr_id),
  CONSTRAINT auth_info_user_ptr_id_fkey FOREIGN KEY (user_ptr_id)
      REFERENCES auth_user (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED,
  CONSTRAINT auth_info_age_check CHECK (age >= 0)
)
WITH (
  OIDS=FALSE
);
ALTER TABLE auth_info
  OWNER TO postgres;
字段 user_ptr_id挺关键的,别忘了。
这样一来,我们就可以像操作普通model一样来操作CustomUser了,也可以将CustomUser当作User类型(继承产生的多态)在后续的用户模块中毫无障碍的使用。

2、用户注册

包括接下来的密码找回、个人信息修改,我们都将借助Django中的表单实现,Django中的表单还是很好用的。
注册表单
class UserCreateForm(UserCreationForm):
    '''
    创建新用户
    '''
    email = forms.EmailField()  #邮箱
    job = forms.CharField(max_length=300, required=False)   #职业
    age = forms.IntegerField(required=False)  #年龄 
    location = forms.CharField(max_length=300, required=False) #工作地点

    class Meta:
        model = User
        fields = (
           "username", "email", "password1", "password2", 
           "job", "age", "location"
        )
    
    # 判断年龄是否为负数
    def clean_age(self):
        age = self.cleaned_data['age']
        if age < 1:
            raise forms.ValidationError("年龄不能小于1岁")
        return age
    
    # 判断该email是否已被注册
    def clean_email(self):
        email = self.cleaned_data["email"]
        try:
            User._default_manager.get(email=email)
        except User.DoesNotExist:
            return email
        raise forms.ValidationError(
            "the email already exists.Plase change one or login",
            code='duplicate_email',
        )
            
    def save(self, commit=True):
        user = super(UserCreateForm, self).save(commit=False)
        user.job = self.cleaned_data["job"]
        user.age = self.cleaned_data["age"]
        user.email = self.cleaned_data["email"]
        user.location = self.cleaned_data["location"]
                
        if commit:
            user.save()
        return user
不一一解释了,代码自身应该能将自身解释的很清楚了,需要注意的是,如果某个字段是非必须的,需注明: required=False,默认的都是必须的。
注册页面
考虑到灵活性,我们没有采用Django代码中默认的方式,那样的话使用弹出框真的是很不方便。
上面省略了,用户名,邮箱唯一性、有效性检查的代码,这部分可自行添加。为防止页面在提交后跳转,我们使用了异步提交(ajaxSubmit, 来自jquery.form.js)
注册代码
def sign_up(request):
    '''
    注册
    '''
    if request.method == "POST":
        form = UserCreateForm(request.POST)
        if form.is_valid():
            usr = form.save()
        else:
            raise HttpInternalError()

3、用户登录

表单:django.contrib.auth.forms.AuthenticationForm
登录页面
上面的
表示用户登录后的跳转页面,这里让其跳到首页。
登录代码
def sign_in(request):
    '''
    登录
    '''
    params = utils.get_params(request)
    redirect_to = utils.get_dict_value(params, 'next', '')
    if request.method == "POST":
        form = AuthenticationForm(request, data=request.POST)
        if form.is_valid():
            login(request, form.get_user())
            rememberme = utils.get_dict_value(params, "remember-me")
            if rememberme == "on":
                request.session.set_expiry(7*24*3600)
            else:
                request.session.set_expiry(0)
            
            return utils.dumps_json({"next":redirect_to,"username": utils.get_param(params, "username")})
     
    raise HttpInternalError()
这里需要说明一下用户登录有效期的问题,默认有效期是 2周,但如果在代码里将session的过期设为了浏览器关闭即过期,那么以后用户再登录如果不进行如何设置的话,默认的就是浏览器关闭即过期了,所以这里当用户选择了“自动登录”,还需要显式的设置一次过期时间。

3、信息修改

表单
class UserProfileForm(UserChangeForm):
    '''
    个人信息修改
    '''
    class Meta:
        model = User
        fields = ('username','email','password','age', 'job', 'location')
        password = ReadOnlyPasswordHashField()

    def __init__(self, *args, **kwargs):
        super(UserProfileForm, self).__init__(*args, **kwargs)

    def clean_password(self):
        return self.initial["password"]

修改页面与上面大同小异,也是form+ajaxSubmit的组合,不再举例。
修改代码
def update(request):
    '''
    修改个人信息
    '''
    if request.method == 'POST':
        form = UserProfileForm(request.POST, instance=request.user.customuser)
        if form.is_valid():
            form.save()
            if 'username' in request.REQUEST:
                usr = CustomUser.objects.get(username = request.POST['username'])
                return str(usr)
            elif 'email' in request.REQUEST:
                usr = CustomUser.objects.get(email = request.POST['email'])
                return str(usr)
    raise HttpInternalError()

4、密码找回

在密码找回里使用了注册码(django-simple-captcha)防止用户恶意提交。基本流程是:用户点击“忘记密码”,弹出密码重置对话框(包括邮箱和注册码两个输入项),用户输入相关信息,提交,如果没问题,向其填写的邮箱中发送密码重置链接,如果有问题,停留在当前对话框,提示相关错误,等待用户重新输入相关信息。
表单
class CaptchaForm(forms.Form):
    email = forms.EmailField() 
    captcha = CaptchaField()
注册码模块
urls.py:
urlpatterns += patterns('',
    url(r'^captcha/', include('captcha.urls')),
)
settings.py
邮箱配置:
EMAIL_HOST = 'smtp.issas.ac.cn'             #SMTP地址
EMAIL_PORT = 25                         #SMTP端口
EMAIL_HOST_USER = '[email protected]'  #邮箱地址
EMAIL_HOST_PASSWORD = '******'        #邮箱密码

DEFAULT_FROM_EMAIL = EMAIL_HOST_USER


views.py
def reset_password_dialog(request):
    '''
    重置密码对话框
    '''
    form = CaptchaForm()
    return render_to_response("accounts/inc/password_reset_dialog.html", locals(),
                                      context_instance=RequestContext(request))
accounts/inc/password_reset_dialog.html


邮箱 {{form.email}}

验证码 {{form.captcha}}

如你所见,上述html代码中没有提交代码,这个对话框是ajax方式请求,提交代码在其母页面中。
重置密码模块
前端代码:
后端代码
urls.py:
urlpatterns += patterns('django.contrib.auth.views',
    url(r'^password/reset/(?P[0-9A-Za-z]+)-(?P.+)/$', 
        'password_reset_confirm', {"template_name":"accounts/inc/password_reset_confirm.html"}, name="password_reset_confirm"),
    url(r'^password/done/$', 'password_reset_complete', {"template_name":"accounts/inc/password_reset_complete.html"}, name="password_reset_complete")
)
views.py
def reset_password(request):
    '''
    重置用户密码
    '''
    if request.method == 'POST':
        
        form_sn = CaptchaForm(request.POST)
        if form_sn.is_valid():
            human = True
        else:
            return "sn_error"   #验证码错误
        
        form_email = PasswordResetForm(request.POST)
        email_template_name = "accounts/inc/password_reset_email.html"
        if form_email.is_valid():
            opts = {
                'use_https': request.is_secure(),
                'token_generator': default_token_generator,
                'from_email': settings.DEFAULT_FROM_EMAIL,
                'email_template_name': email_template_name,
                'request': request,
            }
            #form.save(**opts)
            
            from MapCloud.common.ThreadPool import async_execute
            async_execute(form_email.save, kwds=opts)
            
            return render_to_response("accounts/inc/password_reset_done.html", locals(),
                                      context_instance=RequestContext(request))
                        
    raise HttpInternalError()
这部分代码是参考着django.contrib.auth.forms.PasswordResetForm来写的,是有点麻烦,主要是其中的模板参数不清楚,下面一一解释一下(强烈建议看看django/contrib/admin/templates/registration中的默认模板):
  • password_reset_complete.html:用户通过重置链接重置密码成功后跳转到的页面
示例:


密码已重置,您现在可以返回首页重新登录!

  • password_reset_confirm.html:这个就是用户重置密码的页面
示例:

密码重置

{% if validlink %}
{% csrf_token %} {{ form.new_password1.errors }}

{{ form.new_password1 }}

{{ form.new_password2.errors }}

{{ form.new_password2 }}

{% else %}

密码重置失败,可能这个链接已经被使用过了,您可以重新申请密码重置

{% endif %}
  • password_reset_done.html:这个是用户输入了正确的邮箱和验证码,系统给出的确认信息。
示例:

我们向您的邮箱里发送了密码重置链接,请按链接中的指示重置密码。

如果没收到相关邮件,请确认您在上一步所填写的邮箱是否时您注册时使用的邮箱。

谢谢!

  • password_reset_email.html:这个是向用户发送的邮件的内容,即用户会在其邮箱看到的内容
示例:
{% autoescape off %}
您收到此封邮件是因为您请求重置 {{ site_name }} 上的帐号密码.

请点击下面的链接并设置一个新的密码:
{% block reset_link %}
{{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %}
{% endblock %}

另:您的用户名为:{{ user.get_username }}

谢谢您对我们的支持!

{% endautoescape %}
OK,这样这部分基本上就可以使用了。
另外,系统再向用户发送邮箱的会有一个比较明显的延迟,为此我们使用线程池构造了一个异步任务执行模块,这个会再另一篇文章了介绍。

5、密码修改

待续。。。











你可能感兴趣的:(Python)