django搭建BBS 表單創建&註冊 0824自我總結 文件結構 app 介面 migrations _\_inint\_\_.py admin.py apps.py bbsform.py models.py tests.py views.py avatar BBS \_\_inint\_\_.p ...
django搭建BBS-表單創建&註冊
0824自我總結
文件結構
- app 介面
- migrations
- __inint__.py
- admin.py
管理員頁面註冊表單用
- apps.py
- bbsform.py
form組件相關設置
- models.py
模型存放
- tests.py
- views.py
業務邏輯
- avatar
圖片文件存儲
- BBS
項目名稱以及路由存放
- __inint__.py
- settings.py
- urls.py
- wsgi.py
- static
- bootstrap-3.3.7-dist
bootstrap文件網上下載的
- jquery-3.4.1.min.js
jq文件
- bootstrap-3.3.7-dist
- templates
頁面文件存放
一.django相關設置
settings.py
"""
Django settings for BBS project.
Generated by 'django-admin startproject' using Django 1.11.22.
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 = 's0x+v@gqeoxs4ruj58cq5&*5#7on_h$n4-$hwb3cr&h(@qcmoc'
# 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',
'app.apps.AppConfig',
]
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.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.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'),
}
}
# 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'),
)
#因為我創建模型的時候用到了user的類
AUTH_USER_MODEL='app.Userinfo'
二.模型的創建&管理頁面註冊
models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
nid = models.AutoField(primary_key=True)
# 頭像:FileField文件(varchar類型),default:預設值,upload_to上傳的路徑
avatar = models.FileField(upload_to='avatar/', default='avatar/default.png')
#跟blog表一對一
#OneToOneField本質就是ForeignKey,只不過有個唯一性約束
blog = models.OneToOneField(to='Blog', to_field='nid',null=True)
# blog = models.ForeignKey(to='Blog', to_field='nid',null=True,unique=True)
class Meta:
# db_table='xxxx'
# 在admin中顯示的表名
verbose_name='用戶表'
#去掉 用戶表 後的s
verbose_name_plural = verbose_name
class Blog(models.Model):
nid = models.AutoField(primary_key=True)
#站點名稱
title = models.CharField(max_length=64)
#站點副標題
site_name = models.CharField(max_length=32)
#不同人不同主題
theme = models.CharField(max_length=64)
def __str__(self):
return self.site_name
#分類表
class Category(models.Model):
nid = models.AutoField(primary_key=True)
#分類名稱
title = models.CharField(max_length=64)
#跟博客是一對多的關係,關聯欄位寫在多的一方
#to 是跟哪個表關聯 to_field跟表中的哪個欄位做關聯, null=True 表示可以為空
blog = models.ForeignKey(to='Blog', to_field='nid', null=True)
def __str__(self):
return self.title
class Tag(models.Model):
nid = models.AutoField(primary_key=True)
#標簽名字
title = models.CharField(max_length=64)
# 跟博客是一對多的關係,關聯欄位寫在多的一方
blog = models.ForeignKey(to='Blog', to_field='nid', null=True)
def __str__(self):
return self.title
class Article(models.Model):
nid = models.AutoField(primary_key=True)
#verbose_name在admin中顯示該欄位的中文
title = models.CharField(max_length=64,verbose_name='文章標題')
#文章摘要
desc = models.CharField(max_length=255)
#文章內容 大文本
content = models.TextField()
#DateTimeField 年月日時分秒(註意跟datafield的區別)
#auto_now_add=True:插入數據會存入當前時間
#auto_now=True 修改數據會存入當前時間
create_time = models.DateTimeField(auto_now_add=True)
commit_num=models.IntegerField(default=0)
up_num=models.IntegerField(default=0)
down_num=models.IntegerField(default=0)
#一對多的關係
blog = models.ForeignKey(to='Blog', to_field='nid', null=True)
# 一對多的關係
category = models.ForeignKey(to='Category', to_field='nid', null=True)
#多對多關係 through_fields 不能寫反了:
tag = models.ManyToManyField(to='Tag', through='ArticleTOTag', through_fields=('article', 'tag'))
def __str__(self):
return self.title+'----'+self.blog.userinfo.username
class ArticleTOTag(models.Model):
nid = models.AutoField(primary_key=True)
article = models.ForeignKey(to='Article', to_field='nid')
tag = models.ForeignKey(to='Tag', to_field='nid')
class Commit(models.Model):
#誰對那篇文章評論了什麼內容
nid = models.AutoField(primary_key=True)
user = models.ForeignKey(to='UserInfo', to_field='nid')
article = models.ForeignKey(to='Article', to_field='nid')
content = models.CharField(max_length=255)
#評論時間
create_time = models.DateTimeField(auto_now_add=True)
#自關聯()
parent = models.ForeignKey(to='self', to_field='nid',null=True,blank=True)
class UpAndDown(models.Model):
#誰對那篇文章點贊或點踩
nid = models.AutoField(primary_key=True)
user = models.ForeignKey(to='UserInfo', to_field='nid')
article = models.ForeignKey(to='Article', to_field='nid')
#布爾類型,本質也還是0和1
is_up = models.BooleanField()
class Meta:
#聯合唯一,一個用戶只能給一片文章點贊或點踩
unique_together = (('user', 'article'),)
admin.py
from django.contrib import admin
# Register your models here.
#先導入模型
from app import models
#註冊表
admin.site.register(models.UserInfo)
admin.site.register(models.Blog)
admin.site.register(models.Category)
admin.site.register(models.Tag)
admin.site.register(models.Article)
admin.site.register(models.ArticleTOTag)
admin.site.register(models.Commit)
admin.site.register(models.UpAndDown)
三.路由
urls.py
from django.conf.urls import url
from django.contrib import admin
#主路由導入視圖內函數
from app import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^register/', views.register),
]
四.form組件
from django import forms
from django.forms import widgets
from django.core.exceptions import ValidationError
from app import models
#寫一個類,繼承Form 沒有頭像校驗的欄位
class Register(forms.Form):
username=forms.CharField(max_length=18,min_length=3,label="用戶名",
error_messages={'max_length':'太長了',
'min_length':'太短了',
'required':'不能為空'},
widget=widgets.TextInput(attrs={'class':'form-control'}),
)
password=forms.CharField(max_length=18,min_length=3,label="密碼",
error_messages={'max_length':'太長了',
'min_length':'太短了',
'required':'不能為空'},
widget=widgets.PasswordInput(attrs={'class':'form-control'}),
)
re_pwd=forms.CharField(max_length=18,min_length=3,label="確認密碼",
error_messages={'max_length':'太長了',
'min_length':'太短了',
'required':'不能為空'},
widget=widgets.PasswordInput(attrs={'class':'form-control'}),
)
email=forms.EmailField(max_length=18,min_length=3,label="郵箱",
error_messages={'max_length':'太長了',
'min_length':'太短了',
'required':'不能為空'},
widget=widgets.EmailInput(attrs={'class':'form-control'}),
)
#局部鉤子,局部校驗
def clean_username(self):
#取出name對應的值
name=self.cleaned_data.get('username')
# if name.startswith('sb'):
# #校驗不通過,拋異常
# raise ValidationError('不能以sb開頭')
# #校驗通過,直接return name值
# else:
# return name
user=models.UserInfo.objects.filter(username=name).first()
if user:
#用戶存在,拋異常
raise ValidationError('用戶已經存在')
else:
return name
#全局鉤子,全局校驗
def clean(self):
pwd=self.cleaned_data.get('password')
r_pwd=self.cleaned_data.get('re_pwd')
if pwd==r_pwd:
#校驗通過,返回清洗後的數據
return self.cleaned_data
else:
#校驗不通過,拋異常
raise ValidationError('兩次密碼不一致')
五.業務邏輯&html頁面
1.業務邏輯
views.py
from django.shortcuts import render,HttpResponse,redirect
from django.http import JsonResponse
#Image導入
#ImageDraw在圖片上寫字
#ImageFont 寫字的格式
from PIL import Image,ImageDraw,ImageFont
import random
# 相當於把文件以byte格式存到記憶體中
from io import BytesIO
from django.contrib import auth
from app.bbsforms import Register
from app import models
from django.db.models import Count
from django.db.models.functions import TruncMonth
from django.db.models import F
# Create your views here.
def register(request):
if request.method=='GET':
form=Register()
return render(request,'register.html',{'form':form})
elif request.is_ajax():
response={'code':100,'msg':None}
form = Register(request.POST)
if form.is_valid():
#校驗通過的數據
clean_data=form.cleaned_data
#把re_pwd剔除
clean_data.pop('re_pwd')
#取出頭像
avatar=request.FILES.get('avatar')
if avatar:
#因為用的是FileField,只需要把文件對象賦值給avatar欄位,自動做保存
clean_data['avatar']=avatar
user=models.UserInfo.objects.create_user(**clean_data)
if user:
response['msg'] = '創建成功'
else:
response['code'] = 103
# 把校驗不通過的數據返回
response['msg'] = '創建失敗'
else:
response['code']=101
#把校驗不通過的數據返回
response['msg']=form.errors
print(type(form.errors))
return JsonResponse(response,safe=False)
2.網頁
常見的幾條函數的封裝
jq.html
<script src="/static/jquery-3.4.1.min.js"></script>
bootstrap.html
<link rel="stylesheet" href='/static/bootstrap-3.3.7-dist/css/bootstrap.min.css'>
<script src="/static/bootstrap-3.3.7-dist/js/bootstrap.min.js"></script>
主視圖
register.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>註冊</title>
{% include 'bootstrap.html' %}
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h1>註冊</h1>
<form id="my_form">
{% csrf_token %}
{% for foo in form %}
<div class="form-group">
{#foo.auto_id 就是foo生成的input的id#}
<label for="{{ foo.auto_id }}">{{ foo.label }}</label>
{{ foo }} <span style="color: red" class="error pull-right"></span>
</div>
{% endfor %}
<div class="form-group">
<label for="id_file">頭像
<img src="/static/img/default.png" width="80" height="80" style="margin-left: 20px" id="id_img">
</label>
<input type="file" name="file" id="id_file" style="display: none">
</div>
<input type="button" class="btn btn-success" value="提交" id="id_submit">
</form>
</div>
</div>
</div>
</body>
{% include 'jq.html' %}
<script>
//當該控制項發生變化,響應該事件
$("#id_file").change(function () {
//alert(1)
//取到文件對象
var file = $("#id_file")[0].files[0]
//放到img控制項上,藉助於filereader 中間的東西,文件閱讀器
//生成一個文件閱讀器對象賦值給filereader
var filereader = new FileReader()
//把文件讀到filereader對象中
//讀文件需要時間,需要文件讀完再去操作img
filereader.readAsDataURL(file)
filereader.onload = function () {
$("#id_img").attr('src', filereader.result)
}
})
$("#id_submit").click(function () {
//ajax 上傳文件
var formdata = new FormData()
//一個一個往裡添加,稍微複雜,用簡便方法
// formdata.append('name',$("#id_name").val())
// formdata.append('pwd',$("#id_pwd").val())
//簡便方法
//form 對象的serializeArray,它會把form中的數據包裝到一個對象中(不包含文件)
var my_form_data = $("#my_form").serializeArray()
//console.log(typeof my_form_data)
//console.log(my_form_data)
//jq的迴圈,傳兩個參數,第一個是要迴圈的對象,第二個參數是一個匿名函數
$.each(my_form_data, function (k, v) {
{#console.log(k)#}
{#console.log(v)#}
formdata.append(v.name, v.value)
})
formdata.append('avatar', $("#id_file")[0].files[0])
$.ajax({
url: '/register/',
type: 'post',
processData: false, //告訴jQuery不要去處理髮送的數據
contentType: false,// 告訴jQuery不要去設置Content-Type請求頭
data: formdata,
success: function (data) {
//console.log(data)
if(data.code==100){
location.href='/login/'
}else if(data.code==101){
$.each(data.msg,function (k,v) {
console.log(k)
console.log(v)
$("#id_"+k).next().html(v[0])
if(k=='__all__'){
$("#id_re_pwd").next().html(v[0])
}
})
}
//定時器
setTimeout(function () {
$(".error").html("")
},3000)
}
})
})
</script>
</html>