python3-开发进阶-仿博客园项目setting.py的文件的配置,admin,forms(2)
前面我们先分析了一下,做这个项目需要的几张表,今天我们从配置文件开始一步一步去解释这么的原因
首先先来看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('错误信息')
1、注册的form类
2、上传文件
1.表单上传
form标签两个属性
1.method='post'
2.enctype='multipart/form-data'
2.AJAX上传
1.创建一个FormData对象,使用对象的append()方法填充数据
2.AJAX需要设置两个参数
1.processData:false
2.contentTyoe:false
3.Django关于上传文件的几个配置
1.上传文件存在哪儿?
1.settings.py中 配置MEDIA_ROOT=os.path.join(BASE_DIR,'media')
2.我用户怎么访问我上传的文件
1.settings.py中配置 MEDIA_URL='/media/
2.在urls.py中 配置 media开头的访问请求交给谁来处理'
url(r'^media/(?P<path>.*)$', serve, {"document_root": settings.MEDIA_ROOT})
4.浏览器图片预览
1.FileReader对象
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>
注册的效果图: