博客园--登录注册

表结构:

注意:UserInfo表继承了AbstractUser,需要在settings 中写入:AUTH_USER_MODEL ="项目名称.UserInfo"

class UserInfo(AbstractUser):   #settings  :AUTH_USER_MODEL ="项目名称.UserInfo"
    '''用户信息表'''
    nid = models.BigAutoField(primary_key=True)
    nickname =models.CharField(max_length=32,verbose_name="昵称",unique=True)
    tel = models.IntegerField(verbose_name="电话",unique=True,null=True,blank=True)
    email = models.EmailField(max_length=64,verbose_name="邮箱")
    avatar = models.FileField(verbose_name="头像",upload_to="avatar",default="/avatar/default.png")
    create_time = models.DateTimeField(verbose_name="创建时间",auto_now_add=True)

    class Meta:
        verbose_name_plural = "用户信息表"
    def __str__(self):
        return self.username

class Article(models.Model):
    '''
    文章表
    '''
    title = models.CharField(max_length=64,verbose_name="文章标题")
    summary = models.CharField(max_length=244, verbose_name="文章概要")
    create_time = models.DateTimeField(verbose_name="创建时间")
    update_time = models.DateTimeField(verbose_name="修改时间",auto_now=True)
    poll_count = models.IntegerField(verbose_name="点赞数",default=0)
    comment_count = models.IntegerField(verbose_name="评论数",default=0)
    read_count = models.IntegerField(verbose_name="阅读数",default=0)
    # is_essence = models.BooleanField(verbose_name="是否精华",default=0)
    # is_top = models.BooleanField(verbose_name="是否置顶",default=0)
    user = models.ForeignKey(to="UserInfo",verbose_name="所属作者",null=True,blank=True)
    classify = models.ForeignKey(to="Classfication",verbose_name="所属类别",null=True,blank=True)
    tags = models.ManyToManyField(to="Tag",through="Article2tag",through_fields=('article', 'tag'),verbose_name="所属标签")
    site_category_article=models.ForeignKey("SiteArticle",verbose_name="左侧菜单文章类别",null=True)
    class Meta:
        verbose_name_plural = "文章表"
    def __str__(self):
        return self.title+"+"+self.user.username

class Article_detail(models.Model):
    '''文章细节表'''
    article = models.OneToOneField(to="Article",verbose_name="所属文章")
    content =models.TextField(verbose_name="文章内容")

    class Meta:
        verbose_name_plural = "文章细节表"

    def __str__(self):
        return self.article.title
class Tag(models.Model):
    '''标签表'''
    name = models.CharField(max_length=32,verbose_name="标签名")
    blog = models.ForeignKey(to="Blog",verbose_name="所属博客")
    class Meta:
        verbose_name_plural = "标签表"
    def __str__(self):
        return self.name+"+"+self.blog.title

class Article2tag(models.Model):
    article = models.ForeignKey(verbose_name="文章",to="Article")
    tag = models.ForeignKey(verbose_name="标签",to="Tag")
    class Meta:
        '''联合唯一'''
        unique_together = [
            ("article","tag")
        ]
    def __str__(self):
        return self.article.title+" + "+self.tag.name
class Comment(models.Model):
    '''评论表'''
    time = models.DateTimeField(verbose_name="评论时间",auto_now_add=True)
    content = models.CharField(max_length=265,verbose_name="评论内容")
    up_count = models.IntegerField(default=0)
    user = models.ForeignKey(to="UserInfo",verbose_name="评论人",null=True,blank=True)
    article = models.ForeignKey(to="Article",verbose_name="评论文章",null=True,blank=True)
    farther_comment = models.ForeignKey(to="Comment",verbose_name="父级评论",null=True,blank=True)
    # farther_comment = models.ForeignKey("self",verbose_name="父级评论",null=True,blank=True)

    class Meta:
        verbose_name_plural = "评论表"
    def __str__(self):
        return self.user.nickname+"----"+self.article.title+"+"+self.article.user.nickname

