技术笔记——Django+Nginx+uwsgi搭建自己的博客(十二)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/BetrayArmy/article/details/81274505

这个系列好久没有更新了,主要是因为之前没有找到下一步开发的方向。这几天又看了看django的文档,决定研究下django的权限和用户系统,并采用django的用户系统重写之前的users app,将以前涉及到users的功能全部替换为django的User类。

这篇博文主要介绍以下内容:1. django的auth.User的基本功能使用;2. 将原本注册在users类的user数据迁移到auth.User(以下简称为User)中,确保数据兼容性。

首先来看User类的基本功能。django中的User模块为我们提供了包括注册用户、用户登录、验证用户等等功能,我们可以使用这些功能来改写我们现有的users app的相关方法。此外,由于User提供的字段有限,我们还需建立一个新的model:UserProfile来存储User的扩展信息,并将其与User建立一对一关系。这样,我们就将原本的users类拆分为User+UserProfile两个类。

我们在users/models.py实现UserProfile:

# users/models.py
# ...
# from django.contrib.auth.models import User
# ...
class UserProfile(models.Model):
    user = models.OneToOneField(User,on_delete=models.CASCADE)
    logoimage = models.ImageField(upload_to='logoimages', null=True, blank=True, verbose_name=u'头像')
    # new field
    birthday = models.DateTimeField(null=True, blank=True, verbose_name=u'生日')
    email = models.CharField(max_length=255, null=True, blank=True, verbose_name=u'电子邮件')
    mobilephone = models.CharField(max_length=11, null=True, blank=True, verbose_name=u'手机号码')
    # new field end
    registertime = models.DateTimeField(default=timezone.now())

该类包含user这个指向User的一对一关系,以及原本在users中的用户信息字段。

有了新的UserProfile类,我们就要对userregister做一些修改,以便我们使用新的User+UserProfile来建立用户:

# users/views.py
from django.db.models.signals import post_save
from django.dispatch import receiver
# ...
@receiver(post_save,sender=User)
def createProfile(sender,created,instance,**kwargs):
    if created:
        profile = UserProfile(user=instance)
        profile.save()

def userregister(request):
    if request.method == 'POST':
         form = UserRegisterForm(request.POST,request.FILES)
         if form.is_valid():
             username = form.cleaned_data['username']
             password = form.cleaned_data['password']
             logoimage = form.cleaned_data['logoimage']
             birthday = form.cleaned_data['birthday']
             email = form.cleaned_data['email']
             mobile = form.cleaned_data['mobilephone']
             user = User.objects.create_user(username,'',password)
             user.userprofile.logoimage = logoimage
             user.userprofile.birthday = birthday
             user.userprofile.mobilephone = mobile
             user.userprofile.email = email
             user.userprofile.save()
             # form.save()
             result_info = 'success'
             return HttpResponseRedirect(reverse('users:registerResult', kwargs={'info': result_info}))
    else:
         form = UserRegisterForm()
    return render(request,'users/userregister.html',{'form':form})
# ...

这里我们抛弃了原本使用UserRegisterForm类,而是使用User提供的create_user方法来注册一个用户。create_user可以传入三个参数:username, email和password。在这里我们先填入username和password,而将email放入我们新建的UserProfile里。

由于UserProfile与User是一对一关系,所以当建立User时我们同时需要建立该user的userprofile。在这里我们使用django的信号机制。在django中,主要的内建信号包含以下四种:模型信号、管理信号、请求/响应信号以及测试信号。这里我们使用模型信号post_save来建立UserProfile。当任一个model发生了save行为后,都会发射post_save信号,由于create_user函数相当于一个save行为,因此我们要做的就是写一个函数来监听User发送的post_save信号,当检测到User的post_save后,就建立其对应的UserProfile。

我们建立一个createProfile(sender, created, instance,**kwargs)来监听User的post_save信号。这里使用receiver修饰器来指定监听的信号以及发送者sender。created用于标识是否是第一次建立User,如果是第一次建立的话,则建立对应的UserProfile;instance为发送者的实例,也即刚刚建立的User。

在建立好UserProfile后,我们使用表单中传来的值为刚才的UserProfile中各字段赋值、存储。

我们再来修改userlogin和logoff方法:

# users/views.py
from django.contrib.auth import authenticate,login,logout
# ...
def userlogin(request):
    if request.method == 'POST':
        form = UserLoginForm(request.POST)
        if form.is_valid():
            username = form.cleaned_data['username']
            password = form.cleaned_data['password']
            result_info = ''
            try:
                user = authenticate(username=username, password=password)
                if user is not None:
                    login(request, user)
                    result_info = 'success'
                else:
                    result_info = 'fail'
            except Exception as e:
                result_info = 'fail'
            return HttpResponseRedirect(reverse('users:loginResult', kwargs={'info': result_info}))
    else:
        form = UserLoginForm()
    return render(request, 'users/userlogin.html',{'form':form})
