1. 程式人生 > >DAY87-BBS專案(一) 資料庫設計與簡單登陸、驗證碼

DAY87-BBS專案(一) 資料庫設計與簡單登陸、驗證碼

一、BBS專案之專案分析

專案流程:

1 搞清楚需求(產品經理)

  (1) 基於使用者認證元件和Ajax實現登入驗證(圖片驗證碼)

  (2) 基於forms元件和Ajax實現註冊功能

  (3) 設計系統首頁(文章列表渲染)

  (4) 設計個人站點頁面---跨表查詢,分組查詢

  (5) 文章詳情頁

  (6) 實現文章點贊功能

  (7) 實現文章的評論
      ---文章的評論
      ---評論的評論

  (8) 副文字編輯框 和 防止xss攻擊(防止別人提交js程式碼)


2 設計表結構


3 按著每一個功能分別進行開發
  

4 功能測試


5 專案部署上線

二、資料庫設計

1.分析資料表以及表關係

使用者表:UserInfo
個人站點表:blog
文章表:Article
評論表:commit
點贊點踩表:upanddown
文章分類表:category
文章標籤表:tag

2.設計欄位

User:繼承AbstractUser,使用者表
    nid
    phone
    avatar   使用者頭像
    blog
Blog:站點
    nid
    title
    site_name
    theme
category:分類
    nid
    title
    blog   跟blog一對多
tag:(文章關鍵字)
    nid
    title
    blog    跟blog一對多

article:文章
    nid
    title
    desc    摘要
    create_time    auto_add_now:當該條記錄建立時,自動添加當前時間
    content   文章內容
    category    一對多
    blog        一對多
    tag         多對多

commit:評論
    nid
    user     哪個使用者
    article  對哪篇文章
    content   評論了什麼內容
    commit_time  時間
    parent_id   子評論
    
UpAndDown:點贊
    nid
    user
    article
    is_up

3.建立資料庫

from django.db import models
from django.contrib.auth.models import AbstractUser


# Create your models here.

class UserInfo(AbstractUser):
    nid = models.AutoField(primary_key=True)
    # 電話可以為空
    phone = models.CharField(max_length=32, null=True)
    # 頭像
    avatar = models.FileField(upload_to='avatar/', default='/static/image/default.png')
    # 站點
    blog = models.OneToOneField(to='Blog', to_field='nid')

    # 使用者和使用者的站點應該是聯合唯一的
    class Mate:
        unique_together = (('username', 'blog'),)


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)


# 分類
class Category(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=64)
    # 個人站點可以擁有多個分類
    blog = models.ForeignKey(to='Blog', to_field='nid', null=True)


# 標籤
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)


# 文章
class Article(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=64)
    # 文章簡介
    desc = models.CharField(max_length=255)
    # 文章內容
    content = models.TextField()
    # 建立時間
    create_time = models.DateTimeField(auto_now_add=True)
    # 個人站點可以擁有多篇文章
    blog = models.ForeignKey(to='Blog', to_field='nid', null=True)
    # 一個分類可以擁有多篇文章
    category = models.ForeignKey(to='Category', to_field='nid', null=True)
    # 文章和標籤是多對多的關係,一篇文章可以由多個標籤,一個標籤可以給多篇文章使用
    # 手動建立第三張表
    tag = models.ManyToManyField(to='Tag', through='Article2Tag', through_fields=('article', 'tag'))


# 多對多第三張表
class Article2Tag(models.Model):
    nid = models.AutoField(primary_key=True)
    tag = models.ForeignKey(to='Tag', to_field='nid')
    article = models.ForeignKey(to='Article', to_field='nid')

    class Mate:
        unique_together = (('tag', 'article'),)


# 評論
class Commit(models.Model):
    nid = models.AutoField(primary_key=True)
    # 評論內容
    content = models.TextField()
    # 評論時間
    create_time = models.DateTimeField(auto_now_add=True)
    # 可以對評論作出評論,設定一個欄位用來表示是對哪條評論做出的評論
    # 方法1
    # parent_id = models.ForeignKey(to='Commit',to_field='nid')
    # 方法2
    parent_id = models.ForeignKey(to='self',to_field='nid')
    # 一個使用者可以評論多條
    user = models.ForeignKey(to='UserInfo', to_field='nid')
    # 一篇文章可以有多條評論
    article = models.ForeignKey(to='Article', to_field='nid')


# 點贊
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')
    # 1是贊,0是踩
    is_up = models.BooleanField()

    class Meta:
        # 一個使用者只能對一篇文章贊或踩,所以要做聯合唯一
        unique_together = (('user', 'article'),)

三、專案配置

setting.py

#連結資料庫
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'bbs',
        'POST': 3306,
        'HOST': '127.0.0.1',
        'USER': 'root',
        'PASSWORD': 'root'
    }
}
#靜態檔案路徑配置
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static')
]
#auth元件authinfo表路徑
AUTH_USER_MODEL = 'blog.UserInfo'