class Article_poll(models.Model):
    '''文章点赞表'''
    time = models.DateTimeField(verbose_name="点赞时间",auto_now_add=True)
    article = models.ForeignKey(to="Article",verbose_name="点赞文章",null=True,blank=True)   #一个文章可以有多个赞
    user = models.ForeignKey(to="UserInfo",verbose_name="点赞人",null=True,blank=True)
    # is_positive = models.BooleanField(default=1,verbose_name="点赞或踩")

    class Meta:
        '''联合唯一'''
        unique_together = ("user", "article",)
        verbose_name_plural = "文章点赞表"
    def __str__(self):
        return self.user.nickname+"---"+self.article.title+"+"+self.article.user.nickname

class Comment_poll(models.Model):
    '''评论点赞表'''
    time=models.DateTimeField(verbose_name="点赞时间",auto_now_add=True)
    # is_positive = models.BooleanField(verbose_name="点赞或踩",default=1)
    user = models.ForeignKey(to="UserInfo",verbose_name="点赞用户",null=True,blank=True)
    comment = models.ForeignKey(to="Comment",verbose_name="点赞所属评论",null=True,blank=True)   #一个评论可以有多个赞

    class Meta:
        '''联合唯一'''
        unique_together = ("user","comment",)
        verbose_name_plural = "评论点赞表"

class Blog(models.Model):
    '''个人站点表'''
    title = models.CharField(max_length=32,verbose_name="个人博客标题")
    url = models.CharField(max_length=64,verbose_name="个人博客后缀",unique=True)
    theme = models.CharField(max_length=32,verbose_name="博客主题")
    user = models.OneToOneField(to="UserInfo", verbose_name="所属用户")
    class Meta:
        '''通过admin录入数据的时候个中文显示'''
        verbose_name_plural = "个人站点表"

    def __str__(self):
        return self.title



class Classfication(models.Model):
    '''博主个人文章分类表'''
    title = models.CharField(max_length=32, verbose_name="分类标题")
    blog = models.ForeignKey(to="Blog",verbose_name="所属博客")

    class Meta:
        verbose_name_plural = "个人分类表"
    def __str__(self):
        return self.title
class SiteCategory(models.Model):
    name=models.CharField(max_length=32)

    class Meta:
        verbose_name_plural = "网站分类表"
    def __str__(self):
        return self.name
class SiteArticle(models.Model):
    name=models.CharField(max_length=32)
    site_category=models.ForeignKey("SiteCategory")
    class Meta:
        verbose_name_plural = "左侧菜单具体分类表"
    def __str__(self):
        return self.name 

登录

  返回登录页面

from django.contrib import auth

def login(request):
    if request.method=='POST':
        username=request.POST.get("username")
        password=request.POST.get("password")
        login_response={"is_login":False,"error_msg":None}
        user=auth.authenticate(username=username,password=password)
        if user:#用户名密码正确
            login_response["is_login"]=True
            auth.login(request,user) #  相当于session   request.session[is_login]=True
        else:
            login_response["error_msg"] = "username or password error"
        # print(login_response)
        return HttpResponse(json.dumps(login_response))
    return render(request, 'login.html')

  

  通过ajax提交数据,根据视图函数返回不同结果,如果response["is_login"]=True,则显示验证码插件

  反之,则在登录框下发返回response["error_msh"]里面的报错信息。

  注意:POST请求处理,每一个ajax函数都要写:headers:{"X-CSRFToken":$.cookie('csrftoken')},

 登录Ajax:

var handlerPopup = function (captchaObj) {
     // 成功的回调
        captchaObj.onSuccess(function () {
            var validate = captchaObj.getValidate();
            $.ajax({
                url: "/pc-geetest/ajax_validate", // 进行二次验证
                type: "post",
                dataType: "json",
                data: {
                    username: $('#username1').val(),
                    password: $('#password1').val(),
                    geetest_challenge: validate.geetest_challenge,
                    geetest_validate: validate.geetest_validate,
                    geetest_seccode: validate.geetest_seccode
                },
                headers:{"X-CSRFToken":$.cookie('csrftoken')},
                success: function (data) {
                    if (data && (data.status === "success")) {
                       location.href="/index/";
                    } else {
                        $(".error").html(response["error_msg"]).css("color", "red");
                    }
                }
            });
        });
          // 将验证码加到id为captcha的元素里
            captchaObj.appendTo("#popup-captcha");
            // 更多接口参考:http://www.geetest.com/install/sections/idx-client-sdk.html
            $("#popup-submit").click(function () {
                $.ajax({
                    url: "/login/",
                    type: "POST",
                    data: {
                        "username": $("#username").val(),
                        "password": $("#password").val(),
                        "csrfmiddlewaretoken": $("[name='csrfmiddlewaretoken']").val(),
                    },
                    headers:{"X-CSRFToken":$.cookie('csrftoken')},
                    success: function (data) {
                        var response = JSON.parse(data);
                        console.log(captchaObj);
                        if (response["is_login"]) {
                            console.log(response["is_login"]);
                            captchaObj.show();
                        } else {
                            $(".error").html(response["error_msg"]).css("color", "red")
                        }
                    }
                });
            })
 };
        // 验证开始需要向网站主后台获取id,challenge,success(是否启用failback)
       $.ajax({
        url: "/pc-geetest/register?t=" + (new Date()).getTime(), // 加随机数防止缓存
        type: "get",
        dataType: "json",
        headers:{"X-CSRFToken":$.cookie('csrftoken')},
        success: function (data) {
            // 使用initGeetest接口
            // 参数1:配置参数
            // 参数2:回调,回调的第一个参数验证码对象,之后可以使用它做appendTo之类的事件
            initGeetest({
                gt: data.gt,
                challenge: data.challenge,
                product: "popup", // 产品形式,包括:float,embed,popup。注意只对PC版验证码有效
                offline: !data.success // 表示用户后台检测极验服务器是否宕机,一般不需要关注
                // 更多配置参数请参见:http://www.geetest.com/install/sections/idx-client-sdk.html#config
            }, handlerPopup);
        }
 });

插件url:

 url(r'^pc-geetest/register',views.pcgetcaptcha),
    url(r'^pc-geetest/validate$',views.pcvalidate),
    url(r'^pc-geetest/ajax_validate',views.pcajax_validate),

插件视图函数:

from app.geetest import GeetestLib
pc_geetest_id = "b46d1900d0a894591916ea94ea91bd2c"
pc_geetest_key = "36fc3fe98530eea08dfc6ce76e3d24c4"
mobile_geetest_id = "7c25da6fe21944cfe507d2f9876775a9"
mobile_geetest_key = "f5883f4ee3bd4fa8caec67941de1b903"

def pcgetcaptcha(request):
    user_id = 'test'
    gt = GeetestLib(pc_geetest_id, pc_geetest_key)
    status = gt.pre_process(user_id)
    request.session[gt.GT_STATUS_SESSION_KEY] = status
    request.session["user_id"] = user_id
    response_str = gt.get_response_str()
    return HttpResponse(response_str)

def pcvalidate(request):
    if request.method == "POST":
        gt = GeetestLib(pc_geetest_id, pc_geetest_key)
        challenge = request.POST.get(gt.FN_CHALLENGE, '')
        validate = request.POST.get(gt.FN_VALIDATE, '')
        seccode = request.POST.get(gt.FN_SECCODE, '')
        status = request.session[gt.GT_STATUS_SESSION_KEY]
        user_id = request.session["user_id"]
        print("status",status)
        if status:
            result = gt.success_validate(challenge, validate, seccode, user_id)
        else:
            result = gt.failback_validate(challenge, validate, seccode)
        result = "<html><body><h1>登录成功</h1></body></html>" if result else "<html><body><h1>登录失败</h1></body></html>"
        return HttpResponse(result)
    return HttpResponse("error")

def pcajax_validate(request):

    if request.method == "POST":
        gt = GeetestLib(pc_geetest_id, pc_geetest_key)
        challenge = request.POST.get(gt.FN_CHALLENGE, '')
        validate = request.POST.get(gt.FN_VALIDATE, '')
        seccode = request.POST.get(gt.FN_SECCODE, '')
        status = request.session[gt.GT_STATUS_SESSION_KEY]
        user_id = request.session["user_id"]
        print("status",status)
        if status:
            result = gt.success_validate(challenge, validate, seccode, user_id)
        else:
            result = gt.failback_validate(challenge, validate, seccode)
        result = {"status":"success"} if result else {"status":"fail"}
        return HttpResponse(json.dumps(result))
    return HttpResponse("error")

import sys
import random
import json
import requests
import time
from hashlib import md5


if sys.version_info >= (3,):
    xrange = range    

