tornado项目搭建_Day71-73 BBS项目(1)
目錄
- 1、項(xiàng)目開發(fā)流程
- 2、bbs表設(shè)計(jì)
- 3、數(shù)據(jù)庫(kù)表創(chuàng)建及同步
- 4、注冊(cè)功能
- forms組件
- 注冊(cè)頁(yè)面
- 5、登錄功能
- 實(shí)現(xiàn)圖片驗(yàn)證碼
- 6、搭建BBS首頁(yè)導(dǎo)航條
- 修改密碼
- 退出登錄
- 7、admin后臺(tái)管理
- 8、首頁(yè)文章展示
- media配置及用戶頭像展示
- 9、圖片防盜鏈
- 10、個(gè)人站點(diǎn)頁(yè)面搭建
1、項(xiàng)目開發(fā)流程
架構(gòu)師+產(chǎn)品經(jīng)理+開發(fā)組組長(zhǎng),在跟客戶溝通交流中引導(dǎo)客戶往我們之前想好的方案上靠。形成一個(gè)初步的方案
架構(gòu)師進(jìn)行項(xiàng)目設(shè)計(jì):
編程語(yǔ)言的選擇、
框架的選擇(flask、Django、Tornado)、
數(shù)據(jù)庫(kù)的選擇(主庫(kù)mysql postgresql、緩存數(shù)據(jù)庫(kù)redis MongoDB memcache)、
功能劃分(將項(xiàng)目劃分成幾個(gè)模塊)、
組長(zhǎng)開會(huì)(分發(fā)任務(wù))、
項(xiàng)目報(bào)價(jià)
組長(zhǎng)找組員開會(huì),安排各自功能模塊,我們就是在架構(gòu)師設(shè)計(jì)好的框架中填寫自己的代碼
測(cè)試部門對(duì)代碼進(jìn)行壓力測(cè)試
交給運(yùn)維人員
2、BBS表設(shè)計(jì)(https://www.cnblogs.com/)
一個(gè)項(xiàng)目中最重要的不是業(yè)務(wù)邏輯的書寫而是前期表的設(shè)計(jì),只要將表設(shè)計(jì)好了,后續(xù)功能書寫才會(huì)一帆風(fēng)順。
繼承AbstractUser
擴(kuò)展字段:phoneavatarcreate_time
外鍵字段:
一對(duì)一“個(gè)人站點(diǎn)表”
site_namesite_title|site_theme
name
外鍵字段:
一對(duì)多“個(gè)人站點(diǎn)表”
name
外鍵字段:
一對(duì)多“個(gè)人站點(diǎn)表”
titledesc文章簡(jiǎn)介content文章內(nèi)容create_time發(fā)布時(shí)間
數(shù)據(jù)庫(kù)字段設(shè)計(jì)優(yōu)化(雖然點(diǎn)贊數(shù)點(diǎn)踩數(shù)評(píng)論數(shù)可以從其他表跨表查詢得到,但是頻繁跨表效率低)
up_num點(diǎn)贊數(shù)down_num點(diǎn)踩數(shù)comment_num評(píng)論數(shù)(在點(diǎn)贊點(diǎn)踩表評(píng)論表增加數(shù)據(jù)時(shí),給這三個(gè)普通字段數(shù)值同步更新+1)
外鍵字段:
一對(duì)多“個(gè)人站點(diǎn)表”
多對(duì)多“文章標(biāo)簽表“
一對(duì)多”文章分類表”
user ForeignKey(to='User')
article ForeignKey(to='Article')
is_up BooleanField()
user ForeignKey(to='User')
article ForeignKey(to='Article')
content CharField()
comment_time DateField()
# 自關(guān)聯(lián)
# parent ForeignKey(to='comment',null=True)
# ORM專門提供的自關(guān)聯(lián)的寫法
parent ForeignKey(to='self',null=True)
根評(píng)論:直接評(píng)論當(dāng)前發(fā)布的內(nèi)容
子評(píng)論:評(píng)論別人的評(píng)論
(根評(píng)論與子評(píng)論是一對(duì)多的關(guān)系)
3、數(shù)據(jù)庫(kù)表創(chuàng)建及同步
配置MySQL數(shù)據(jù)庫(kù)參數(shù)
DATABASES = {'default': {'ENGINE': 'django.db.backends.mysql','NAME': 'bbs','USER':'root','PASSWORD':'dingding','PORT':'3306','HOST':'127.0.0.1','CHARSET':'utf8'} }init文件中進(jìn)行代碼聲明:
import pymysql pymysql.install_as_MySQLdb()models.py文件
from django.db import models# Create your models here. # 創(chuàng)建表:先寫普通字段,再寫外鍵字段from django.contrib.auth.models import AbstractUserclass UserInfo(AbstractUser):phone = models.BigIntegerField(verbose_name='手機(jī)號(hào)',null=True)# 頭像# 給avatar字段傳文件對(duì)象,該文件會(huì)自動(dòng)存儲(chǔ)到avatar文件夾下,設(shè)置一個(gè)默認(rèn)頭像avatar = models.FileField(upload_to='avatar/',default='avatar/default.jpg',verbose_name='用戶頭像')create_time = models.DateField(auto_now_add = True)# 一對(duì)一關(guān)系blog = models.OneToOneField(to='Blog',null=True)class Blog(models.Model):site_name = models.CharField(max_length=32,verbose_name='站點(diǎn)名稱')site_title = models.CharField(max_length=32,verbose_name='站點(diǎn)標(biāo)題')# site_theme站點(diǎn)樣式中存儲(chǔ)css/js的文件路徑site_theme = models.CharField(max_length=64,verbose_name='站點(diǎn)樣式')class Category(models.Model):name = models.CharField(max_length=32,verbose_name='文章分類')# 外鍵字段blog = models.ForeignKey(to='Blog',null=True)class Tag(models.Model):name = models.CharField(max_length=32,verbose_name='文章標(biāo)簽')blog = models.ForeignKey(to='Blog',null=True)def __str__(self):return self.nameclass Article(models.Model):title = models.CharField(max_length=64,verbose_name='文章標(biāo)題')desc = models.CharField(max_length=255,verbose_name='文章簡(jiǎn)介')# 文章內(nèi)容一般用TextFieldcontent = models.TextField(verbose_name='文章內(nèi)容')create_time = models.DateField(auto_now_add=True)# 數(shù)據(jù)庫(kù)字段優(yōu)化設(shè)計(jì)up_num = models.BigIntegerField(default=0,verbose_name='點(diǎn)贊數(shù)')down_num = models.BigIntegerField(default=0,verbose_name='點(diǎn)踩數(shù)')comment_num = models.BigIntegerField(default=0,verbose_name='評(píng)論數(shù)')# 外鍵字段blog = models.ForeignKey(to='Blog', null=True)category = models.ForeignKey(to='Category', null=True)tags = models.ManyToManyField(to='Tag',through='Article2Tag',through_fields=('article','tag'))class Article2Tag(models.Model):article = models.ForeignKey(to='Article')tag = models.ForeignKey(to='Tag')class UpAndDown(models.Model):user = models.ForeignKey(to = 'UserInfo')article = models.ForeignKey(to = 'Article')is_up = models.BooleanField(verbose_name='是否點(diǎn)贊')class Comment(models.Model):user = models.ForeignKey(to = 'UserInfo')article = models.ForeignKey(to = 'Article')content = models.CharField(max_length=255,verbose_name='評(píng)論內(nèi)容')comment_time = models.DateTimeField(verbose_name='評(píng)論時(shí)間',auto_now_add=True)# 自關(guān)聯(lián)parent = models.ForeignKey(to='self',null=True)到settings文件中添加代碼:AUTH_USERMODEL = 'app01.UserInfo'
告訴Django用UserInfo替代auth_user表。
然后執(zhí)行數(shù)據(jù)庫(kù)遷移命令。
4、注冊(cè)功能
forms組件我們之前是直接在views.py中書寫的forms組件代碼,但是為了解耦合,應(yīng)該將所有的forms組件代碼單獨(dú)寫到一個(gè)地方。
如果你的項(xiàng)目自始至終只用到一個(gè)forms組件,那么建一個(gè)py文件即可。但是如果你的項(xiàng)目需要使用很多個(gè)forms組件,那么可以創(chuàng)建一個(gè)文件夾,在文件夾內(nèi)根據(jù)forms組件功能的不同創(chuàng)建不同的py文件(userform.pyorderform.py).
app01文件夾下創(chuàng)建myforms.py文件:
from django import forms from app01 import models# 書寫針對(duì)用戶表的forms組件代碼 class MyForm(forms.Form):username = forms.CharField(label='用戶名', min_length=3, max_length=8,error_messages={'required': '用戶名不能為空','min_length': '用戶名最少3位數(shù)','max_length': '用戶名最多8位數(shù)',},# 讓標(biāo)簽有bootstrap的樣式widget=forms.widgets.TextInput(attrs={'class': 'form-control'}))password = forms.CharField(min_length=3, max_length=8, label='密碼',error_messages={'required': '密碼不能為空','min_length': '密碼最少3位','max_length': '密碼最多8位'},widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}))confirm_password = forms.CharField(min_length=3, max_length=8, label='確認(rèn)密碼',error_messages={'required': '密碼不能為空','min_length': '密碼最少3位','max_length': '密碼最多8位'},widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}))email = forms.EmailField(label='郵箱',error_messages={'invalid': '郵箱格式不正確','required': '郵箱不能為空'},widget=forms.widgets.EmailInput(attrs={'class': 'form-control'}))# 鉤子函數(shù)# 局部鉤子:校驗(yàn)用戶名是否存在def clean_username(self):username = self.cleaned_data.get('username')# 去數(shù)據(jù)庫(kù)中校驗(yàn)is_exist = models.UserInfo.objects.filter(username=username)if is_exist:# 提示信息self.add_error('username', '用戶名已存在')return username# 全局鉤子:校驗(yàn)兩次密碼是否一致def clean(self):password = self.cleaned_data.get('password')confirm_password = self.cleaned_data.get('confirm_password')if not password == confirm_password:self.add_error('confirm_password', '兩次密碼不一致')return self.cleaned_data注冊(cè)頁(yè)面views.py文件:
from django.shortcuts import render,HttpResponse from app01.myforms import MyRegForm from app01 import models from django.http import JsonResponse # Create your views here.def register(request):form_obj = MyRegForm()if request.method == 'POST':back_dic = {'code':1000,'msg':''}form_obj = MyRegForm(request.POST)if form_obj.is_valid():# print(form_obj.cleaned_data)# 得到四個(gè)鍵值對(duì)# {'username': 'jack', 'password': '123', 'confirm_password': '123', 'email': '123@qq.com'}# 將校驗(yàn)通過(guò)的數(shù)據(jù)字典賦值個(gè)一個(gè)變量clean_data = form_obj.cleaned_data# 將字典中的confirm_password鍵值對(duì)刪除clean_data.pop('confirm_password')# 獲取用戶頭像file_obj = request.FILES.get('avatar')# 針對(duì)用戶頭像一定要判斷是否傳值,不能直接添加到字典中去if file_obj:clean_data['avatar'] = file_obj# 直接操作數(shù)據(jù)庫(kù)保存數(shù)據(jù)models.UserInfo.objects.create_user(**clean_data)# 注冊(cè)成功之后要跳轉(zhuǎn)到一個(gè)登錄界面,所以給這個(gè)前后端交互的字典加上一個(gè)urlback_dic['url'] = '/login/'else:back_dic['code'] = 2000back_dic['msg'] = form_obj.errorsreturn JsonResponse(back_dic) # 將這個(gè)字典返回給回調(diào)函數(shù)return render(request,'register.html',locals())register.html頁(yè)面
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title><script src="jQuery.js"></script><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script><link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"><script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script></head> <body> <div class="container-fluid"><div class="row"><div class="col-md-8 col-md-offset-2"><h1 class="text-center">注冊(cè)頁(yè)面</h1>{# 里我們不用form表單提交數(shù)據(jù),只是單純用一下form標(biāo)簽,#}{# 之后在ajax請(qǐng)求中會(huì)用到$('#myform').serializeArray()這個(gè)方法,能夠獲取到form標(biāo)簽內(nèi)所有用戶普通鍵值對(duì)數(shù)據(jù) :[{},{},{},{}]#}<form id="myform">{% csrf_token %}{% for form in form_obj %}<div class="form-group">{# form.auto_id找到標(biāo)簽所對(duì)應(yīng)的id值#}<label for="{{ form.auto_id }}">{{ form.label }}:</label>{{ form }}<span style="color: red" class="pull-right"></span></div>{% endfor %}{# 手動(dòng)渲染獲取用戶頭像的標(biāo)簽#}<div class="form-group"><label for="myfile">頭像{% load static %}<img id="myimg" src="{% static 'img/default.jpg' %}" alt="" width="80" style="margin-left: 1px">{# 只要是點(diǎn)擊label內(nèi)的內(nèi)容,都會(huì)跳轉(zhuǎn)到for指定的標(biāo)簽上 #}</label><input type="file" id="myfile" name="avatar" style="display: none"></div>{# 不要寫submit,會(huì)出發(fā)form表單的提交#}<input id="id_commit" type="button" value="注冊(cè)" class="btn btn-primary pull-right"></form></div></div> </div><script>// 實(shí)時(shí)展示用戶頭像:$('#myfile').change(function () {// 文件閱讀器對(duì)象// 1、先生成一個(gè)文件閱讀器對(duì)象let myFileReaderObj = new FileReader();// 2、獲取用戶上傳的頭像文件let fileobj = $(this)[0].files[0];// 3、將文件對(duì)象交給閱讀器對(duì)象myFileReaderObj.readAsDataURL(fileobj)// 這是一個(gè)異步操作 IO操作,所以要等待文件閱讀器讀取完畢之后再進(jìn)行文件的展示,否則頭像不顯示// 需要用到onload功能// 4、利用文件閱讀器將文件展示到前端頁(yè)面// 修改src屬性myFileReaderObj.onload = function(){$('#myimg').attr('src',myFileReaderObj.result)}}){#發(fā)送ajax請(qǐng)求,我們的數(shù)據(jù)中既包含普通鍵值對(duì),也包含文件,所以用到了FormData內(nèi)置對(duì)象#}$('#id_commit').click(function(){let formDataObj = new FormData();// 1、添加普通鍵值對(duì)$.each($('#myform').serializeArray(),function (index,obj) {formDataObj.append(obj.name,obj.value)})// 2、添加文件數(shù)據(jù)formDataObj.append('avatar',$('#myfile')[0].files[0]);// 3、發(fā)送ajax請(qǐng)求$.ajax({url:"",type:'post',data:formDataObj,// 需要指定兩個(gè)關(guān)鍵性的參數(shù)contentType:false,processData:false,success:function(args){if (args.code == 1000){// 跳轉(zhuǎn)到登錄頁(yè)面window.location.href = args.url} else{// 將對(duì)應(yīng)的錯(cuò)誤提示展示到對(duì)應(yīng)的input框后面// forms組件渲染的標(biāo)簽的ID值都是:id_字段名$.each(args.msg,function (index,obj) {let targetId = '#id_'+index;$(targetId).next().text(obj[0]).parent().addClass('has-error')})}}})})// 當(dāng)框變成紅色并且有錯(cuò)誤提示時(shí),要達(dá)到鼠標(biāo)點(diǎn)擊框則紅色框錯(cuò)誤提示都消失的效果:// 給所有的input框綁定獲取焦點(diǎn)事件$('input').focus(function () {// 將input下面的span標(biāo)簽和input外面的div標(biāo)簽修改內(nèi)容及屬性// jQuery的鏈?zhǔn)讲僮?(this).next().text('').parent().removeClass('has-error')}) </script> </body> </html>5、登陸功能
urls.py文件
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [url(r'^admin/', admin.site.urls),url(r'^register/',views.register,name='reg'),url(r'^login/',views.login,name='login'),# 圖片驗(yàn)證碼相關(guān)操作url(r'^get_code/',views.get_code,name='get_code') ]views.py文件
from django.shortcuts import render,HttpResponse from app01.myforms import MyRegForm from app01 import models from django.http import JsonResponse from django.contrib import authdef login(request):if request.method == 'POST':# 針對(duì)ajax的前后端交互,通常會(huì)定義一個(gè)字典back_dic = {'code':1000,'msg':''}username = request.POST.get('username')password = request.POST.get('password')code = request.POST.get('code')# 校驗(yàn)驗(yàn)證碼是否正確if request.session.get('code').upper() == code.upper(): # 都轉(zhuǎn)成大寫來(lái)比較(忽略大小寫)# 校驗(yàn)用戶名和密碼是否正確user_obj = auth.authenticate(request,username=username,password=password)if user_obj:# 保存用戶狀態(tài)auth.login(request,user_obj)# 校驗(yàn)成功的話,給字典增加一個(gè)url,之后再跳轉(zhuǎn)到home首頁(yè)back_dic['url'] = '/home/'else:back_dic['code'] = 2000back_dic['msg'] = '用戶名或密碼錯(cuò)誤'else:back_dic['code'] = 3000back_dic['msg'] = '驗(yàn)證碼錯(cuò)誤'return JsonResponse(back_dic)return render(request,'login.html')# 圖片相關(guān)的模塊 from PIL import Image,ImageDraw,ImageFont # Image:生成圖片 # ImageDraw:在圖片上寫字 # ImageFont:控制字體樣式 from io import BytesIO,StringIO # 內(nèi)存管理器模塊 # BytesIO:臨時(shí)幫你存儲(chǔ)數(shù)據(jù),返回時(shí)數(shù)據(jù)是二進(jìn)制 # StringIO:臨時(shí)幫你存儲(chǔ)數(shù)據(jù),返回時(shí)數(shù)據(jù)是字符串import random def get_random():return random.randint(0,255),random.randint(0,255),random.randint(0,255)def get_code(request):# 推導(dǎo)步驟1:直接獲取后端現(xiàn)成的圖片二進(jìn)制數(shù)據(jù)發(fā)送給前端,圖片局限于本地# with open(r'/Users/dingding/PycharmProjects/day72_BBS/static/img/default.jpg','rb') as f:# data = f.read()# return HttpResponse(data)# 推導(dǎo)步驟2:利用pillow模塊動(dòng)態(tài)產(chǎn)生圖片,io操作頻繁,效率低# img_obj = Image.new('RGB',(430,35),'red')# img_obj = Image.new('RGB',(430,35),get_random())# 先將圖片對(duì)象保存起來(lái)# with open('x.png','wb') as f:# img_obj.save(f,'png')# 再將圖片對(duì)象讀取出來(lái)# with open('x.png','rb') as f:# data = f.read()# return HttpResponse(data)# 推導(dǎo)步驟3:# img_obj = Image.new('RGB', (430, 35), get_random())# 生成一個(gè)io內(nèi)存管理器對(duì)象,可以看成是文件句柄# io_obj = BytesIO()# img_obj.save(io_obj,'png') # 要指定圖片的格式# 從內(nèi)存管理器中讀取二進(jìn)制的圖片數(shù)據(jù)返回給前端# return HttpResponse(io_obj.getvalue())# 最終步驟4:寫圖片驗(yàn)證碼img_obj = Image.new('RGB', (430, 35), get_random())# 產(chǎn)生一個(gè)畫筆對(duì)象img_draw = ImageDraw.Draw(img_obj)# 字體樣式(.ttf格式的文件)img_font = ImageFont.truetype('static/font/楊任東竹石體-Semibold.ttf',30) # 要加上字體大小# 接下來(lái)產(chǎn)生隨機(jī)驗(yàn)證碼(包含5位數(shù)的數(shù)字、大寫小寫字母)code = ''for i in range(5):random_upper = chr(random.randint(65,90)) # chr會(huì)將數(shù)字通過(guò)ASCII表轉(zhuǎn)成數(shù)字對(duì)應(yīng)的字母random_lower = chr(random.randint(97,122))random_int = str(random.randint(0,9)) # 轉(zhuǎn)成字符串# 從上面3個(gè)隨機(jī)選擇一個(gè)tmp = random.choice([random_upper,random_lower,random_int])# 將產(chǎn)生的隨機(jī)字符串一個(gè)個(gè)寫入到圖片上(一個(gè)個(gè)寫可以控制每個(gè)字之間的間隙,生成之后再寫就沒(méi)法控制間隙了)img_draw.text((i*60+50,0),tmp,get_random(),img_font)code += tmpprint(code)# 隨機(jī)驗(yàn)證碼在登錄的視圖函數(shù)中需要用到,要進(jìn)行校驗(yàn),所以需要存起來(lái),并且其他視圖函數(shù)也要能拿到,可以存到session中。request.session['code'] = codeio_obj = BytesIO()img_obj.save(io_obj,'png')return HttpResponse(io_obj.getvalue())login.html頁(yè)面
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title><script src="jQuery.js"></script><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script><link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"><script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>{% load static %} </head> <body> <div class="container-fluid"><div class="row"><div class="col-md-8 col-md-offset-2"><h1 class="text-center">登錄頁(yè)面</h1><div class="form-group"><label for="username">用戶名</label><input type="text" name="username" id="username" class="form-control"></div><div class="form-group"><label for="password">密碼</label><input type="password" name="password" id="password" class="form-control"></div><div class="form-group"><label for="">驗(yàn)證碼</label><div class="row"><div class="col-md-6 col-xs-6"><input type="text" name="code" id="id_code" class="form-control"></div><div class="col-md-6 col-xs-6">{# <img src="{% static 'img/default.jpg' %}" alt="" height="35" width="350" >#}{# 動(dòng)態(tài)展示驗(yàn)證碼圖片,這個(gè)頁(yè)面只要一加載出來(lái)就會(huì)朝/get_code/這個(gè)頁(yè)面發(fā)get請(qǐng)求#}{# 給這個(gè)圖片驗(yàn)證碼綁定一個(gè)點(diǎn)擊事件,使得點(diǎn)擊后圖片會(huì)刷新#}<img id="get_code" src="/get_code/" alt="" height="35" width="350"></div></div></div><input id="id_commit" type="button" class="btn btn-success pull-right" value="登陸"><span style="color: red" id="error"></span></div></div> </div> <script>$('#get_code').click(function(){// 1、先獲取標(biāo)簽之前的srclet oldVal = $(this).attr('src');$(this).attr('src',oldVal += '?')})// 點(diǎn)擊登錄按鈕發(fā)送ajax請(qǐng)求$('#id_commit').click(function(){$.ajax({url:'',type:'post',data:{'username':$('#username').val(),'password':$('#password').val(),'code':$('#id_code').val(),'csrfmiddlewaretoken':'{{ csrf_token }}'},success:function (args) {if (args.code == 1000){// 跳轉(zhuǎn)到首頁(yè)window.location.href = args.url}else{// 渲染錯(cuò)誤信息$('#error').text(args.msg)}}})}) </script> </body> </html>補(bǔ)充:img標(biāo)簽src屬性后面可以寫的內(nèi)容:
1、直接寫網(wǎng)絡(luò)圖片地址
2、url后綴(自動(dòng)朝該url發(fā)送get請(qǐng)求獲取數(shù)據(jù))
3、圖片二進(jìn)制數(shù)據(jù)
6、搭建BBS首頁(yè)導(dǎo)航條
導(dǎo)航條以及修改密碼、退出登錄功能
views.py文件
def home(request):return render(request,'home.html')@ login_required def set_password(request):# 直接判斷是不是ajax請(qǐng)求,只處理ajax請(qǐng)求if request.is_ajax():back_dic = {'code':1000,'msg':''}if request.method == 'POST':old_password = request.POST.get('old_password')new_password = request.POST.get('new_password')confirm_new_password = request.POST.get('confirm_new_password')is_right = request.user.check_password(old_password) # 自動(dòng)轉(zhuǎn)成加密密碼并校驗(yàn)if is_right:if new_password == confirm_new_password:request.user.set_password(new_password)request.user.save()back_dic['msg'] = '密碼修改成功'else:back_dic['code'] = 1001back_dic['msg'] = '兩次密碼輸入不一致'else:back_dic['code'] = 1002back_dic['msg'] = '原密碼錯(cuò)誤'return JsonResponse(back_dic)@login_required def logout(request):auth.logout(request)# 注銷后重定向到home頁(yè)面return redirect('/home/')home.html頁(yè)面
<body> <nav class="navbar navbar-inverse"><div class="container-fluid"><!-- Brand and toggle get grouped for better mobile display --><div class="navbar-header"><button type="button" class="navbar-toggle collapsed" data-toggle="collapse"data-target="#bs-example-navbar-collapse-1" aria-expanded="false"><span class="sr-only">Toggle navigation</span><span class="icon-bar"></span><span class="icon-bar"></span><span class="icon-bar"></span></button><a class="navbar-brand" href="#">BBS</a></div><!-- Collect the nav links, forms, and other content for toggling --><div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"><ul class="nav navbar-nav"><li class="active"><a href="#">博客 <span class="sr-only">(current)</span></a></li><li><a href="#">文章</a></li><li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"aria-expanded="false"> 更多<span class="caret"></span></a><ul class="dropdown-menu"><li><a href="#">Action</a></li><li><a href="#">Another action</a></li><li><a href="#">Something else here</a></li><li role="separator" class="divider"></li><li><a href="#">Separated link</a></li><li role="separator" class="divider"></li><li><a href="#">One more separated link</a></li></ul></li></ul><form class="navbar-form navbar-left"><div class="form-group"><input type="text" class="form-control" placeholder="Search"></div><button type="submit" class="btn btn-default">Submit</button></form><ul class="nav navbar-nav navbar-right">{# 根據(jù)用戶是否登錄顯示的內(nèi)容也不一樣#}{% if request.user.is_authenticated %}<li><a href="#">{{ request.user.username }}</a></li><li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"aria-expanded="false">更多<span class="caret"></span></a><ul class="dropdown-menu"><li><a href="#" data-toggle="modal" data-target=".bs-example-modal-lg">修改密碼</a></li><li><a href="#">修改頭像</a></li><li><a href="#">后臺(tái)管理</a></li><li role="separator" class="divider"></li><li><a href="{% url 'logout' %}">退出登錄</a></li></ul><!-- Large modal --><div class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog"aria-labelledby="myLargeModalLabel"><div class="modal-dialog modal-lg" role="document"><div class="modal-content"><h1 class="text-center">修改密碼</h1><div class="row"><div class="col-md-8 col-md-offset-2"><div class="form-group"><label for="">用戶名</label><input type="text" disabled value="{{ request.user.username }}"class="form-control"></div><div class="form-group"><label for="">原密碼</label><input type="text" id="id_old_password" class="form-control"></div><div class="form-group"><label for="">新密碼</label><input type="text" id="id_new_password" class="form-control"></div><div class="form-group"><label for="">確認(rèn)密碼</label><input type="text" id="id_confirm_new_password" class="form-control"></div><div class="modal-footer"><button id="id_edit" type="button" class="btn btn-primary">修改</button><button type="button" class="btn btn-default" data-dismiss="modal">取消</button><span id="password_errors" style="color: red"></span></div></div></div></div></div></div></li>{% else %}<li><a href="{% url 'reg' %}">注冊(cè)</a></li><li><a href="{% url 'login' %}">登錄</a></li>{% endif %}</ul></div><!-- /.navbar-collapse --></div><!-- /.container-fluid --> </nav><script>// 給修改按鈕綁定一個(gè)點(diǎn)擊事件$('#id_edit').click(function () {$.ajax({url: '/set_password/',type: 'post',data: {'old_password': $('#id_old_password').val(),'new_password': $('#id_new_password').val(),'confirm_new_password': $('#id_confirm_new_password').val(),'csrfmiddlewaretoken': '{{ csrf_token }}'},success: function (args) {if (args.code == 1000) {window.location.reload() // 如果修改成功可以刷新一下頁(yè)面或者跳轉(zhuǎn)到登錄頁(yè)面讓用戶重新登錄} else {$("#password_errors").text(args.msg)}}})}) </script> </body>7、admin后臺(tái)管理
先創(chuàng)建一個(gè)超級(jí)管理員:createsuperuser。
Django給你提供了一個(gè)管理員可視化界面讓你方便的對(duì)模型表進(jìn)行增刪改查操作。
如果你想要使用admin后臺(tái)管理操作這些模型表,就需要先注冊(cè)你的模型表(告訴admin你需要操作哪些模型表)。
注冊(cè)模型表app01/admin.py文件:
from django.contrib import admin from app01 import models # Register your models here. 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.Article2Tag) admin.site.register(models.UpAndDown) admin.site.register(models.Comment)修改admin后臺(tái)管理默認(rèn)顯示的表名,models.py文件后面加上class Meta::
from django.db import models from django.contrib.auth.models import AbstractUserclass UserInfo(AbstractUser):phone = models.BigIntegerField(verbose_name='手機(jī)號(hào)',null=True,blank=True)# null=True 表示數(shù)據(jù)庫(kù)中該字段可以為空# blank=True 表示admin后臺(tái)管理該字段可以為空# 頭像# 給avatar字段傳文件對(duì)象,該文件會(huì)自動(dòng)存儲(chǔ)到avatar文件夾下,設(shè)置一個(gè)默認(rèn)頭像avatar = models.FileField(upload_to='avatar/',default='avatar/default.jpg',verbose_name='用戶頭像')create_time = models.DateField(auto_now_add = True)# 一對(duì)一關(guān)系blog = models.OneToOneField(to='Blog',null=True)class Meta:# verbose_name = '用戶表' # admin后臺(tái)管理默認(rèn)顯示的表名后面會(huì)加sverbose_name_plural = '用戶表' # 修改admin后臺(tái)管理默認(rèn)顯示的表名def __str__(self):return self.usernameadmin會(huì)給每一個(gè)注冊(cè)了的模型表自動(dòng)生成增刪改查四條url:
以UserInfo表為例:http://127.0.0.1:8000/admin/app01/userinfo/add/http://127.0.0.1:8000/admin/app01/userinfo/1/delete/http://127.0.0.1:8000/admin/app01/userinfo/1/change/
http://127.0.0.1:8000/admin/app01/userinfo/
數(shù)據(jù)綁定需要注意的是:
先去文章表綁定數(shù)據(jù)
個(gè)人站點(diǎn)
文章分類
將用戶和個(gè)人站點(diǎn)綁定關(guān)系標(biāo)簽和文章(不要把別人的文章綁定到其他人的標(biāo)簽)。
8、首頁(yè)文章展示
media配置網(wǎng)站所使用的靜態(tài)文件默認(rèn)放在static文件夾下,用戶上傳的靜態(tài)文件也應(yīng)該保存下來(lái)。
media配置:該配置可以讓用戶上傳的所有文件都固定存放在某一個(gè)指定的文件夾下。
在settings.py文件中配置:
等下次用戶上傳了文件時(shí),系統(tǒng)會(huì)自動(dòng)在根目錄下生成文件夾media/avatar/存放文件。我們之前在根目錄下手動(dòng)創(chuàng)建的avatar文件夾就不需要了。
如何開設(shè)后端指定文件夾資源urls.py中增加代碼:
from django.conf.urls import url from django.contrib import admin from app01 import views from django.views.static import serve from day72_BBS import settingsurlpatterns = [url(r'^admin/', admin.site.urls),url(r'^register/',views.register,name='reg'),url(r'^login/',views.login,name='login'),# 圖片驗(yàn)證碼相關(guān)操作url(r'^get_code/',views.get_code,name='get_code'),# 首頁(yè)url(r'^home/',views.home,name='home'),# 退出登錄url(r'^logout/',views.logout,name='logout'),# 修改密碼url(r'^set_password/',views.set_password,name='set_password'),# 暴露后端指定文件夾資源(MEDIA_ROOT的路徑)# 固定寫法,不要自己改動(dòng)url(r'^media/(?P<path>.*)',serve,{'document_root':settings.MEDIA_ROOT}) ]home.html首頁(yè)文章展示部分代碼:
<div class="container-fluid"><div class="col-md-2 col-xs-2"><div class="panel panel-warning"><div class="panel-heading"><h3 class="panel-title">廣告1</h3></div><div class="panel-body">內(nèi)容1</div></div><div class="panel panel-danger"><div class="panel-heading"><h3 class="panel-title">廣告2</h3></div><div class="panel-body">內(nèi)容2</div></div><div class="panel panel-info"><div class="panel-heading"><h3 class="panel-title">廣告3</h3></div><div class="panel-body">內(nèi)容3</div></div></div><div class="col-md-8 col-xs-8">{# 文章展示 #}<ul class="media-list">{% for article_obj in article_queryset %}<li class="media"><h4 class="media-heading"><a href="">{{ article_obj.title }}</a></h4><div class="media-left"><a href="#">{# 圖片的路徑需要我們手動(dòng)加上media前綴#}<img class="media-object" src="/media/{{ article_obj.blog.userinfo.avatar }}" alt="..." width="60"height="60"></a></div><div class="media-body">{{ article_obj.desc }}</div><br><div>{# 通過(guò)文章拿到用戶對(duì)象#}<span><a href="">{{ article_obj.blog.userinfo.username }}</a></span><span> 發(fā)布于 </span><span><a href=""> {{ article_obj.create_time|date:"Y-m-d" }} </a></span><span><span class="glyphicon glyphicon-comment"></span> 評(píng)論({{ article_obj.comment_num }}) </span><span><span class="glyphicon glyphicon-thumbs-up"></span> 點(diǎn)贊({{ article_obj.up_num }}) </span></div></li><hr>{% endfor %}</ul></div><div class="col-md-2 col-xs-2"><div class="panel panel-warning"><div class="panel-heading"><h3 class="panel-title">廣告1</h3></div><div class="panel-body">內(nèi)容1</div></div><div class="panel panel-danger"><div class="panel-heading"><h3 class="panel-title">廣告2</h3></div><div class="panel-body">內(nèi)容2</div></div><div class="panel panel-info"><div class="panel-heading"><h3 class="panel-title">廣告3</h3></div><div class="panel-body">內(nèi)容3</div></div></div> </div>9、圖片防盜鏈
如何避免別的網(wǎng)站直接通過(guò)本網(wǎng)站的url訪問(wèn)本網(wǎng)站資源:
簡(jiǎn)單的防盜:可以做到請(qǐng)求來(lái)的時(shí)候先看看當(dāng)前請(qǐng)求是從哪個(gè)網(wǎng)站過(guò)來(lái)的,如果是本網(wǎng)站那么可以正常訪問(wèn),如果是其它網(wǎng)站則直接拒絕。
請(qǐng)求頭里有一個(gè)專門記錄請(qǐng)求來(lái)自于哪個(gè)網(wǎng)址的參數(shù):Referer: http://127.0.0.1:8000/asfdasf/
繞過(guò)別人的防盜方式:
1、修改請(qǐng)求頭Referer
2、寫爬蟲將對(duì)方網(wǎng)址的所有資源直接下載到我們自己的服務(wù)器上
10、個(gè)人站點(diǎn)頁(yè)面搭建
ps:由于url方法第一個(gè)參數(shù)是正則表達(dá)式,所以當(dāng)路由特別多的時(shí)候,可能會(huì)出現(xiàn)被頂替的情況,針對(duì)這種情況有兩種解決方式:
1、修改正則表達(dá)式
2、調(diào)整url方法的位置
添加個(gè)人站點(diǎn)的路由:url(r'^(?P<username>w+)/$',views.site,name='site')
增加視圖函數(shù):
def site(request,username):# 先校驗(yàn)當(dāng)前用戶名對(duì)應(yīng)的個(gè)人站點(diǎn)是否存在user_obj = models.UserInfo.objects.filter(username=username).first()# 如果站點(diǎn)不存在則返回404頁(yè)面if not user_obj:return render(request,'errors.html')blog = user_obj.blog# 查詢當(dāng)前個(gè)人站點(diǎn)下的所有文章article_list = models.Article.objects.filter(blog = blog)return render(request,'site.html',locals())個(gè)人站點(diǎn)頁(yè)面site.html
<!DOCTYPE html> <html lang="en"> <head><meta charset="UTF-8"><title>Title</title><script src="jQuery.js"></script><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script><link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet"><script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script></head> <body> {# 這里要加上導(dǎo)航條的代碼 #} <div class="container-fluid"><div class="row"><div class="col-md-3 col-xs-3"><div class="panel panel-warning"><div class="panel-heading"><h3 class="panel-title">廣告1</h3></div><div class="panel-body">內(nèi)容1</div></div><div class="panel panel-danger"><div class="panel-heading"><h3 class="panel-title">廣告2</h3></div><div class="panel-body">內(nèi)容2</div></div><div class="panel panel-info"><div class="panel-heading"><h3 class="panel-title">廣告3</h3></div><div class="panel-body">內(nèi)容3</div></div></div><div class="col-md-9 col-xs-9"><ul class="media-list">{% for article_obj in article_list %}<li class="media"><h4 class="media-heading"><a href="">{{ article_obj.title }}</a></h4><div class="media-left"><a href="#">{# 圖片的路徑需要我們手動(dòng)加上media前綴#}<img class="media-object" src="/media/{{ article_obj.blog.userinfo.avatar }}" alt="..."width="60"height="60"></a></div><div class="media-body">{{ article_obj.desc }}</div><div class="pull-right"><span>posted </span><span>@ </span><span>{{ article_obj.create_time|date:"Y-m-d" }} </span><span>{{ article_obj.blog.userinfo.username }} </span><span><span class="glyphicon glyphicon-comment"></span>評(píng)論({{ article_obj.comment_num }}) </span><span><span class="glyphicon glyphicon-thumbs-up"></span>點(diǎn)贊({{ article_obj.up_num }}) </span><span><a href="">編輯</a></span></div></li><hr>{% endfor %}</ul></div></div> </div></body> </html>總結(jié)
以上是生活随笔為你收集整理的tornado项目搭建_Day71-73 BBS项目(1)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 向fedora虚拟机中复制文件_Unra
- 下一篇: ftp 工具_ftp,ftp工具哪个好用