项目训练:美多商城(二)用户注册

1 创建子应用

cd ~/projects/meiduo_project/meiduo_mall/meiduo_mall/apps
python ../../manage.py startapp users

2 添加导包路径

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 添加了apps的导包路径
sys.path.append(BASE_DIR + '/apps')
# 如下导包也是可以的
# sys.path.insert(0, os.path.join(BASE_DIR, 'apps')) 
print(sys.path)

3 注册应用

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'users'
]

4 路由配置

# 总
url(r'^', include('users.urls', namespace='users'))
# 子
url(r'^register/$', views.RegisterView.as_view(), name='register')

5 返回页面接口

class RegisterView(View):
    """注册"""
    def get(self, request):
        return render(request, 'register.html')
  • register.html

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
	<title>美多商城-注册title>
	<link rel="stylesheet" type="text/css" href="/static/css/reset.css">
	<link rel="stylesheet" type="text/css" href="/static/css/main.css">
	<script type="text/javascript" src="/static/js/host.js">script>
    <script type="text/javascript" src="/static/js/axios-0.18.0.min.js">script>
	<script type="text/javascript" src="/static/js/jquery-1.12.4.min.js">script>
	<script type="text/javascript" src="/static/js/vue-2.5.16.js">script>
head>
<body>
	<div class="register_con">
		<div class="l_con fl">
			<a href="index.html" class="reg_logo"><img src="images/logo.png">a>
			<div class="reg_slogan">商品美 · 种类多 · 欢迎光临div>
			<div class="reg_banner">div>
		div>

		<div class="r_con fr">
			<div class="reg_title clearfix">
				<h1>用户注册h1>
				<a href="login.html">登录a>
			div>
			<div class="reg_form clearfix" id="app">
				<form method="post" class="register_form">
				<ul>
					<li>
						<label>用户名:label>
						<input type="text" name="user_name" id="user_name" v-model="username" @blur="check_username">
						<span class="error_tip">[[ error_name_message ]]span>
					li>					
					<li>
						<label>密码:label>
						<input type="password" name="pwd" id="pwd" v-model="password" @blur="check_pwd">
						<span class="error_tip">[[error_password_message]]span>
					li>
					<li>
						<label>确认密码:label>
						<input type="password" name="cpwd" id="cpwd" v-model="password2" @blur="check_cpwd">
						<span class="error_tip">[[error_password2_message]]span>
					li>
					<li>
						<label>手机号:label>
						<input type="text" name="phone" id="phone" v-model="mobile" @blur="check_phone">
						<span class="error_tip">[[error_phone_message]]span>
					li>
					<li>
                        <label>图形验证码:label>
                        <input type="text" name="pic_code" id="pic_code" class="msg_input" v-model="image_code" @blur="check_image_code">
                        <img src="images/pic_code.jpg" alt="图形验证码" class="pic_code">
                        <span class="error_tip">[[error_image_code_message]]span>
                    li>
					<li>
						<label>短信验证码:label>
						<input type="text" name="msg_code" id="msg_code" class="msg_input" v-model="sms_code" @blur="check_sms_code">
						<a href="javascript:;" class="get_msg_code">获取短信验证码a>
						<span class="error_tip">[[error_sms_code_message]]span>
					li>
					<li class="agreement">
						<input type="checkbox" name="allow" id="allow" checked="checked" v-model="allow" @blur="check_allow">
						<label>同意”美多商城用户使用协议“label>
						<span class="error_tip">[[error_allow_message]]span>
					li>
					<li class="reg_sub">
						<input type="submit" value="注 册">
					li>
				ul>				
				form>
			div>

		div>

	div>

	<div class="footer no-mp">
		<div class="foot_link">
			<a href="#">关于我们a>
			<span>|span>
			<a href="#">联系我们a>
			<span>|span>
			<a href="#">招聘人才a>
			<span>|span>
			<a href="#">友情链接a>		
		div>
		<p>CopyRight © 2016 北京美多商业股份有限公司 All Rights Reservedp>
		<p>电话:010-****888    京ICP备*******8号p>
	div>
	<script type="text/javascript" src="/static/js/common.js">script>
	<script type="text/javascript" src="/static/js/register.js">script>