VERSION = "3.0.0"


class GeetestLib(object):

    FN_CHALLENGE = "geetest_challenge"
    FN_VALIDATE = "geetest_validate"
    FN_SECCODE = "geetest_seccode"

    GT_STATUS_SESSION_KEY = "gt_server_status"

    API_URL = "http://api.geetest.com"
    REGISTER_HANDLER = "/register.php"
    VALIDATE_HANDLER = "/validate.php"
    JSON_FORMAT = False

    def __init__(self, captcha_id, private_key):
        self.private_key = private_key
        self.captcha_id = captcha_id
        self.sdk_version = VERSION
        self._response_str = ""


    def pre_process(self, user_id=None,new_captcha=1,JSON_FORMAT=1,client_type="web",ip_address=""):
        """
        验证初始化预处理.
        //TO DO  arrage the parameter
        """
        status, challenge = self._register(user_id,new_captcha,JSON_FORMAT,client_type,ip_address)
        self._response_str = self._make_response_format(status, challenge,new_captcha)
        return status

    def _register(self, user_id=None,new_captcha=1,JSON_FORMAT=1,client_type="web",ip_address=""):
        pri_responce = self._register_challenge(user_id,new_captcha,JSON_FORMAT,client_type,ip_address)
        if pri_responce:
            if JSON_FORMAT == 1:
                response_dic = json.loads(pri_responce)
                challenge = response_dic["challenge"]
            else:
                challenge = pri_responce
        else:
            challenge=" "
        if len(challenge) == 32:
            challenge = self._md5_encode("".join([challenge, self.private_key]))
            return 1,challenge
        else:
            return 0, self._make_fail_challenge()

    def get_response_str(self):
        return self._response_str

    def _make_fail_challenge(self):
        rnd1 = random.randint(0, 99)
        rnd2 = random.randint(0, 99)
        md5_str1 = self._md5_encode(str(rnd1))
        md5_str2 = self._md5_encode(str(rnd2))
        challenge = md5_str1 + md5_str2[0:2]
        return challenge

    def _make_response_format(self, success=1, challenge=None,new_captcha=1):
        if not challenge:
            challenge = self._make_fail_challenge()
        if new_captcha:
            string_format = json.dumps(
                {'success': success, 'gt':self.captcha_id, 'challenge': challenge,"new_captcha":True})
        else:
            string_format = json.dumps(
                {'success': success, 'gt':self.captcha_id, 'challenge': challenge,"new_captcha":False})
        return string_format

    def _register_challenge(self, user_id=None,new_captcha=1,JSON_FORMAT=1,client_type="web",ip_address=""):
        if user_id:
            register_url = "{api_url}{handler}?gt={captcha_ID}&user_id={user_id}&json_format={JSON_FORMAT}&client_type={client_type}&ip_address={ip_address}".format(
                    api_url=self.API_URL, handler=self.REGISTER_HANDLER, captcha_ID=self.captcha_id, user_id=user_id,new_captcha=new_captcha,JSON_FORMAT=JSON_FORMAT,client_type=client_type,ip_address=ip_address)
        else:
            register_url = "{api_url}{handler}?gt={captcha_ID}&json_format={JSON_FORMAT}&client_type={client_type}&ip_address={ip_address}".format(
                    api_url=self.API_URL, handler=self.REGISTER_HANDLER, captcha_ID=self.captcha_id,new_captcha=new_captcha,JSON_FORMAT=JSON_FORMAT,client_type=client_type,ip_address=ip_address)
        try:
            response = requests.get(register_url, timeout=2)
            if response.status_code == requests.codes.ok:
                res_string = response.text
            else:
                res_string = ""
        except:
            res_string = ""
        return res_string

    def success_validate(self, challenge, validate, seccode, user_id=None,gt=None,data='',userinfo='',JSON_FORMAT=1):
        """
        正常模式的二次验证方式.向geetest server 请求验证结果.
        """
        if not self._check_para(challenge, validate, seccode):
            return 0
        if not self._check_result(challenge, validate):
            return 0
        validate_url = "{api_url}{handler}".format(
            api_url=self.API_URL, handler=self.VALIDATE_HANDLER)
        query = {
            "seccode": seccode,
            "sdk": ''.join( ["python_",self.sdk_version]),
            "user_id": user_id,
            "data":data,
            "timestamp":time.time(),
            "challenge":challenge,
            "userinfo":userinfo,
            "captchaid":gt,
            "json_format":JSON_FORMAT
        }
        backinfo = self._post_values(validate_url, query)
        if JSON_FORMAT == 1:
            backinfo = json.loads(backinfo)
            backinfo = backinfo["seccode"]
        if backinfo == self._md5_encode(seccode):
            return 1
        else:
            return 0

    def _post_values(self, apiserver, data):
        response = requests.post(apiserver, data)
        return response.text

    def _check_result(self, origin, validate):
        encodeStr = self._md5_encode(self.private_key + "geetest" + origin)
        if validate == encodeStr:
            return True
        else:
            return False

    def failback_validate(self, challenge, validate, seccode):
        """
        failback模式的二次验证方式.在本地对轨迹进行简单的判断返回验证结果.
        """
        if not self._check_para(challenge, validate, seccode):
            return 0
        validate_result = self._failback_check_result(
            challenge, validate,)
        return validate_result

    def _failback_check_result(self,challenge,validate):
        encodeStr = self._md5_encode(challenge)
        if validate == encodeStr:
            return True
        else:
            return False



    def _check_para(self, challenge, validate, seccode):
        return (bool(challenge.strip()) and bool(validate.strip()) and  bool(seccode.strip()))



    def _md5_encode(self, values):
        if type(values) == str:
            values = values.encode()
        m = md5(values)
        return m.hexdigest()

