天王盖地虎宝塔镇河妖 阅读(7) 评论(0)

 前面我们先分析了一下,做这个项目需要的几张表,今天我们从配置文件开始一步一步去解释这么的原因

首先先来看setting.py文件:

"""
Django settings for mybbs project.

Generated by 'django-admin startproject' using Django 1.11.11.

For more information on this file, see
https://docs.djangoproject.com/en/1.11/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.11/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'b)f=d-08hdi_w_z1^=5e9oom9#+h1a*jk-y#u!-vt$rj%5y&aj'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myapp.apps.MyappConfig',
    'debug_toolbar'          #https://www.cnblogs.com/ManyQian/p/9277861.html 具体详情参考这里
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'debug_toolbar.middleware.DebugToolbarMiddleware',    
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]


ROOT_URLCONF = 'mybbs.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')]
        ,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'mybbs.wsgi.application'


# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases

# DATABASES = {
#     'default': {
#         'ENGINE': 'django.db.backends.sqlite3',
#         'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
#     }
# }

DATABASES={
    'default':{
        'ENGINE':'django.db.backends.mysql',   #数据库的配置
        'NAME':'duoduo',
        'USER':'root',
        'PASSWORD':'123',
        'HOST':'127.0.0.1',
        'PORT':3306,
    }
}


# Password validation
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = False  #本机的时间,不用考虑时区的问题


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/

STATIC_URL = '/static/'

STATICFILES_DIRS=[
    os.path.join(BASE_DIR,'static')
]

# 指定一下认证使用 自定义的UserInfo表
AUTH_USER_MODEL='myapp.UserInfo'


#用户上传的文件配置项
MEDIA_URL='/media/'
MEDIA_ROOT=os.path.join(BASE_DIR,'media')


# 日志配置  Google浏览器jQuery 的URL在国内不支持
DEBUG_TOOLBAR_CONFIG ={
    'JQUERY_URL':'//cdn.bootcss.com/jquery/2.2.4/jquery.min.js',
}  #设置为空,就用项目中的jQuery

BASE_LOG_DIR = os.path.join(BASE_DIR, "log")

LOGGING = {
    'version': 1,
    # 禁用已经存在的logger实例
    'disable_existing_loggers': False,
    # 定义日志 格式化的 工具
    'formatters': {
        'standard': {
            'format': '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]'
                      '[%(levelname)s][%(message)s]'
        },
        'simple': {
            'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
        },
        'collect': {
            'format': '%(message)s'
        }
    },
    # 过滤
    'filters': {
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    # 日志处理器
    'handlers': {
        'console': {
            'level': 'DEBUG',
            'filters': ['require_debug_true'],  # 只有在Django debug为True时才在屏幕打印日志
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },

        'default': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自动切
            'filename': os.path.join(BASE_LOG_DIR, "info.log"),  # 日志文件
            'maxBytes': 1024 * 1024 * 50,  # 日志大小 50M
            'backupCount': 3,
            'formatter': 'standard',
            'encoding': 'utf-8',
        },

        'error': {
            'level': 'ERROR',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自动切
            'filename': os.path.join(BASE_LOG_DIR, "err.log"),  # 日志文件
            'maxBytes': 1024 * 1024 * 50,  # 日志大小 50M
            'backupCount': 5,
            'formatter': 'standard',
            'encoding': 'utf-8',
        },

        'collect': {
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自动切
            'filename': os.path.join(BASE_LOG_DIR, "xxx_collect.log"),
            'maxBytes': 1024 * 1024 * 50,  # 日志大小 50M
            'backupCount': 5,
            'formatter': 'collect',
            'encoding': "utf-8"
        }
    },
    # logger实例
    'loggers': {
       # 默认的logger应用如下配置
        '': {
            'handlers': ['default', 'console', 'error'],  # 上线之后可以把'console'移除
            'level': 'DEBUG',
            'propagate': True,
        },
        # 名为 'collect'的logger还单独处理
        'collect': {
            'handlers': ['console', 'collect'],
            'level': 'INFO',
        }
    },
}

INTERNAL_IPS = ['127.0.0.1', ]

就是验证登陆和注册forms表单数据的合法性

from django import forms
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
from myapp import models

#登陆的用户名
class LoginForm(forms.Form):
    username=forms.CharField(
        label='用户名',
        min_length=3,
        max_length=12,
        error_messages={
            'required':'用户名不能为空',
            'min_length':'用户名最短3位',
            'max_length':'用户名最长12位',
        },
        widget=forms.widgets.TextInput(
            attrs={'class':'form-control '}
        )
    )

    password=forms.CharField(
        label='密码',
        min_length=4,
        max_length=12,
        error_messages={
            'required':'密码不能为空',
            'min_length':'密码最短3位',
            'max_length':'密码最长12位',
        },
        widget=forms.widgets.PasswordInput(
            attrs={'class':'form-control'}
        )
    )

#注册的form
class RegForm(forms.Form):
    username=forms.CharField(
        label='用户名',
        min_length=3,
        max_length=12,
        error_messages={
            'required':'用户名不能为空!',
            'min_length':'用户名最短3位',
            'max_length':'用户名最长12位',
        },
        widget=forms.widgets.TextInput(
            attrs={'class':'form-control'}
        ),
    )
    password=forms.CharField(
        label='密码',
        min_length=4,
        max_length=12,
        error_messages={
            'required':'密码不能为空!',
            'min_length':'密码最短4位',
            'max_length':'密码最长12位',
        },
        widget=forms.widgets.PasswordInput(
            attrs={'class':'form-control'}
        )

    )


    re_password = forms.CharField(
        label="确认密码",
        min_length=4,
        max_length=12,
        error_messages={
            "required": "确认密码不能为空!",
            "min_length": "密码最短4位",
            "max_length": "密码最长12位"
        },
        widget=forms.widgets.PasswordInput(attrs={"class": "form-control"})
    )

    phone = forms.CharField(
        label="手机",
        min_length=11,
        max_length=11,
        validators=[
            RegexValidator(r'^\d{11}$', "手机号必须是数字"),
            RegexValidator(r'^1[356789][0-9]{9}$', "手机号码格式不正确")
        ],
        error_messages={
            "required": "手机不能为空!",
            "min_length": "手机号码11位",
            "max_length": "手机号码11位"
        },
        widget=forms.widgets.TextInput(attrs={"class": "form-control"})
    )


    # 局部钩子
    def clean_username(self):
        value = self.cleaned_data.get("username", "")
        if "xxx" in value:
            raise ValidationError("不符合社会主义核心价值观!")
        elif models.UserInfo.objects.filter(username=value):
            raise ValidationError("用户名已存在!")
        else:
            return value


    # 全局钩子
    def clean(self):
        pwd = self.cleaned_data.get("password", "")
        re_pwd = self.cleaned_data.get("re_password", "")

        if re_pwd and pwd == re_pwd:
            return self.cleaned_data
        else:
            err_msg = "两次输入的密码不一致"
            self.add_error("re_password", err_msg)
            raise ValidationError(err_msg)

把表格注册到admin中

from django.contrib import  admin
from myapp import  models

# Register your models here.

admin.site.register(models.Article)
admin.site.register(models.UserInfo)
admin.site.register(models.ArticleDetail)
admin.site.register(models.Blog)
admin.site.register(models.Category)
admin.site.register(models.Tag)
admin.site.register(models.Comment)
admin.site.register(models.ArticleUpDown)
admin.site.register(models.Article2Tag)

 文件的结构:

我们先从注册功能开始说起:

1.html

  form_obj.字段    ------》生产html代码

2.views.py

  form_obj=forms.RegForm(request.POST)

  form_obj.is_valid() ------->返回布尔值

    1.如果返回Trun,就能拿到校验后的数据

        form_obj.cleaned_data

3.看源码分析得到钩子函数

  1.局部钩子

      def clean_字段名():

          1.return value

          2.raise ValidationError('错误信息')

  2.全局钩子

      def clean():

          1.return self.cleaned_data

          2.raise ValidationError('错误信息')

from django.shortcuts import render, HttpResponse, redirect
from myapp import forms, models
from django.contrib import auth
from django.http import JsonResponse
import logging

# 生成一个以当前文件名命名的logger实例
logger = logging.getLogger(__name__)
# 生成一个名为collect的logger实例
collect_logger = logging.getLogger("collect")

def reg(request):
    logger.info("又来了,小伙子!")
    collect_logger.info("多多来洗脚!")
    if request.method == "POST":
        ret = {"code": 0}
        form_obj = forms.RegForm(request.POST)
        logger.debug(request.FILES)
        if form_obj.is_valid():
            # 数据经过校验,没问题
            logger.debug(form_obj.cleaned_data)
            avatar_obj = request.FILES.get("avatar")
            # 创建用户
            form_obj.cleaned_data.pop("re_password", "")
            models.UserInfo.objects.create_user(
                avatar=avatar_obj,
                **form_obj.cleaned_data    #这个骚操作大家还记得?
            )
            ret["data"] = "/login/"
        else:
            # 数据没有经过校验,有问题
            ret["code"] = 1
            ret["data"] = form_obj.errors

        return JsonResponse(ret)

    form_obj = forms.RegForm()
    return render(request, "reg.html", {"form_obj": form_obj})

reg.html文件的代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>欢迎注册</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.min.css">
    <style>
        .reg-form {
            margin-top: 70px;
        }
        #show-avatar {
            width: 80px;
            height: 80px;
        }
    </style>
</head>
<body>

<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-3 reg-form">
            <form class="form-horizontal" autocomplete="off" novalidate>
                <div class="form-group">
                    <label for="{{ form_obj.username.id_for_label }}"
                           class="col-sm-2 control-label">{{ form_obj.username.label }}</label>
                    <div class="col-sm-10">
                        {{ form_obj.username }}
                        <span class="help-block"></span>
                    </div>
                </div>
                <div class="form-group">
                    <label for="{{ form_obj.password.id_for_label }}"
                           class="col-sm-2 control-label">{{ form_obj.password.label }}</label>
                    <div class="col-sm-10">
                        {{ form_obj.password }}
                        <span class="help-block"></span>
                    </div>
                </div>
                <div class="form-group">
                    <label for="{{ form_obj.re_password.id_for_label }}"
                           class="col-sm-2 control-label">{{ form_obj.re_password.label }}</label>
                    <div class="col-sm-10">
                        {{ form_obj.re_password }}
                        <span class="help-block"></span>
                    </div>
                </div>
                <div class="form-group">
                    <label for="{{ form_obj.phone.id_for_label }}"
                           class="col-sm-2 control-label">{{ form_obj.phone.label }}</label>
                    <div class="col-sm-10">
                        {{ form_obj.phone }}
                        <span class="help-block"></span>
                    </div>
                </div>

                <div class="form-group">
                    <label class="col-sm-2 control-label">头像</label>
                    <div class="col-sm-10">
                        <input accept="image/*" type="file" id="id_avatar" name="avatar" style="display: none">
                        <label for="id_avatar"><img src="/static/img/default.png" id="show-avatar"></label>      
                    </div>
                </div>

                <div class="form-group">
                    <div class="col-sm-offset-2 col-sm-10">
                        <button type="button" class="btn btn-default" id="reg-button">注册</button>
                    </div>
                </div>
            </form>
        </div>
    </div>
</div>


<script src="/static/jquery-3.3.1.min.js"></script>
<script src="/static/setupAjax.js"></script>

<script>
    // 找到注册按钮绑定点击事件
    $("#reg-button").click(function () {
        var dataObj = new FormData();
        dataObj.append("username", $("#id_username").val());
        dataObj.append("password", $("#id_password").val());
        dataObj.append("re_password", $("#id_re_password").val());
        dataObj.append("phone", $("#id_phone").val());
        dataObj.append("avatar", $("#id_avatar")[0].files[0]);
        $.ajax({
            url: "/reg/",
            type: "POST",
            processData: false,
            contentType: false,
            data: dataObj,
            success: function (data) {
                console.log(data);
                if (data.code) {
                    // 如果有报错信息,应该在页面的对应位置展示出来
                    var errMsgObj = data.data;
                    $.each(errMsgObj, function (k, v) {
                        // k: 字段名  v:报错信息的数组
                        // 根据字段名找对应的input标签,把错误信息添加到对应位置
                        $("#id_" + k).next(".help-block").text(v[0]).parent().parent().addClass("has-error");
                    })
                } else {
                    console.log(data.data);
                    location.href = data.data || "/login/"
                }

            }
        })
    });

    // 给每一个input标签绑定focus事件,移除当前的错误提示信息
    $("input.form-control").focus(function () {
        $(this).next(".help-block").text("").parent().parent().removeClass("has-error");
    });

    // 头像预览
    $("#id_avatar").change(function () {
        // 找到你选中的那个头像文件
        var fileObj = this.files[0];
        console.log(fileObj);
        // 读取文件路径
        var fileReader = new FileReader();
        fileReader.readAsDataURL(fileObj);
        // 等图片被读取完毕之后,再做后续操作
        fileReader.onload = function () {
            // 设置预览图片
            $("#show-avatar").attr("src", fileReader.result);
        };
    })

</script>
</body>
</html>

注册的效果图: