django 注册、登录及第三方接口程序(4):扩展邮箱注册,登录,微博登录

1.邮箱注册

这里需要扩展User,两种解决办法,1,注册时将email字段内容赋给username,这种瞒天过海型的,另一种就是扩展user,这里介绍django1.5的扩展方法。

1.setting配置

AUTH_USER_MODEL = 'manager.MyUser'                         # 扩展表的位置,appname.Model, MyUser是manager app下models中的用户类
AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend', 'manager.models.CustomAuth')   # 后端认证

2.model
这里包含了扩展的方法和字段,贴代码如下,不明白可以看官网详解。

#coding=utf-8
from django.conf import settings
from django.db import models
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser


class UserManager(BaseUserManager):
    """通过邮箱,密码创建用户"""
    def create_user(self, email,username, password=None,type=None,**kwargs):
        if not email:
            raise ValueError(u'用户必须要有邮箱')

        user = self.model(
            email=UserManager.normalize_email(email),
            username=username,
            type=type if type else 0
        )
        user.set_password(password)
        if kwargs:
            if kwargs.get('sex', None): user.sex = kwargs['sex']
            if kwargs.get('is_active', None): user.is_active=kwargs['is_active']
            if kwargs.get('uid', None): user.uid=kwargs['uid']
            if kwargs.get('access_token', None): user.access_token=kwargs['access_token']
            if kwargs.get('url', None): user.url=kwargs['url']
            if kwargs.get('desc', None): user.desc=kwargs['desc']
            if kwargs.get('avatar', None): user.avatar=kwargs['avatar']

        user.save(using=self._db)
        return user

    def create_superuser(self, email, username, password):
        user = self.create_user(email,
            password=password,
           username=username,
        )
        user.is_admin = True
        user.save(using=self._db)
        return user


class MyUser(AbstractBaseUser):
    """扩展User"""
    email = models.EmailField(verbose_name='Email', max_length=255, unique=True,db_index=True)
    username = models.CharField(max_length=50, unique=True, db_index=True)
    is_active = models.BooleanField(default=True)
    is_admin = models.BooleanField(default=False)
    type = models.IntegerField(default=0)                   # 类型,0本站,1微博登录
    sex = models.IntegerField(default=1)                    # sex
    uid = models.CharField(max_length=50, null=True)                    # weibo uid
    access_token = models.CharField(max_length=100, null=True)          # weibo access_token
    url = models.URLField(null=True)                            # 个人站点
    desc = models.CharField(max_length=2000, null=True)         # 个人信息简介
    avatar = models.CharField(max_length=500, null=True)        # 头像
    objects = UserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['username']

    def get_full_name(self):
        # The user is identified by their email address
        return self.email

    def get_short_name(self):
        # The user is identified by their email address
        return self.email

    def __unicode__(self):
        return self.email

    def has_perm(self, perm, obj=None):
        "Does the user have a specific permission?"
        # Simplest possible answer: Yes, always
        return True

    def has_module_perms(self, app_label):
        "Does the user have permissions to view the app `app_label`?"
        # Simplest possible answer: Yes, always
        return True

    @property
    def is_staff(self):
        "Is the user a member of staff?"
        # Simplest possible answer: All admins are staff
        return self.is_admin


    class Meta:
        db_table = 'user'


class CustomAuth(object):
    """自定义用户验证"""
    def authenticate(self, email=None, password=None):
        try:
            user = MyUser.objects.get(email=email)
            if user.check_password(password):
                return user
        except MyUser.DoesNotExist:
            return None

    def get_user(self, user_id):
        try:
            user = MyUser.objects.get(pk=user_id)
            if user.is_active:
                return user
            return None
        except MyUser.DoesNotExist:
            return None

这样就可以使用扩展字段了。

2.注册/登录/更改密码表单

这里不罗嗦了,看代码就行了,总贴代码唯恐管理员移除了我的博客,吐槽下,博客园后台管理编辑文章markdown方式的超级烂。

class LoginForm(forms.Form):
""" =============================================================================== function: 表单登录类 developer: BeginMan add-time 2014/6/3 =============================================================================== """
email = forms.EmailField(label=u'邮箱',max_length=100,widget=forms.TextInput(
        attrs={'class': 'form-control', 'placeholder': u'邮箱', 'required': '', 'autofocus': ''}
    ),
)
pwd = forms.CharField(label=u'密码',widget=forms.PasswordInput(
        attrs={'class': 'form-control', 'placeholder': u'密码', 'required': ''}
    )
)
auto_login = forms.BooleanField(label=u'记住密码',required=False,
    widget=forms.CheckboxInput(attrs={'value': 1}),
)

def __init__(self, request=None, *args, **kwargs):
    self.request = request
    self.user_cache = None
    self.auth_login = None
    super(LoginForm, self).__init__(*args, **kwargs)


def clean(self):
    email = self.cleaned_data.get('email')
    password = self.cleaned_data.get('pwd')
    auth_login = self.cleaned_data.get('auth_login', None)

    if email and password:
        if not MyUser.objects.filter(email=email).exists():
            raise forms.ValidationError(u'该账号不存在')

        self.user_cache = authenticate(email=email, password=password)
        if self.user_cache is None:
            raise forms.ValidationError(u'邮箱或密码错误!')

        elif not self.user_cache.is_active:
            raise forms.ValidationError(u'该帐号已被禁用!')


    if auth_login:      # 如果用户勾选了自动登录
        self.auth_login = True

    return self.cleaned_data

def get_user_id(self):
    """获取用户id"""
    if self.user_cache:
        return self.user_cache.id
    return None

def get_user(self):
    """获取用户实例"""
    return self.user_cache

def get_auto_login(self):
    """是否勾选了自动登录"""
    return self.auth_login

def get_user_is_first(self):
    """获取用户是否是第一次登录"""
    is_first = False
    if self.user_cache and self.user_cache.type == -1:
        is_first = True
        self.user_cache.type == 0
        self.user_cache.save()
    return is_first



class RegisterForm(forms.Form):
    """ =============================================================================== function: 表单注册类 developer: BeginMan add-time 2014/6/3 =============================================================================== """
    email = forms.EmailField(label=u'Email', max_length=100, widget=forms.TextInput(
            attrs={'class': 'form-control', 'placeholder': u'Email', 'required': '', 'autofocus': ''}
        ),
    )
    pwd = forms.CharField(label=u'密码',widget=forms.PasswordInput(
            attrs={'class': 'form-control', 'placeholder': u'密码', 'required': ''}
        )
    )
    pwd2 = forms.CharField(label=u'密码(重复)',widget=forms.PasswordInput(
            attrs={'class': 'form-control', 'placeholder': u'重复密码', 'required': ''}
        )
    )

    def __init__(self, request=None, *args, **kwargs):
        self.request = request
        self.user = None
        super(RegisterForm, self).__init__(*args, **kwargs)

    def clean(self):
        data = self.cleaned_data
        email = data.get('email')
        pwd = data.get('pwd')
        pwd2 = data.get('pwd2')
        if MyUser.objects.filter(email=email).exists():
            raise forms.ValidationError(u'该邮箱已被注册')
        if pwd != pwd2:
            raise forms.ValidationError(u'两次输入密码不一致')
        if pwd2 == pwd and len(pwd) < 6:
            raise forms.ValidationError(u'密码不能小于6位')
        # 用户注册
        # type = -1表示首次
        avatar = random.choice(range(35))
        avatar = '/site_media/avatar/%s.jpg' % avatar
        # 生成用户并验证
        username = email.split('@')
        if len(username) !=2 :
            raise forms.ValidationError(u'邮箱格式不对')
        else:
            username = ValidUs(username[0])
            self.user = MyUser.objects.create_user(email=email, username=username, password=pwd, type=-1, avatar=avatar).id
        return data

    def get_user(self):
        """获取用户实例"""
        return self.user



class PasswordForm(forms.Form):
    """ =============================================================================== function: 用户修改密码 developer: BeginMan add-time 2014/6/3 =============================================================================== """
    oldpwd = forms.CharField(label=u'原始密码', widget=forms.PasswordInput(
    attrs={'class': 'form-control', 'placeholder': u'原始密码', 'required': ''})
    )
    password1 = forms.CharField(label=u'新密码', widget=forms.PasswordInput(
    attrs={'class': 'form-control', 'placeholder': u'密码长度在5-12位', 'required': ''})
    )
    password2 = forms.CharField(label=u'在输入一次', widget=forms.PasswordInput(
    attrs={'class': 'form-control', 'placeholder': u'在输入一次', 'required': ''})
    )

def __init__(self, user=None, *args, **kwargs):
    self.user = user
    self.newpwd = None
    super(PasswordForm, self).__init__(*args, **kwargs)

def clean(self):
    cleaned_data = super(PasswordForm, self).clean()
    oldpwd = cleaned_data.get("oldpwd")
    password1 = cleaned_data.get("password1")
    password2 = cleaned_data.get("password2")

    if not self.user.check_password(oldpwd):
        msg = u"原密码错误。"
        self._errors["oldpwd"] = self.error_class([msg])
        # raise forms.ValidationError(u'原密码错误')
    if password1 and password2:
        if password1 != password2:
            msg = u"两个密码字段不一致。"
            self._errors["password2"] = self.error_class([msg])
        if not 4 < len(password1) < 13:
            msg = u"密码要在5-12位之间。"
            self._errors["password1"] = self.error_class([msg])

    return cleaned_data



class AvatarForm(forms.Form):
    """ =============================================================================== function: 用户修改头像 developer: BeginMan add-time 2014/6/3 =============================================================================== """
    avatar = forms.ImageField(label=u'图片上传', widget=forms.FileInput(
    attrs={'class': 'form-control', 'placeholder': u'图片上传', 'required': ''})
    )

    def clean(self):
        cleaned_data = super(AvatarForm, self).clean()
        image = cleaned_data.get("avatar", None)
        if image:
            if image.content_type not in ['image/jpeg', 'image/png']:
                raise forms.ValidationError(u'你上传的是图片吗?')
            else:
                img = Image.open(image)
                w, h = img.size
                max_width = max_height = 1000
                if w >= max_width or h >= max_height:
                    raise forms.ValidationError(u'上传的图片要尺寸要小于或等于%s宽,%s高' % (max_width, max_height))
                if img.format.lower() not in ['jpeg', 'pjpeg', 'png', 'jpg']:
                    raise forms.ValidationError(u'暂时只接纳JPEG or PNG.')
                #validate file size
                if len(image) > (1 * 1024 * 1024):
                    raise forms.ValidationError('Image file too large ( maximum 1mb )')
        else:
            raise forms.ValidationError(u'额,图片呢?')
        return cleaned_data

3.微博登录

如果你的网站想接入微博登录,那么先看看新浪开发者平台上的python SDK,API等,这里提供自己的处理方式,还是贴代码。。

首先配置的东西如key等,建议放在settings中,如下:

 # 微博登陆
URL = 'http://codetheme.sinaapp.com'
APP_KEY = '你的'
APP_SERCET = '你的'
CALLBACK_URL = URL+'/login/weibo_check/'  # 回调地址

url路由就自己写吧,这里给出视图方法,

def weiboLogin(request):
    """微博登录"""
    client = APIClient(app_key=settings.APP_KEY, app_secret=settings.APP_SERCET, redirect_uri=settings.CALLBACK_URL)
    url = client.get_authorize_url()
    return HttpResponseRedirect(url)


def weibo_check(request):
    code = request.GET.get('code', None)
    now = datetime.datetime.now()
    if code:
        client = APIClient(app_key=settings.APP_KEY, app_secret=settings.APP_SERCET, redirect_uri=settings.CALLBACK_URL)
        r = client.request_access_token(code)
        access_token = r.access_token   # 返回的token,类似abc123xyz456
        expires_in = r.expires_in       # token过期的UNIX时间:http://zh.wikipedia.org/wiki/UNIX%E6%97%B6%E9%97%B4
        uid = r.uid
        # 在此可保存access token
        client.set_access_token(access_token, expires_in)
        request.session['access_token'] = access_token
        request.session['expires_in'] = expires_in
        request.session['uid'] = uid
        user = SupserWeibo(access_token=access_token, uid=uid, request=request)      # 实例化超级微博类
        # 更新数据库
        if MyUser.objects.filter(uid=uid).exists():
            MyUser.objects.filter(uid=uid).update(last_login=now)
            user.Login()    # 登陆
            return HttpResponseRedirect('/')
        else:
            # 创建用户并登陆
            u_id = user.createUser()
            if u_id:
                return HttpResponseRedirect('/manage/user/%s/' %u_id)