注册

form组件:

from django.forms import Form
from django.forms import fields
from django.forms import widgets
from django.forms import ValidationError
from app import models
class Reg_Form(Form):
     username=fields.CharField(
         required=True,
         min_length=3,
         max_length=10,
         error_messages={
             "required": "不能为空","min_length":"至少3位","max_length":"至多10位"},
         widget=widgets.TextInput(attrs={"class":"form-control input-lg","placeholder":"Username"})
     )
     password=fields.CharField(
         required=True,
         min_length=4,
         max_length=10,
         error_messages={
             "required": "不能为空","min_length":"至少4位","max_length":"至多12位"},
         widget=widgets.PasswordInput(attrs={"class":"form-control input-lg","placeholder":"Password"})
     )
     ret_password=fields.CharField(
         required=True,
         min_length=4,
         max_length=10,
         error_messages={
             "required": "不能为空","min_length":"至少4位","max_length":"至多12位"},
         widget=widgets.PasswordInput(attrs={"class":"form-control input-lg","placeholder":"Password"})
     )
     nickname = fields.CharField(
         required=True,
         min_length=2,
         max_length=8,
         error_messages={
             "required": "不能为空", "min_length": "至少2位", "max_length": "至多8位"},
         widget=widgets.TextInput(attrs={"class": "form-control input-lg", "placeholder": "Nickname"})
     )
     tel=fields.CharField(
         required=True,
         min_length=11,
         error_messages={
             "required": "不能为空", "min_length": "手机号需要11位", },
         widget=widgets.TextInput(attrs={"class": "form-control input-lg", "placeholder": "Phone"})
     )
     email=fields.EmailField(
         required=True,
         error_messages={
             "required": "不能为空" },
         widget=widgets.EmailInput(attrs={"class": "form-control input-lg", "placeholder": "Email"})
     )

     def clean_username(self):
        ret=models.UserInfo.objects.filter(username=self.cleaned_data.get("username"))
        if not ret:
            return self.cleaned_data.get("username")
        else:
            raise ValidationError("用户名已注册")
     def clean_password(self):
        data=self.cleaned_data.get("password")
        if not data.isdigit():
            return self.cleaned_data.get("password")
        else:
            raise ValidationError("密码不能全是数字")
     def clean(self):
        if self.cleaned_data.get("password") == self.cleaned_data.get("ret_password"):
            return self.cleaned_data
        else:
            raise  ValidationError("两次密码不一致")

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

    

首先:用户请求,返回注册表单,通过ajax提交数据,因为有头像:二进制文件,用FormData提交

注册Ajax:

/**
 * Created by 张宁阳 on 2017/11/21.
 */
   /**
 * Created by 张宁阳 on 2017/11/21.
 */