四、登入功能以及驗證碼

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登入</title>
    {% load static %}
    <link rel="stylesheet" href="{% get_static_prefix %}bootstrap-3.3.7-dist/css/bootstrap.css">
    <script src="{% get_static_prefix %}jquery-3.3.1.js"></script>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-4 col-md-offset-4">
            <h1 style="text-align: center">登入</h1>
            <form>
                {% csrf_token %}
                <div class="form-group">
                    <label for="name">使用者名稱</label>
                    <input type="text" class="form-control" id="name" placeholder="使用者名稱" required>
                </div>
                <div class="form-group">
                    <label for="pwd">密碼</label>
                    <input type="password" class="form-control" id="pwd" placeholder="密碼" required>
                </div>
                <div class="form-group">
                    <label for="validCode">驗證碼</label>
                    <div class="row">
                        <div class="col-md-6">
                            <input type="text" class="form-control" id="validCode" placeholder="驗證碼不分大小寫" required>
                        </div>
                        <div class="col-md-6">
                            <img src="/get_validCode/" title="60s後失效" height="35" width="200" class="img-rounded"
                                 id="get_validCode_img">
                        </div>
                    </div>
                </div>
            </form>
            <button type="button" class="btn btn-primary btn-lg pull-right" id="btn">登入</button>
        </div>

    </div>
</div>
</body>
<script>
    $(function () {
        $("#get_validCode_img").click(function () {
            $(this)[0].src += '?'
        })
    });
    $('#btn').click(function () {

        var da = {'name':$('#name').val(),'pwd':$('#pwd').val(),'validCode':$('#validCode').val(),csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val()}
        $.ajax({
            url:'/login/',
            type:'post',
            data:da,
            success:function (data) {
                alert(data)
            }
        })
    })
</script>
</html>

驗證碼

from PIL import Image, ImageDraw, ImageFont
from BBS import common
from io import BytesIO

def get_validCode(request):
    # 第一種方式:本地存放固定圖片
    # with open('static/1.png','rb') as f:
    #     data = f.read()
    # return HttpResponse(data)

    # # 第二種方式:本地存放隨機圖片
    # # 生成圖片 new(模式,寬高,顏色)
    # img = Image.new('RGB', (200, 35),color=common.get_color())
    # # 寫一個空白本地圖片檔案
    # with open('ve_code.png', 'wb') as f:
    #     # 呼叫img物件的save方法儲存到空檔案
    #     img.save(f,'png')
    # # 開啟檔案,再返回
    # with open('ve_code.png','rb') as f:
    #     data = f.read()
    # return HttpResponse(data)

    # # 第三種方式:在記憶體中存放隨機圖片
    # # 生成圖片 new(模式,寬高,顏色)
    # img = Image.new('RGB', (200, 35),color=common.get_color())
    # # 生成一個空白記憶體檔案
    # f = BytesIO()
    # # 呼叫img物件的save方法儲存到空記憶體檔案
    # img.save(f,'png')
    # # 獲取記憶體檔案的內容
    # data = f.getvalue()
    # return HttpResponse(data)

    # 第四種方式:在記憶體中存放隨機圖片,在圖片上加上隨機驗證碼
    # 生成圖片物件 new(模式,寬高,顏色)
    img = Image.new('RGB', (200, 35), color=common.get_color())
    # 生成畫筆物件,並將圖片物件傳入
    img_draw = ImageDraw.Draw(img)
    # 生成字型物件
    font = ImageFont.truetype('static/font/ss.ttf', size=25)
    # 將文字用畫筆寫入圖片
    # text(座標,文字,顏色,字型)
    img_draw.text((50, 0), common.get_ve_code(request), common.get_color(), font)
    # 生成一個空白記憶體檔案
    f = BytesIO()
    img.save(f, 'png')
    data = f.getvalue()
    return HttpResponse(data)

共用模組

import random

# 產生隨機RGB
def get_color():
    return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))

# 產生隨機驗證碼
def get_ve_code(request):
    ve_code = ''
    for i in range(5):
        number = random.randint(0, 9)
        upper = chr(random.randint(97, 122))
        lower = chr(random.randint(65, 90))
        add = random.choice((number, upper, lower))
        ve_code += str(add)
    request.session['ve_code']=ve_code
    request.session.set_expiry(60)
    return ve_code

登入

from django.shortcuts import render, HttpResponse
from django.contrib import auth


# Create your views here.
def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    else:
        name = request.POST.get('name')
        pwd = request.POST.get('pwd')
        validCode = request.POST.get('validCode')
        user = auth.authenticate(username=name, password=pwd)
        validCode_sess = request.session.get('ve_code')
        if validCode.upper() != validCode_sess.upper():
            return HttpResponse('驗證碼錯誤')
        if user is not None:
            return HttpResponse('登陸成功')
        else:
            return HttpResponse('使用者名稱或密碼錯誤')