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