1. 程式人生 > 其它 >BBS專案註冊及登入過程詳細

BBS專案註冊及登入過程詳細

內容回顧

  • auth模組

    """
    認證模組
    	校驗使用者是否存在
    	儲存使用者狀態
    	校驗使用者是否登陸
    	修改密碼
    	...
    該模組預設需要用到django預設的auth_user表
    
    django的admin後臺管理需要用到該表
    
    如何建立超級使用者/管理員
    	python3 manage.py createsuperuser
    """
    from django.contrib import auth
    # 1.校驗使用者名稱和密碼是否正確
    auth.authenticate(request,username=username,password=password)
    """注意括號內使用者名稱和密碼必須都給才行,該方法有一個返回值 使用者物件/None"""
    # 2.儲存使用者資訊
    auth.login(request,user_obj)
    """
    內部自動幫你操作django_session表 request.session[key] = user_obj
    該方法執行完畢之後,在任意位置都可以通過request.user獲取到當前登陸的使用者物件
    """
    # 3.判斷當前使用者是否登陸
    request.user.is_authenticated()
    # 4.校驗使用者是否登陸裝飾器
    from django.contrib.auth.decorators import login_required
    	1.區域性配置
      @login_required(login_url='/login/')
      2.全域性配置
      LOGIN_URL = '/login/'
      # 全域性省事區域性靈活
    # 5.驗證原密碼是否正確
    request.user.check_password(old_password)
    # 6.修改密碼
    request.user.set_password(new_password)
    request.user.save()
    # 7.建立使用者
    from django.contrib.auth.models import User
    User.objects.create()  # 密碼不加密 不用
    User.objects.create_user()  # 普通使用者 經常使用
    User.objects.create_superuser()  # 超級使用者(郵箱必填)
    

    如何擴充套件auth_user表

    # 第一種 利用一對一的表關係
    	不推薦使用,不太方便
    # 第二種	利用面向物件繼承
    from django.contrib.auth.models import AbstractUser
    class UserInfo(AbstractUser):
      # 擴充套件auth_user表中沒有的欄位
    """
    一定要去配置檔案中配置
    AUTH_USER_MODEL = '應用名.類名'
    
    1.在沒有建立auth_user表的前提下才能替代
    	如果已經建立了那就換一個庫
    2.擴充套件的欄位儘量不要與原來的欄位衝突
    3.之前auth模組所有的功能和方法照樣可以使用,知識參考的表由原來的變成了UserInfo
    """
    

    專案開發流程

    # 1.需求分析
    
    # 2.專案設計
    
    # 3.分組開發
    
    # 4.測試
    
    # 5.交付上線
    

    資料庫表設計

    """
    資料庫的表設計是整個專案的重點所在
    """
    1.使用者表
    
    2.個人站點表
    
    3.文章標籤表
    
    4.文章分類表
    
    5.文章表
    	
      1.資料庫欄位優化設計
      點贊數  普通欄位
      點踩數  普通欄位
      評論數  普通欄位
      # 只需要確保後續在操作點贊點踩和評論表的時候同步的將對應的普通欄位更新即可
    
    6.點贊點踩表
    
    7.評論表
    	根評論和子評論
      to='self'
      
    # 7張表之間的關係
    

內容概要

  • 表建立及同步
  • 註冊功能
    • forms元件
    • 使用者頭像前端實時展示
    • ajax
  • 登陸功能
    • 自己實現圖片驗證碼
    • ajax
  • 搭建bbs首頁
    • 導航條根據使用者是否登陸展示不同的內容

內容詳細

資料庫表建立及同步

"""
由於django自帶的sqlite資料庫對日期不敏感,所以我們換成MySQL
"""
from django.db import models

# Create your models here.
"""
先寫普通欄位
之後再寫外來鍵欄位
"""
from django.contrib.auth.models import AbstractUser


