1、註冊相關的知識點
一、Form組件css
咱們通常寫Form的時候都是把它寫在views視圖裏面,那麼他和咱們的視圖函數也不影響,咱們能夠吧它單另拿出來,在應用下面建一個forms.py的文件來存放html
二、局部鉤子函數mysql
def clean_username(self):
username = self.cleaned_data.get("username")
valid = models.UserInfo.objects.filter(username = username).first()
if valid:
raise ValidationError("用戶名已存在")
return username
三、全局鉤子函數jquery
#自定義全局鉤子:驗證兩次密碼是否一致
def clean(self):
if self.cleaned_data.get("password") == self.cleaned_data.get("password_again"):
return self.cleaned_data
else:
raise ValidationError("兩次密碼不一致")
四、 jQuery的屬性操做相關的ajax
attr:
一個參數是獲取屬性的值,兩個參數是設置屬性值
removeAttr(屬性名):
刪除屬性值
prop:
適應於屬性的返回值是布爾類型的(單選,反選,取消的例子)
removePorp:
刪除屬性的值
五、循環的兩種方式:sql
$.each(數組/對象,function(i,v){})
$("div").each(function(i,v){})
六、css中的三種隱藏:數據庫
1、display:none 隱藏全部內容
2、visibility:hidden 隱藏內容
3、overflow:hidden 隱藏溢出內容
三者都是用來隱藏的:
區別在於:
visibility雖然隱藏了,可是被隱藏的內容依然佔據這空間,這段隱藏了的內容卻保留空間的位置會在網頁中顯示空白
而display:隱藏了不佔用空間
咱們在註冊的時候不用display:none,否則選擇文件的那個功能就沒有了,咱們能夠吧透明度
七、提交二進制數據用FormDatadjango
var formData=new FormData();
formData.append("username",$("#id_username").val());
formData.append("email",$("#id_email").val());
formData.append("tel",$("#id_tel").val());
formData.append("password",$("#id_password").val());
formData.append("password_again",$("#id_password_again").val());
formData.append("avatar_img",$("#avatar")[0].files[0]);
記得要加上bootstrap
contentType:false
processData:false
八、能夠用下面的方法判斷是什麼請求數組
if request.ajax(): #若是ajax請求
if request,method=="POST": #若是是POST請求
九、上傳文件有一個固定的配置參數media,和static類似 但又不一樣
步驟以下:
- 首先在settings中配置:
# ============media配置===============
MEDIA_URL="/media/" #別名
MEDIA_ROOT=os.path.join(BASE_DIR,"app01","media","uploads") #具體路徑
- 在url中配置
url(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
用處:
用處一:
----- avatar = models.FileField(verbose_name='頭像', upload_to='avatar', default="/avatar/default.png")
會把接收的文件放在media指代的路徑與upload_to的拼接:BASE_DIR+blog+media+uploads+avatar/a.png
avatar字段在數據庫中保存的是:avatar/a.png
用處二:
------ <img src="/media/avatar/a.png">
若是上傳成功會把圖片自動保存在這裏
十、頭像圖片預覽
//頭像預覽
$(".avatar_file").change(function () {
var ele_file = $(this)[0].files[0]; //當前選中的文件
var reader = new FileReader();
reader.readAsDataURL(ele_file); //對應找到打開的url
reader.onload=function () {
{# 方式一#}
$(".avatar_img").attr("src",this.result) ; //this.result是上面找到的url
{# 方式二#}
{# $(".avatar_img")[0].src=this.result; //設置圖片屬性#}
}
})
十一、form自動生成的錯誤信息
當你定義了全局鉤子的時候,並且正好出現你的那個全局鉤子函數中的錯(好比兩次密碼輸入不一致),這樣你打印錯誤信息的時候
會有一個__all__對象,這個就是你設置的全局鉤子生成的。
因此還要單獨判斷一下,如今全局鉤子只有一個,你能夠這樣判斷,可是,當全局鉤子多的時候就得一個一個分開來判斷
if (i=="__all__"){
$("#id_password_again").after($span)
}
from django.conf.urls import url,include
from django.contrib import admin
from django.conf import settings
from django.views.static import serve
from blog import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^login/', views.login),
url(r'^index/', views.index),
url(r'^v_code/', views.v_code),
url(r'^login2/', views.login2),
url(r'^pcgetcaptcha/', views.pcgetcaptcha),
# 以上是bbs day75的內容
url(r'^reg/$', views.reg),
url(r'^media/(?P<path>.*)$', serve, {"document_root":settings.MEDIA_ROOT})
]
if settings.DEBUG:
import debug_toolbar
urlpatterns = [
url(r'^__debug__', include(debug_toolbar.urls))
] + urlpatterns
def reg(request):
logger.info("又來了,小夥子")
collect_logger.info("小夥子又來洗.腳.了")
if request.method == "POST":
ret = {"ret":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})
from django import forms
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
from blog import models
# 登錄Form
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 "金/瓶/梅" 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)
<!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 () {
let 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.data) {
// 若是有報錯信息, 應該在頁面的對應的位置展現出來
let errMsgObj = data.data;
$.each(errMsgObj, function(k,v){
$("#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() {
// 找到你選中的那個頭像文件
let fileObj = this.files[0];
console.log(fileObj);
// 讀取文件路徑
let fileReader = new FileReader();
fileReader.readAsDataURL((fileObj));
// 等圖片被讀取完畢後,在進行操做
fileReader.onload = function() {
// 設置預覽圖片
$("#show-avatar").attr("src",fileReader.result);
};
})
</script>
</body>
</html>
from django.db import models
# Create your models here.
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
'''
用戶信息表
'''
nid = models.AutoField(primary_key=True)
phone = models.CharField(max_length=11, null=True, unique=True)
avatar = models.FileField(upload_to="avatars/", default="avatars/default.png")
blog = models.OneToOneField(to="Blog", to_field="nid", null=True)
def __str__(self):
return self.username
class Meta:
verbose_name = "用戶信息"
verbose_name_plural = verbose_name
class Blog(models.Model):
'''
博客信息
'''
nid = models.AutoField(primary_key=True)
title = models.CharField(max_length=64) # 我的博客主題
theme = models.CharField(max_length=32) # 每一個博客主題
def __str__(self):
return self.title
class Meta:
verbose_name = "博客"
verbose_name_plural = verbose_name
class Category(models.Model):
'''
我的文章分類
'''
nid = models.AutoField(primary_key=True)
title = models.CharField(max_length=32) # 分類主題
blog = models.ForeignKey(to="Blog", to_field="nid") # 外鍵關聯博客,一個博客能夠有多個分類
def __str__(self):
return "{}-{}".format(self.blog.title, self.title)
class Meta:
verbose_name = "文章分類"
verbose_name_plural = verbose_name
class Tag(models.Model):
'''
標貼
'''
nid = models.AutoField(primary_key=True)
title = models.CharField(max_length=32) # 標籤
blog = models.ForeignKey(to="Blog", to_field="nid") # 所屬的博客
def __str__(self):
return self.title
class Meta:
verbose_name = "標籤"
verbose_name_plural = verbose_name
class Article(models.Model):
'''
文章
'''
nid = models.AutoField(primary_key=True)
title = models.CharField(max_length=64) # 文章標題
desc = models.CharField(max_length=255) # 文章描述
create_time = models.DateTimeField(auto_now_add=True) # 建立時間
category = models.ForeignKey(to="Category", to_field="nid", null=True)
user = models.ForeignKey(to="UserInfo", to_field="nid")
tag = models.ManyToManyField(
to="Tag",
through="Article2Tag",
through_fields=("article", "tag")
)
def __str__(self):
return self.title
class Meta:
verbose_name = "文章"
verbose_name_plural = verbose_name
class ArticleDetail(models.Model):
'''
文章詳情表格
'''
nid = models.AutoField(primary_key=True)
content = models.TextField()
article = models.OneToOneField(to="Article", to_field="nid")
class Meta:
verbose_name = "文章詳情"
verbose_name_plural = verbose_name
class Article2Tag(models.Model):
'''
文章和標籤多對多的關係
'''
nid = models.AutoField(primary_key=True)
article = models.ForeignKey(to="Article", to_field="nid")
tag = models.ForeignKey(to="Tag", to_field="nid")
def __str__(self):
return "{}-{}".format(self.article, self.tag)
class Meta:
unique_together = (("article", "tag"),)
verbose_name = "文章-標籤"
verbose_name_plural = verbose_name
class ArticleUpDown(models.Model):
'''
點贊表
'''
nid = models.AutoField(primary_key=True)
user = models.ForeignKey(to="UserInfo", null=True)
article = models.ForeignKey(to="Article", null=True)
is_up = models.BooleanField(default=True)
class Meta:
unique_together = (("article","user"))
verbose_name = "點贊"
verbose_name_plural = verbose_name
class Comment(models.Model):
'''
評論表
'''
nid = models.AutoField(primary_key=True)
article = models.ForeignKey(to="Article", to_field="nid")
user = models.ForeignKey(to="UserInfo", to_field="nid")
content = models.CharField(max_length=255)
create_time = models.DateTimeField(auto_now_add=True)
parent_comment = models.ForeignKey("self", null=True)
def __str__(self):
return self.content
class Meta:
verbose_name = "評論"
verbose_name_plural = verbose_name
"""
Django settings for bbs_d76 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 = 'jng4g!)5pve-0s7)6lv(nty7$jn^!p7ov+$-*hdn@!!kkg7q#e'
# 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',
'blog.apps.BlogConfig',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'bbs_d76.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 = 'bbs_d76.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'bbs_d76',
'USER': 'root',
'PASSWORD': '1234567890',
'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 = True
# 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 = "blog.UserInfo"
# 用戶上傳的文件配置項
MEDIA_URL = "/media/"
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
# 日誌配置
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',]