body>
html>
  • register.js
var vm = new Vue({
     
    el: '#app',
    // 修改Vue变量的读取语法,避免和django模板语法冲突
    delimiters: ['[[', ']]'],
    data: {
     
        host: host,
        error_name: false,
        error_password: false,
        error_check_password: false,
        error_phone: false,
        error_image_code: false,
        error_sms_code: false,
        error_allow: false,
        error_name_message: '',
        error_password_message: '',
        error_password2_message: '',
        error_phone_message: '',
        error_image_code_message: '',
        error_sms_code_message: '',
        error_allow_message: '请勾选用户协议',
        image_code_id: '',
        image_code_url: '',
        sms_code_tip: '获取短信验证码',
        sending_flag: false,
        username: '',
        password: '',
        password2: '',
        mobile: '',
        image_code: '',
        sms_code: '',
        allow: true
    },
    mounted: function () {
     
        // 向服务器获取图片验证码
        this.generate_image_code();
    },
    methods: {
     
        generateUUID: function () {
     
            var d = new Date().getTime();
            if (window.performance && typeof window.performance.now === "function") {
     
                d += performance.now(); //use high-precision timer if available
            }
            var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
     
                var r = (d + Math.random() * 16) % 16 | 0;
                d = Math.floor(d / 16);
                return (c == 'x' ? r : (r & 0x3 | 0x8)).toString(16);
            });
            return uuid;
        },
        // 生成一个图片验证码的编号,并设置页面中图片验证码img标签的src属性
        generate_image_code: function () {
     
            // 生成一个编号 : 严格一点的使用uuid保证编号唯一, 不是很严谨的情况下,也可以使用时间戳
            this.image_code_id = this.generateUUID();
            // 设置页面中图片验证码img标签的src属性
            this.image_code_url = this.host + "/image_codes/" + this.image_code_id + "/";
            console.log(this.image_code_url);
        },
        // 检查用户名
        check_username: function () {
     
            var re = /^[a-zA-Z0-9_-]{5,20}$/;
            if (re.test(this.username)) {
     
                this.error_name = false;
                this.error_name_message = '';
            } else {
     
                this.error_name_message = '请输入5-20个字符的用户名';
                this.error_name = true;
            }
            // 检查重名
            if (this.error_name == false) {
     
                var url = this.host + '/usernames/' + this.username + '/count/';
                axios.get(url, {
     
                    responseType: 'json'
                })
                    .then(response => {
     
                        if (response.data.count > 0) {
     
                            this.error_name_message = '用户名已存在';
                            this.error_name = true;
                        } else {
     
                            this.error_name = false;
                            this.error_name_message = '';
                        }
                    })
                    .catch(error => {
     
                        console.log(error.response);
                    })
            }
        },
        // 检查密码
        check_pwd: function () {
     
            var re = /^[0-9A-Za-z]{8,20}$/;
            if (re.test(this.password)) {
     
                this.error_password = false;
                this.error_password_message = '';
            } else {
     
                this.error_password = true;
                this.error_password_message = '请输入8-20位的密码';
            }
        },
        // 确认密码
        check_cpwd: function () {
     
            if (this.password != this.password2) {
     
                this.error_check_password = true;
                this.error_password2_message = '两次输入的密码不一致';
            } else {
     
                this.error_check_password = false;
                this.error_password2_message = '';
            }
        },
        // 检查手机号
        check_phone: function () {
     
            var re = /^1[345789]\d{9}$/;
            if (re.test(this.mobile)) {
     
                this.error_phone = false;
                this.error_phone_message = '';
            } else {
     
                this.error_phone_message = '您输入的手机号格式不正确';
                this.error_phone = true;
            }
            if (this.error_phone == false) {
     
                var url = this.host + '/mobiles/' + this.mobile + '/count/';
                axios.get(url, {
     
                    responseType: 'json'
                })
                    .then(response => {
     
                        if (response.data.count > 0) {
     
                            this.error_phone_message = '手机号已存在';
                            this.error_phone = true;
                        } else {
     
                            this.error_phone = false;
                            this.error_phone_message = '';
                        }
                    })
                    .catch(error => {
     
                        console.log(error.response);
                    })
            }
        },
        // 检查图片验证码
        check_image_code: function () {
     
            if (!this.image_code) {
     
                this.error_image_code_message = '请填写图片验证码';
                this.error_image_code = true;
            } else {
     
                this.error_image_code = false;
                this.error_image_code_message = '';
            }

        },
        // 检查短信验证码
        check_sms_code: function () {
     
            if (!this.sms_code) {
     
                this.error_sms_code_message = '请填写短信验证码';
                this.error_sms_code = true;
            } else {
     
                this.error_sms_code = false;
                this.error_sms_code_message = '';
            }
        },
        // 检查是否勾选协议
        check_allow: function () {
     
            if (!this.allow) {
     
                this.error_allow = true;
                this.error_allow_message = '请勾选用户协议';
            } else {
     
                this.error_allow = false;
                this.error_allow_message = '';
            }
        },
        // 发送手机短信验证码
        send_sms_code: function () {
     
            if (this.sending_flag == true) {
     
                return;
            }
            this.sending_flag = true;

            // 校验参数,保证输入框有数据填写
            this.check_phone();
            this.check_image_code();

            if (this.error_phone == true || this.error_image_code == true) {
     
                this.sending_flag = false;
                return;
            }

            // 向后端接口发送请求,让后端发送短信验证码
            var url = this.host + '/sms_codes/' + this.mobile + '/?image_code=' + this.image_code + '&image_code_id=' + this.image_code_id;
            axios.get(url, {
     
                responseType: 'json'
            })
                .then(response => {
     
                    // 表示后端发送短信成功
                    if (response.data.code == '0') {
     
                        // 倒计时60秒,60秒后允许用户再次点击发送短信验证码的按钮
                        var num = 60;
                        // 设置一个计时器
                        var t = setInterval(() => {
     
                            if (num == 1) {
     
                                // 如果计时器到最后, 清除计时器对象
                                clearInterval(t);
                                // 将点击获取验证码的按钮展示的文本回复成原始文本
                                this.sms_code_tip = '获取短信验证码';
                                // 将点击按钮的onclick事件函数恢复回去
                                this.sending_flag = false;
                            } else {
     
                                num -= 1;
                                // 展示倒计时信息
                                this.sms_code_tip = num + '秒';
                            }
                        }, 1000, 60)
                    } else {
     
                        if (response.data.code == '4001') {
     
                            this.error_image_code_message = response.data.errmsg;
                            this.error_image_code = true;
                        } else {
      // 4002
                            this.error_sms_code_message = response.data.errmsg;
                            this.error_sms_code = true;
                        }
                        this.generate_image_code();
                        this.sending_flag = false;
                    }
                })
                .catch(error => {
     
                    console.log(error.response);
                    this.sending_flag = false;
                })
        },
        // 表单提交
        on_submit(){
     
            this.check_username();
            this.check_pwd();
            this.check_cpwd();
            this.check_phone();
            this.check_sms_code();
            this.check_allow();

            if (this.error_name == true || this.error_password == true || this.error_check_password == true
                || this.error_phone == true || this.error_sms_code == true || this.error_allow == true) {
     
                // 不满足注册条件:禁用表单
                window.event.returnValue = false;
            }

        }
    }
});
  • 期间遇到的问题
1.vue与html内容的双向绑定
解:用v-model
2.触发验证调用方法
解:用@blur,光标挪开触发

6 定义用户模型

class User(AbstractUser):
    """自定义用户模型类"""
    mobile = models.CharField(max_length=11, unique=True, verbose_name='手机号')

    class Meta:
        db_table = 'tb_users'
        verbose_name = '用户'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.username
  • 遇到报错
auth.User.groups: (fields.E304) Reverse accessor for 'User.groups' clashes with reverse accessor for 'User.groups'.
        HINT: Add or change a related_name argument to the definition for 'User.groups' or 'User.groups'.
auth.User.user_permissions: (fields.E304) Reverse accessor for 'User.user_permissions' clashes with reverse accessor for 'User.user_permissions'.
        HINT: Add or change a related_name argument to the definition for 'User.user_permissions' or 'User.user_permissions'.
users.User.groups: (fields.E304) Reverse accessor for 'User.groups' clashes with reverse accessor for 'User.groups'.
        HINT: Add or change a related_name argument to the definition for 'User.groups' or 'User.groups'.
users.User.user_permissions: (fields.E304) Reverse accessor for 'User.user_permissions' clashes with reverse accessor for 'User.user_permissions'.
        HINT: Add or change a related_name argument to the definition for 'User.user_permissions' or 'User.user_permissions'.
  • 解决
# 在settings中加入
AUTH_USER_MODEL = 'users.User'
  • 迁移
python manage.py makemigrations
python manage.py migrate

7 注册逻辑

    def post(self, request):
        # 获取数据
        username = request.POST.get('user_name')
        pwd = request.POST.get('pwd')
        cpwd = request.POST.get('cpwd')
        phone = request.POST.get('phone')
        image_code = request.POST.get('pic_code')
        sms_code = request.POST.get('msg_code')
        allow = request.POST.get('allow')
        # 校验参数
        if not all([username, pwd, cpwd, phone, image_code, sms_code]):
            return http.HttpResponseForbidden('缺少必传参数')
        if not re.match(r'^[a-zA-z0-9_-]{5,20}$', username):
            return http.HttpResponseForbidden('请输入5-20个字符的用户名')
        if not re.match(r'^[a-zA-Z0-9]{8,20}$', pwd):
            return http.HttpResponseForbidden('请输入8-20位的密码')
        if pwd != cpwd:
            return http.HttpResponseForbidden('两次密码输入不一致')
        if not re.match(r'^1[3-9]\d{9}$', phone):
            return http.HttpResponseForbidden('请输入正确的手机号')
        if allow != 'on':
            return http.HttpResponseForbidden('请勾选用户协议')
        # 通过验证后保存用户信息
        try:
            User.objects.create_user(username=username, password=pwd, mobile=phone)
        except DatabaseError:
            return render(request, 'register.html', {
     'register_errmsg': '注册失败'})
        return redirect('/')
  • 首页
创建子应用contents,然后三部曲:
注册子应用
总路由
子路由
  • 返回首页
class IndexView(View):
    def get(self, request):
        """首页"""
        return render(request, 'index.html')

8 状态保持

 # 通过验证后保存用户信息
        try:
            user = User.objects.create_user(username=username, password=pwd, mobile=phone)
        except DatabaseError:
            return render(request, 'register.html', {
     'register_errmsg': '注册失败'})
        login(request, user)
        return redirect('/')

9 用户名重复检测

class UsernameRepeatView(View):
    """用户名重复检测"""

    def get(self, request, username):
        count = User.objects.filter(username=username).count()
        return http.JsonResponse({
     'code': '200', 'errmsg': 'ok', 'count': count})

10 手机号重复检测

class PhoneRepeatView(View):
    """手机号重复检测"""
    def get(self, request, mobile):
        count = User.objects.filter(mobile=mobile).count()
        return http.JsonResponse({
     'code':200, 'errmsg': 'ok', 'count': count})

你可能感兴趣的:(#,django项目训练,django,注册,html,vue)