class UserInfo(AbstractUser):
    phone = models.BigIntegerField(verbose_name='手機號',null=True)
    # 頭像
    avatar = models.FileField(upload_to='avatar/',default='avatar/default.png',verbose_name='使用者頭像')
    """
    給avatar欄位傳檔案物件 該檔案會自動儲存到avatar檔案下 然後avatar欄位只儲存檔案路徑avatar/default.png
    """
    create_time = models.DateField(auto_now_add=True)

    blog = models.OneToOneField(to='Blog',null=True)


class Blog(models.Model):
    site_name = models.CharField(verbose_name='站點名稱',max_length=32)
    site_title = models.CharField(verbose_name='站點標題',max_length=32)
    # 簡單模擬 帶你認識樣式內部原理的操作
    site_theme = models.CharField(verbose_name='站點樣式',max_length=64)  # 存css/js的檔案路徑


class Category(models.Model):
    name = models.CharField(verbose_name='文章分類',max_length=32)
    blog = models.ForeignKey(to='Blog',null=True)


class Tag(models.Model):
    name = models.CharField(verbose_name='文章標籤',max_length=32)
    blog = models.ForeignKey(to='Blog', null=True)


class Article(models.Model):
    title = models.CharField(verbose_name='文章標題',max_length=64)
    desc = models.CharField(verbose_name='文章簡介',max_length=255)
    # 文章內容有很多 一般情況下都是使用TextField
    content = models.TextField(verbose_name='文章內容')
    create_time = models.DateField(auto_now_add=True)

    # 資料庫欄位設計優化
    up_num = models.BigIntegerField(verbose_name='點贊數',default=0)
    down_num = models.BigIntegerField(verbose_name='點踩數',default=0)
    comment_num = models.BigIntegerField(verbose_name='評論數',default=0)

    # 外來鍵欄位
    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()  # 傳佈爾值 存0/1


class Comment(models.Model):
    user = models.ForeignKey(to='UserInfo')
    article = models.ForeignKey(to='Article')
    content = models.CharField(verbose_name='評論內容',max_length=255)
    comment_time = models.DateTimeField(verbose_name='評論時間',auto_now_add=True)
    # 自關聯
    parent = models.ForeignKey(to='self',null=True)  # 有些評論就是根評論

註冊功能

"""
我們之前是直接在views.py中書寫的forms元件程式碼
但是為了接耦合 應該將所有的forms元件程式碼單獨寫到一個地方

如果你的專案至始至終只用到一個forms元件那麼你可以直接建一個py檔案書寫即可
	myforms.py
但是如果你的專案需要使用多個forms元件,那麼你可以建立一個資料夾在資料夾內根據
forms元件功能的不同建立不同的py檔案
	myforms資料夾
		regform.py
		loginform.py
		userform.py
		orderform.py
		...
"""
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': 'jason', 'password': '123', 'confirm_password': '123', 'email': '[email protected]'}
            clean_data = form_obj.cleaned_data  # 將校驗通過的資料字典賦值給一個變數
            # 將字典裡面的confirm_password鍵值對刪除
            clean_data.pop('confirm_password')  # {'username': 'jason', 'password': '123', 'email': '[email protected]'}
            # 使用者頭像
            file_obj = request.FILES.get('avatar')
            """針對使用者頭像一定要判斷是否傳值 不能直接新增到字典裡面去"""
            if file_obj:
                clean_data['avatar'] = file_obj
            # 直接操作資料庫儲存資料
            models.UserInfo.objects.create_user(**clean_data)
            back_dic['url'] = '/login/'
        else:
            back_dic['code'] = 2000
            back_dic['msg'] = form_obj.errors
        return JsonResponse(back_dic)
    return render(request,'register.html',locals())

  