$("#popup-submit").click(function () {
    var formdata=new FormData();
       formdata.append("username",$("#id_username").val());
       formdata.append("password",$("#id_password").val());
       formdata.append("ret_password",$("#id_ret_password").val());
       formdata.append("nickname",$("#id_nickname").val());
       formdata.append("tel",$("#id_tel").val());
       formdata.append("email",$("#id_email").val());
       formdata.append("avatar_file",$("#avatar_file")[0].files[0]);
    $.ajax({
        url:"/register/",
        type:"post",
        data:formdata,
        contentType:false,
        processData:false,
        headers:{"X-CSRFToken":$.cookie('csrftoken')},
        success:function (data) {
            var data=JSON.parse(data);
            if (data["is_success"]){
                location.href="/login/"
            }else{
                $.each(data["err_msg"],function (x,y) {
                     $span=$("<span>");
                     $span.addClass("pull-right").css("color","red");
                     $span.html(y[0]);
                     $("#id_"+x).after($span).parent().addClass("has-error");
                     if (x=="__all__"){
                            $("#id_ret_password").after($span)
                       }
                })
            }
        }
   })
});
$("#avatar_file").change(function () {
   var file=$(this)[0].files[0];
   var reader=new FileReader();
        reader.readAsDataURL(file);
        reader.onload=function () {
            $("#img")[0].src=this.result
        }
});

"############################################"
ajax
	通过FormData获取前端页面input值(form组件自动创建的input标签id默认为id_字段名称)
	通过ajax传送
	
	success:
		
	反序列化数据
		如果data["is_success"]为True:
			跳转到登录页面
		反之:
			$.each(data["err_msg"],function (x,y) {
                     	$span=$("<span>");
                     	$span.addClass("pull-right").css("color","red");
                     	$span.html(y[0]);
                     	$("#id_"+x).after($span).parent().addClass("has-error");
                     	if (x=="__all__"){
                        $("#id_ret_password").after($span)
                       }
                })
		注释:	x为出错form组件字段、y为报错
			each循环报错信息、新建span标签,给其增加样式(右漂浮,红色)
			span的内容为报错信息
			给出错的input标签后边添加span标签,并使其父级标签has-error
			如果x使__all__:将报错添加到重新输入密码后边的span中

  

注意:配置media

1、settings中
				MEDIA_ROOT=os.path.join(BASE_DIR,"app","media","upload")
			   	MEDIA_URL="/media/"
2、urls中
				from django.views.static import serve
				from django.conf import settings
		url(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),

  

将头像作为上传文件input框

头像:
</div>
                 <div class="form-group avatar">
                <label for="avatar">头像:</label><img src="/static/img/default.png" id="img">
                <input type="file" class="input-lg" id="avatar_file" >
              </div>		
默认图片路径
		将图片跟上传文件的input框修改为块级标签,设置为同等长和宽
		将图片跟上传文件的input框的父级标签相对定位,然后绝对定位自己
		相对定位:    position: relative;
		绝对定位:    position: absolute;
		上传文件的input框透明度改为0:     opacity: 0;

  

注册视图函数

if request.method=="GET":
        form =Reg_Form(request)
        return render(request,"register.html",{"form":form})
    else:
        form=Reg_Form(request,request.POST)
        response={"is_success":False,"err_msg":None}
        if form.is_valid():
            username=form.cleaned_data["username"]
            password=form.cleaned_data["password"]
            # password=make_password(password)
            nickname=form.cleaned_data["nickname"]
            tel=form.cleaned_data["tel"]
            email=form.cleaned_data["email"]
            avatar_file=request.FILES.get("avatar_file")

            models.UserInfo.objects.create_user(username=username,password=password,nickname=nickname,tel=tel,email=email,avatar=avatar_file)
            response["is_success"]=True
        else:
           response["err_msg"]=form.errors
        return HttpResponse(json.dumps(response))

注册时,因为需要跟数据库密文密码比对,所以给密码加密:            
	from django.contrib.auth.hashers import make_password
	password=form.cleaned_data["password"]
	password=make_password(password)
因为继承了AbstractUser类:
      创建时create_user即可    

  

  

 

posted @ 2017-11-25 15:47  飞天的鱼  阅读(1551)  评论(0)    收藏  举报