return HttpResponse('/404/')

这里自己写了个微博类,用于处理微博登录的基础数据,感兴趣的也可以看看,

#coding=utf-8
""" ================================== function: 基于新浪微博API,对微博登陆进行扩展 addDate: 2014-06-04 author: BeginMan ================================== """

from django.conf import settings
import urllib
import urllib2
import simplejson as json
from manager.models import MyUser
from django.contrib.auth import authenticate, login, logout
from django.http import HttpResponseRedirect

import datetime
# 默认图片
DEFAULT_PIC = 'http://images.cnitblog.com/news/66372/201405/271116202595556.jpg'

# 用户信息
USER_INFO_URL = 'https://api.weibo.com/2/users/show.json'
# 发送微博
SEND_WEIBO_URL = 'https://api.weibo.com/2/statuses/upload_url_text.json'

user_agent = 'Mozilla/5.0 (Windows NT 6.1; rv:28.0) Gecko/20100101 Firefox/28.0'
headers = {'User-Agent': user_agent}

class SupserWeibo(object):
    def __init__(self, access_token, uid, request=None, **kwargs):
        self.access_token = access_token
        self.uid = uid
        self.request = request
        self.user_cache = None
        self.kwargs = kwargs


    def createUser(self):
        """创建用户"""
        userInfo = self.getUserInfo()
        username=userInfo.get('screen_name')
        if MyUser.objects.filter(username=username).exists():
            username = username+'[weibo]'
        u_id = 0
        try:
            new_user = MyUser.objects.create_user(
                    email=str(self.uid) + '@weibo.com',
                    username=username,
                    password=self.uid,
                    type=1,
                    sex=int(userInfo.get('sex', 1)),
                    uid=self.uid,
                    access_token=self.access_token,
                    url=userInfo.get('url', ''),
                    desc =userInfo.get('description', ''),
                    avatar=userInfo.get('avatar_large')
            )
            u_id = new_user.id
        except:
            pass
        self.Login()    # 登陆
        return u_id

    def getUserInfo(self):
        """获取微博用户信息"""
        data = {'access_token': self.access_token, 'uid': self.uid}
        params = urllib.urlencode(data)
        values = urllib2.Request(USER_INFO_URL+'?%s' %params, headers=headers)
        response = urllib2.urlopen(values)
        result = json.loads(response.read())
        if result.get('error_code', None):
            # 写入日志
            print '获取用户信息失败'
            return False
        return result

    def SendWeibo(self):
        """用户发送微博"""
        status = self.kwargs.get('status', None)       # 微博内容
        visible = self.kwargs.get('visible', 0)     # 微博的可见性,0:所有人能看,1:仅自己可见,2:密友可见,3:指定分组可见,默认为0。
        url = self.kwargs.get('url', DEFAULT_PIC)   # 配图
        result = {}
        if status:
            data = {'access_token': self.access_token, 'status': status, 'visible':visible, 'url':url}
            params = urllib.urlencode(data)
            values = urllib2.Request(USER_INFO_URL+'?%s' %params, headers=headers)
            response = urllib2.urlopen(values)
            result = json.loads(response.read())
            if result.get('error_code', None):
                # 写入日志
                print '发送微博失败'
                return False
            return True
        return result

    def Login(self):
        """登陆"""
        user_ = MyUser.objects.filter(uid=self.uid)[0]
        user = authenticate(email=user_.email, password=self.uid)
        login(self.request, user)

你可能感兴趣的:(python,登陆,多接口)