<script>
    $("#myfile").change(function () {
        // 檔案閱讀器物件
        // 1 先生成一個檔案閱讀器物件
        let myFileReaderObj = new FileReader();
        // 2 獲取使用者上傳的頭像檔案
        let fileObj = $(this)[0].files[0];
        // 3 將檔案物件交給閱讀器物件讀取
        myFileReaderObj.readAsDataURL(fileObj)  // 非同步操作  IO操作
        // 4 利用檔案閱讀器將檔案展示到前端頁面  修改src屬性
        // 等待檔案閱讀器載入完畢之後再執行
        myFileReaderObj.onload = function(){
             $('#myimg').attr('src',myFileReaderObj.result)
        }
    })

    $('#id_commit').click(function () {
        // 傳送ajax請求     我們傳送的資料中即包含普通的鍵值也包含檔案
        let formDataObj = new FormData();
        // 1.新增普通的鍵值對
        {#console.log($('#myform').serializeArray())  // [{},{},{},{},{}]  只包含普通鍵值對#}
        $.each($('#myform').serializeArray(),function (index,obj) {
            {#console.log(index,obj)#}  // 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) {
                        {#console.log(index,obj)  //  username        ["使用者名稱不能為空"]#}
                        let targetId = '#id_' + index;
                        $(targetId).next().text(obj[0]).parent().addClass('has-error')
                    })
                }
            }
        })
    })
    // 給所有的input框繫結獲取焦點事件
    $('input').focus(function () {
        // 將input下面的span標籤和input外面的div標籤修改內容及屬性
        $(this).next().text('').parent().removeClass('has-error')
    })
</script>
              
# 擴充套件
"""
一般情況下我們在儲存使用者檔案的時候為了避免檔名衝突的情況
會自己給檔名加一個字首	
	uuid
	隨機字串
	...
"""

登陸功能

"""
img標籤的src屬性
	1.圖片路徑
	2.url
	3.圖片的二進位制資料

我們的計算機上面致所有能夠輸出各式各樣的字型樣式
內部其實對應的是一個個.ttf結尾的檔案

http://www.zhaozi.cn/ai/2019/fontlist.php?ph=1&classid=32&softsq=%E5%85%8D%E8%B4%B9%E5%95%86%E7%94%A8
"""


"""
圖片相關的模組
    pip3 install pillow
"""
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'static/img/111.jpg','rb') as f:
    #     data = f.read()
    # return HttpResponse(data)

    # 推導步驟2:利用pillow模組動態產生圖片
    # img_obj = Image.new('RGB',(430,35),'green')
    # img_obj = Image.new('RGB',(430,35),get_random())
    # # 先將圖片物件儲存起來
    # with open('xxx.png','wb') as f:
    #     img_obj.save(f,'png')
    # # 再將圖片物件讀取出來
    # with open('xxx.png','rb') as f:
    #     data = f.read()
    # return HttpResponse(data)

    # 推導步驟3:檔案儲存繁瑣IO操作效率低  藉助於記憶體管理器模組
    # img_obj = Image.new('RGB', (430, 35), get_random())
    # 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)  # 產生一個畫筆物件
    img_font = ImageFont.truetype('static/font/222.ttf',30)  # 字型樣式 大小

    # 隨機驗證碼  五位數的隨機驗證碼  數字 小寫字母 大寫字母
    code = ''
    for i in range(5):
        random_upper = chr(random.randint(65,90))
        random_lower = chr(random.randint(97,122))
        random_int = str(random.randint(0,9))
        # 從上面三個裡面隨機選擇一個
        tmp = random.choice([random_lower,random_upper,random_int])
        # 將產生的隨機字串寫入到圖片上
        """
        為什麼一個個寫而不是生成好了之後再寫
        因為一個個寫能夠控制每個字型的間隙 而生成好之後再寫的話
        間隙就沒法控制了
        """
        img_draw.text((i*60+60,-2),tmp,get_random(),img_font)
        # 拼接隨機字串
        code += tmp
    print(code)
    # 隨機驗證碼在登陸的檢視函式裡面需要用到 要比對 所以要找地方存起來並且其他檢視函式也能拿到
    request.session['code'] = code
    io_obj = BytesIO()
    img_obj.save(io_obj,'png')
    return HttpResponse(io_obj.getvalue())
  
  
  
  
  
  <script>
    $("#id_img").click(function () {
        // 1 先獲取標籤之前的src
        let oldVal = $(this).attr('src');
        $(this).attr('src',oldVal += '?')
    })
</script>