# ...
def logoff(request):
    logout(request)
    return HttpResponseRedirect(reverse('index'))

先来看userlogin函数的修改。我们使用User的authenticate方法来根据username和password来验证user是否存在。若user存在,我们使用login方法实现登录。原本的登录方法是我自己研究的,在登录后将当前的username存在session的currentuser中,现在使用了login方法登录,我们可以在之后使用request.user拿到当前登录的User对象。

logoff函数的修改也很简单,直接调用logout方法登出user就好。

下面我们要编写用户的迁移功能,即将原来users中的user迁移到User中,这样在其他功能中就不用再考虑两种User的兼容性。

# users/views.py
from django.contrib.auth.decorators import login_required,user_passes_test
# ...
@login_required
@user_passes_test(checksuperuser)
def migrateuser(request):
    oldusers = Users.objects.all()
    for olduser in oldusers:
        try:
            newuser = User.objects.create_user(olduser.username,'',olduser.password)
            newuser.userprofile.logoimage = olduser.logoimage
            newuser.userprofile.birthday = olduser.birthday
            newuser.userprofile.mobilephone = olduser.mobilephone
            newuser.userprofile.email = olduser.email
            newuser.userprofile.save()
            # Users.delete(username=olduser.username)
            result_info = 'success'
        except ValidationError:
            newuser = User.objects.get_by_natural_key(olduser.username)
            newuser.set_password(olduser.password)
        except Exception as e:
            result_info = e
    return render(request,'users/migrateuser.html',{'result_info':result_info})

这个用户迁移功能其实很简单:遍历原本users类的所有对象,把这些对象的相关字段用UserProfile再重新存储一次即可。至于异常的处理是因为我在测试时用新的注册功能注册了原本users的用户名,因此只需把原来的密码用set_password函数改过来即可(该函数也是auth提供的)。

显然我们不应该允许任意的用户都可以使用这个功能,所以我们使用两个装饰器来对这个功能做一些限制:@login_required和@user_pass_test。@login_required顾名思义,只有登录后才能访问;而@user_pass_test支持传入一个函数,执行的检查由这个函数来决定。我们使用checksuperuser函数来检查当前登录用户是否为超级用户:

# users/views.py
# ...
def checksuperuser(user):
    return user.is_superuser

若是超级用户,则允许他使用这个功能。超级用户的建立可以使用以下命令在myblogs目录下执行:

python manage.py createsuperuser --username=<username> --email=<email>

接下来我们修改剩下的userIndex和userinfo这两个小函数,使用request.user来替换掉原来获取user的方式:

# users/views.py
# ...
def userIndex(request,username):
    try:
        # user = Users.objects.get(username=username)
        currentUser = request.user
        blogList = Blog.objects.filter(auther=currentUser).filter(draft=False)
        content = {'username':username,
                   'currentUser':currentUser,
                   'blogList':blogList
                   }
    except Exception as e:
        content = {'username':e}
    return render(request,'users/userindex.html',content)


def userinfo(request,username):
    try:
        # user = Users.objects.get(username=username)
        currentUser = request.user
        birthday = currentUser.userprofile.birthday
        email = currentUser.userprofile.email
        registertime = currentUser.userprofile.registertime
        content = {'username':username,
                   'registertime':registertime,
                   'birthday':birthday,
                   'email':email,
                   'currentUser':currentUser
                   }
    except Exception as e:
        return render(request, 'users/pleaselogin.html')
    return render(request,'users/userinfo.html',content)

注意这里blogList的部分可以先不替换,因为目前我们的改动还没有涉及Blog app,blogs的auther外键仍然是旧的users类,所以这里要是改动的话一定会报错。

最后,我们向users/urls.py加入migrateuser的url,以及修改userTemplate,使迁移用户功能能被超级用户使用:

# users/urls.py
# ...
urlpatterns = [
    # ...
    url(r'^migrateuser/$',views.migrateuser,name='migrateuser')
]
# ...
<!-- userTemplate -->
<!-- ...... -->
{% if currentUser.is_superuser %}
<span><a href="{% url 'users:migrateuser' %}">迁移用户</a></span>
{% endif %}
<!-- ...... -->


<!-- migrateuser.html -->
{% extends "userTemplate.html" %}
{% block content %}
<div>
{% if "{{ result_info }}" == "success" %}
<h3>迁移用户成功,所有旧用户已迁移到新模型!</h3>
{% else %}
<h3>{{ result_info }}</h3>
{% endif %}
</div>
{% endblock %}

当以超级用户登录时,就可看到迁移用户的功能了:

在这篇博文中,我们实现了将旧users的数据迁移到新User+UserProfile的功能,并且对users app的所有功能做了改动以应用新的User实现。在下一篇博文中,我们将把新User的实现应用于Blogs app中,并同样修改Blogs以及index中涉及到user的所有功能。在这里会有一个很大的坑,敬请期待~

猜你喜欢

转载自blog.csdn.net/BetrayArmy/article/details/81274505