Python Django 專題練習:遊戲資訊系統(註冊、登入登出、使用者加入最愛功能)
由於其他許多事情的耽誤,將近一個月沒有複習Django相關的程式,所以寫一個遊戲資訊彙總的系統
並且可以登入登出、收藏文章
以下是此專題練習會依序完成的步驟:
- 繪製系統流程圖
- 建置一個名為 game 的專案
- 建置資料庫結構、圖示
- 建置網頁基礎模板
- 登入登出系統與頁面處理函式及模板
- 註冊系統
- 首頁及詳細頁面處理函式及模板
- 我的最愛列表處理函式及模板
一、遊戲資訊系統流程圖:
以下是接下來將做的遊戲資訊系統的流程圖:
二、建置一個名為 game 的專案
1.建置專案:
- 建立一個名為 game 的專案
- 建立名為 gameapp 的 App
- 建立 templates 目錄、static 目錄
- 建立makemigrations 資料檔,並利用 migrate 將模型與資料庫同步
- 最後完成<setting.py>的設定
2.url.py配置:
from django.contrib import admin from django.urls import path,re_path from gameapp import views urlpatterns = [ path('admin/', admin.site.urls), path('crawler/',views.crawler), path('index/',views.index), re_path('detail/(\d+)/$',views.detail) ]
3.Bootstrap與JQuery配置:
(1)Bootstrap
為了快速達成響應式網頁的架設,這裡採用bootstrap框架
之前沒有說明過如何整合Bootstrap與Django,其實十分簡單
首先到Bootstrap的官方網站,下載bootstrap
將解壓縮後的bootstrap-3.3.7-dist目錄整個丟到game專案裡的static目錄即可
(2)JQuery
Bootstrap依賴JQuery,所以這裡也要同時引入
下載好JQuery後,在static目錄下創建js目錄,並將js檔案放入其中
三、運用爬蟲建置資料庫
因為想練習爬蟲,又希望能有自己的資料庫來做練習
我這邊先使用jupyter notebook做爬蟲的練習與測試,之後再一併整合進Django中
1.巴哈姆特-少女前線看板圖片
由於主圖片變數是要傳給每個頁面,所以這邊設定為全域變數
但由於全域變數不能藉由render傳遞給前台(templates)
所以之後會在各函式中轉換為區域變數
#主圖片爬蟲 def titleimg(): global timg1 url = 'https://forum.gamer.com.tw/A.php?bsn=31406' #選擇網址 user_agent = 'Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15' #偽裝使用者 headers = {'User-Agent':user_agent} data_res = urllib.request.Request(url=url,headers=headers) data = urllib.request.urlopen(data_res) data = data.read().decode('utf-8') sp = BeautifulSoup(data, "html.parser") titleimg = sp.find("div",{"class":"FM-abox1"}).findAll("img", src = re.compile('/welcome/')) for timg in titleimg: timg1 = timg['src']
2.巴哈姆特-少女前線看板文章列表
import requests from bs4 import BeautifulSoup import urllib import re url = 'https://forum.gamer.com.tw/B.php?bsn=31406' #選擇網址 user_agent = 'Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15' #偽裝使用者 headers = {'User-Agent':user_agent} data_res = urllib.request.Request(url=url,headers=headers) data = urllib.request.urlopen(data_res) data = data.read().decode('utf-8') sp = BeautifulSoup(data, "html.parser") title = sp.findAll('td',{"class":"b-list__main"}) titles = [] for titlee in title: titles.append(titlee.text.strip('\n')) links = [] ll = 'https://forum.gamer.com.tw/' link = sp.find("table",{"class":"b-list"}).findAll("a", href = re.compile('C.php?')) for linkk in link: page = re.compile(r'^((?!page).)*$') ##不匹配page last = re.compile(r'^((?!last).)*$') ##不匹配last m = page.match(linkk['href']) ##設定變數m來排除page if m != None: ##若不為None (None會跳出例外) n = last.match(m.group(0)) ## 設定變數n來排除last if n != None: ##若不為None (None會跳出例外) links.append(ll+n.group(0)) for t,l in zip(titles,links): print(t,l) content(l) ##此為獲取文章作者、內容的自訂函式(在下方) ※這邊遇到的問題 爬網址的時候,會多爬取到網頁上顯示文章內頁數的地方(每個文章後面會有最新頁數連結) 導致一個文章的網址會重複爬到多次,因此採用正則表達來排除網址內包含page和last的內容![]()
3.巴哈姆特-少女前線看板文章作者、內容
def content(aa): url = aa #選擇網址 user_agent = 'Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15' #偽裝使用者 headers = {'User-Agent':user_agent} data_res = urllib.request.Request(url=url,headers=headers) data = urllib.request.urlopen(data_res) data = data.read().decode('utf-8') sp = BeautifulSoup(data, "html.parser") authors = sp.find('div',{"class":"c-post__header__author"}).findAll("a",{"class":"username"}) for author in authors: print(author.text) ##打印作者 contents = sp.find('div',{"class":"c-article__content"}) print(contents.text) ##打印內容
4.建置model與資料庫
(1)建立資料模型
開啟 gameapp專案下的 <models.py> 檔,該檔預設已經import model套件。
from django.db import models class gamee(models.Model): cAuthor = models.CharField(max_length=50, blank=True) #建立字串型別的欄位,最大長度為20字元,欄位不可空白 cContent = models.CharField(max_length=9999,blank=True, default='') cTitle = models.CharField(max_length=100,blank=True, default='')#blank=True 欄位可空白 cLink = models.CharField(max_length=100,blank=True, default='') #每一筆資料在管理介面顯示的內容以下列程式定義 def __str__(self): return self.cTitle #表示顯示cTitle欄位
資料庫模型建立完成後,必須將建立資料表的架構和版本記錄下來,以利以後的追蹤。
如果專案伺服器是啟動中的,請按 CTRL+C 關閉伺服器,回到game 目錄,並以「python manage.py makemigrations」更新migrations目錄。
接著以「 python manage.py migrate」進行migrate,migrate會根據migrations紀錄將模型同步到資料庫。
(2)admin 後台管理
請開啟 gameapp 目錄底下的 <admin.py> 並寫入以下程序:
from django.contrib import admin from gameapp.models import gamee #下面會以register的方式,將建立的資料模型向admin註冊 class gameAdmin(admin.ModelAdmin): list_display=('id','cTitle','cAuthor','cContent','cLink') admin.site.register(gamee,gameAdmin)
記得要儲存文件,接下來先關閉server,要建立管理者帳號密碼來登入admin
這邊帳號先以admin,信箱設為admin@admin.com,密碼設為aa000000(不能設為全數字)
5.爬蟲並存入資料庫
我希望的是在遊戲資訊系土網站主頁按下"獲取最新資訊"連結的時候,進行爬蟲並更新網站的資訊內容
也就是按下連結的時候會執行下面所寫的crawler這個函式
其中要特別注意的是,要將網頁原始碼存入資料庫需要進行轉譯
否則會被SQL自帶的防護系統阻擋
轉譯的方法可以參考這篇
def crawler(request): #爬蟲程式 global titles,links,at,ct,t,l url = 'https://forum.gamer.com.tw/B.php?bsn=31406' #選擇網址 user_agent = 'Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15' #偽裝使用者 headers = {'User-Agent':user_agent} data_res = urllib.request.Request(url=url,headers=headers) data = urllib.request.urlopen(data_res) data = data.read().decode('utf-8') sp = BeautifulSoup(data, "html.parser") title = sp.findAll('td',{"class":"b-list__main"}) titles = [] for titlee in title: titles.append(titlee.text.strip('\n')) links = [] ll = 'https://forum.gamer.com.tw/' link = sp.find("table",{"class":"b-list"}).findAll("a", href = re.compile('C.php?')) for linkk in link: page = re.compile(r'^((?!page).)*$') ##不匹配page last = re.compile(r'^((?!last).)*$') ##不匹配last m = page.match(linkk['href']) ##設定變數m來排除page if m != None: ##若不為None (None會跳出例外) n = last.match(m.group(0)) ## 設定變數n來排除last if n != None: ##若不為None (None會跳出例外) links.append(ll+n.group(0)) for t,l in zip(titles,links): print(t,l) content(l) #使用爬蟲出來的網址進行文章內容的爬蟲 sql() #將爬出的內容進行與資料庫的連接 return redirect('/index/') def content(aa): global titles,links,at,ct,t,l url = aa #選擇網址 user_agent = 'Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15' #偽裝使用者 headers = {'User-Agent':user_agent} data_res = urllib.request.Request(url=url,headers=headers) data = urllib.request.urlopen(data_res) data = data.read().decode('utf-8') sp = BeautifulSoup(data, "html.parser") authors = sp.find('div',{"class":"c-post__header__author"}).findAll("a",{"class":"username"}) for author in authors: at = author.text print(at) contents = sp.find('div',{"class":"c-article__content"}) ct = html.escape(str(contents))#html編碼轉譯 print(ct) def sql(): global titles,links,at,ct,t,l cAuthor = at cContent = ct cTitle = t cLink = l try: if gamee.objects.get(cTitle=t): print('已有重複資料') except: unit = gamee.objects.create(cAuthor=cAuthor, cContent=cContent, cTitle=cTitle, cLink=cLink) unit.save() #寫入資料庫 print('成功儲存一筆資料')
這邊先做一個簡單的展示,之後會再做修改
四、網頁基礎模版
1.主頁、詳細頁面,會共同使用的模板
這邊命名為"base.html"
首先載入static目錄的內容
接著導引bootstrap的路徑
在一開始放了一張置中的響應式圖片(由主圖片爬蟲爬出,轉換為區域變數timg2後傳遞)
然後是bootstrap的導覽列(這邊直接修改bootstrap的官方程式碼)
根據使用者是否登入會呈現出不同列表
底下是版權聲明
最後則是載入JQuery
{% load staticfiles %} <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> {% block title %}{% endblock %} <link href="{% static 'bootstrap-3.3.7-dist/css/bootstrap.min.css' %}" rel="stylesheet"> </head> <body> <div id="headings" align="center"> <img src="{{timg2}}" class="img-responsive" /> </div> <nav class="navbar navbar-default"> <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="#my-nav" aria-expanded="false"> <span class="sr-only">切換導航條</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">少女前線資料系統</a> </div> <!-- Collect the nav links, forms, and other content for toggling --> <div class="collapse navbar-collapse" id="my-nav"> <ul class="nav navbar-nav"> <li class="active"><a href="/index/">主頁</a></li> </ul> {% if request.session.is_login %} <!-- 若登入則出現此列 --> <ul class="nav navbar-nav "> <li><a href="/lovepage/">我的最愛列表</a></li> </ul> {% endif %} <ul class="nav navbar-nav navbar-right"> <li><a href="/crawler/">獲取最新資料</a></li> {% if request.session.is_login %} <li><a href="#">{{ request.session.user_name }},歡迎您登入!</a></li> <li><a href="/logout/">登出</a></li> {% else %} <li><a href="/login/">登入</a></li> <li><a href="/register/">註冊</a></li> {% endif %} </ul> </div><!-- /.navbar-collapse --> </div><!-- /.container-fluid --> </nav> {% block content %}{% endblock %} <div id="footer">© Copyright 2017 ivanpotato 遊戲資訊系統</div> <!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> <script src="{% static 'js/jquery-3.3.1.js' %}"></script> <!-- Include all compiled plugins (below), or include individual files as needed --> <script src="{% static 'bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script> </body> </html>
五、登入登出系統與頁面處理函式及模板
1.建立使用者資訊資料庫
為了要讓使用者註冊與登入登出,需要保存各種用戶的資料
因此先建立一個使用者資訊的資料庫
這次將要保存用戶的用戶名稱、密碼、信箱、性別、創建時間
開啟 gameapp專案下的 <models.py> 檔,先前已經建立過gamee資料表
在下面建立一個名為User的資料表:
class User(models.Model): gender = ( ('male','男'), ('female','女'), ) name = models.CharField(max_length=128, unique = True) #唯一,不可有相同姓名 password = models.CharField(max_length=256) email = models.EmailField(unique=True) sex = models.CharField(max_length=32,choices=gender,default="男") ctime = models.DateTimeField(auto_now_add=True) def __str__(self): return self.name
資料庫模型建立完成後,必須將建立資料表的架構和版本記錄下來,以利以後的追蹤。
如果專案伺服器是啟動中的,請按 CTRL+C 關閉伺服器,回到game 目錄,並以「python manage.py makemigrations」更新migrations目錄。
接著以「 python manage.py migrate」進行migrate,migrate會根據migrations紀錄將模型同步到資料庫。
接著使用Django的後台管理系統,請開啟 gamesapp 目錄底下的 <admin.py> 並寫入以下程序:
class UserAdmin(admin.ModelAdmin): list_display=('id','name','password','email','sex','ctime') admin.site.register(User,UserAdmin)
2.urls.py路徑設置
預計會使用到首頁、登入頁面、登出頁面、註冊頁面(將在下一節提到)
from django.contrib import admin from django.urls import path,re_path from gameapp import views urlpatterns = [ path('admin/', admin.site.urls), path('index/',views.index), path('login/',views.login), path('logout/',views.logiout), path('register/',views.register), ]
3.建置登入表單
Django雖然有auth套件可以快速的做出登入驗證,但這裡我選擇自行建立表單模型
同時也方便之後可以自動生成前台表單
首先必須在 gameapp 目錄下新增一個 <form.py> 檔案,然後建立繼承forms.Form的類別,格式與Mode的格式相似
由於搭配Bootstrap使用,所以分別添加了屬性
from django import forms class UserForm(forms.Form): username = forms.CharField(label="用戶名", max_length=128, widget=forms.TextInput(attrs={'class': 'form-control'})) password = forms.CharField(label="密碼", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'}))
3.登入系統前台頁面
這邊使用許多 Bootstrap語法,快速建立出一個登入表單
{{message}}是登入錯誤時的提醒訊息
表單則是能藉由form自動生成
{% extends "base.html" %} {% load staticfiles %} {% block title %}<title>登入頁面</title>{% endblock %} {% block content %} <div class="container"> <div class="col-md-4 col-md-offset-4"> <form class="form-login" action="/login/" method="POST"> {% if message %} <div class="alert alert-warning">{{message}}</div> {% endif %} {% csrf_token %} <h1>歡迎登入!</h1> <div class="form-group"> {{ login_form.username.label_tag }} <!-- 由forms.py自動生成 --> {{ login_form.username }} </div> <div class="form-group"> {{ login_form.password.label_tag }} {{ login_form.password }} </div> <button type="reset" class="btn btn-default pull-left">重設</button> <button type="submit" class="btn btn-primary pull-right">確定登入</button> </form> </div> </div> {% endblock %}
4.登入系統後台頁面(views.py)
註解都打在程式碼裡面了
需要注意的地方就是這裡做了密碼加密
註冊的時候會進行加密
登入的時候需要進行解密
from django.shortcuts import render,redirect from gameapp.models import gamee,User from . import models from django.http import HttpResponse from django import forms from gameapp.forms import UserForm,RegisterForm import hashlib def hash_code(s, salt='ivan'): #密碼加密 h = hashlib.sha256() s = s + salt h.update(s.encode()) return h.hexdigest() def login(request): if request.session.get('is_login',None): #檢查session確定是否登入,不允許重複登入 return redirect("/index/") #若已登入則導向主頁 if request.method == 'POST': #接收POST訊息,若無則讓返回空表單 login_form = UserForm(request.POST) #導入表單模型 if login_form.is_valid(): #驗證表單 username = login_form.cleaned_data['username'] #從表單的cleaned_data中獲得具體值 password = login_form.cleaned_data['password'] try: user = models.User.objects.get(name=username) if user.password == hash_code(password): #密文處理 #使用session寫入登入者資料 request.session['is_login'] = True request.session['user_id'] = user.id request.session['user_name'] = user.name message = "登入成功" return redirect('/index/') else: message = "密碼不正確" except: message = "該用戶不存在" login_form = UserForm(request.POST) #返回空表單 return render(request,"login.html",locals()) def logout(request): if not request.session.get('is_login',None): #如果原本未登入,就不需要登出 return redirect('/index/') request.session.flush() #一次性將session內容全部清除 return redirect('/index/')
六、註冊系統處理函式及模板
1.建立註冊頁面表單模型
註冊頁面同樣也需要一個表單,因此在<forms.py>中建立一個 RegisterForm類別
class RegisterForm(forms.Form): gender = ( ('male','男'), ('female','女'), ) username = forms.CharField(label="用戶名", max_length=128, widget=forms.TextInput(attrs={'class': 'form-control'})) password1 = forms.CharField(label="密碼", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'})) password2 = forms.CharField(label="確認密碼", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control'})) email = forms.EmailField(label = "信箱", max_length=256, widget=forms.EmailInput(attrs={'class':'form-control'})) sex = forms.ChoiceField(label = "性別",choices=gender)
2.註冊系統前台頁面
這邊跟登入系統大同小異
使用許多 Bootstrap語法,快速建立出一個註冊表單
{{message}}是輸入錯誤時的提醒訊息
表單則是能藉由form自動生成
{% extends "base.html" %} {% load staticfiles %} {% block title %}<title>註冊頁面</title>{% endblock %} {% block content %} <div class="container"> <div class="col-md-4 col-md-offset-4"> <form class="form-register" action="/register/" method="POST"> {% if message %} <div class="alert alert-warning">{{ message }}</div> {% endif %} {% csrf_token %} <h2>歡迎註冊!</h2> <div class="form-group"> {{ register_form.username.label_tag }} {{ register_form.username }} </div> <div class="form-group"> {{ register_form.password1.label_tag }} {{ register_form.password1 }} </div> <div class="form-group"> {{ register_form.password2.label_tag }} {{ register_form.password2 }} </div> <div class="form-group"> {{ register_form.email.label_tag }} {{ register_form.email }} </div> <div class="form-group"> {{ register_form.sex.label_tag }} {{ register_form.sex }} </div> <button type="reset" class="btn btn-default pull-left">重設</button> <button type="submit" class="btn btn-primary pull-right">送出</button> </form> </div> </div> {% endblock %}
3.註冊系統後台頁面(views.py)
註解都打在程式碼裡面了
需要注意的地方就是這裡做了密碼加密
註冊的時候會進行加密
登入的時候需要進行解密
def register(request): if request.method == 'POST': register_form = RegisterForm(request.POST) message = '請檢察填寫的內容!' if register_form.is_valid(): #驗證數據,提取表單內容 username = register_form.cleaned_data['username'] password1 = register_form.cleaned_data['password1'] password2 = register_form.cleaned_data['password2'] email = register_form.cleaned_data['email'] sex = register_form.cleaned_data['sex'] if password1 != password2: #若兩次密碼不同 message = "兩次輸入的密碼不同!" return render(request, 'register.html', locals()) else: same_name_user = models.User.objects.filter(name=username) #比對資料庫是否有相同用戶名 if same_name_user: message = "該用戶名稱已存在!" return render(request, 'register.html', locals()) same_email_user = models.User.objects.filter(email=email) #比對資料庫是否有相同信箱 if same_email_user: message = "信箱已被使用!" return render(request, 'register.html', locals()) #若上面條件皆通過,則創建新的用戶 new_user = models.User() new_user.name = username new_user.password = hash_code(password1) new_user.email = email new_user.sex = sex new_user.save() return redirect('/login/') #自動跳轉到登入頁面 register_form = RegisterForm(request.POST) return render(request, 'register.html', locals())
七、首頁及詳細頁面處理函式及模板
1.首頁(前台)
將資料庫的檔案以for迴圈的方式顯示,傳遞id參數至detail詳細頁面
{% extends "base.html" %} {% load staticfiles %} {% block content %} <div class="topfunction"> <a href="/crawler/" title="獲取最新資訊">獲取最新資訊</a> </div> <table border="3" cellpadding="2" cellspacing="2"> <th>標題</th> <th>作者</th> <th>連結網址</th> {% for un in unit %} <tr> <td><a href="/detail/{{un.id}}">{{un.cTitle}}</a></td> <td>{{un.cAuthor}}</td> <td><a href="{{un.cLink}}">巴哈姆特討論串</a></td> </tr> {% endfor %} </table> {% endblock %}
2.首頁(後台)
反序查找資訊,最新資訊顯示在最前面
def index(request): unit = gamee.objects.all().order_by( '-id' ) return render(request,"index.html",locals())
3.詳細頁面(前台)
將查找到的資料,以表格的方式顯示
點選加入我的最愛,會將該文章加入我的最愛列表(這部分將在之後提到)
並加入回主頁、回頂部的連結
這邊 <meta name="referrer" content="never">讓瀏覽器不帶refer訊息,繞過顯示爬蟲圖片網址的refer檢測
{% extends "base.html" %} {% load staticfiles %} {% block title %} <title>遊戲資訊文章頁面</title> <!-- 讓瀏覽器不帶refer訊息,繞過顯示爬蟲圖片網址的refer的檢測 --> <meta name="referrer" content="never"> {% endblock %} {% block content %} <div align="center"> <a href="/index/" class="btn btn-info" role="button">返回主頁</a> <!-- 判斷是否登入,若有登入則顯示此按鈕 --> {% if request.session.is_login %} <a href="/adtlovelist/add/{{unit.id}}" class="btn btn-danger" role="button">加入最愛</a> {% endif %} </div> <div> </div> <div class="table-responsive"> <table border="3" cellpadding="2" cellspacing="2" class="table"> <tr> <td>標題:{{cTitle}}</td> </tr> <tr> <td>作者:{{cAuthor}}</td> </tr> <tr> <td>內文:{% autoescape off %}{{cContent}}{% endautoescape %}</td> </tr> </table> </div> <div align="center"> <a href="/index/" class="btn btn-info" role="button">返回主頁</a> <a href="." class="btn btn-danger" role="button">回頂部</a> </div> {% endblock %}
4.詳細頁面(後台)
根據傳入的參數,查找資料
def detail(request, detailid=None): timg2 = timg1 unit = gamee.objects.get(id=detailid) cTitle = unit.cTitle cAuthor = unit.cAuthor cContent = html.unescape(unit.cContent).replace("data-src","src") #cContent反轉譯,且由於巴哈有延遲載入,因此src屬性名稱不同需替換 return render(request,"detail.html",locals())
八、加入、刪除,我的最愛文章列表處理函式及模板
1.修改使用者資料表
在使用者資訊中添加一個 名為 clove 的欄位,用來儲存 我的最愛列表 的串列
由於需要串列的型態存入、以串列的型態取出來使用
Django 沒有內建的 ListField ,所以需要自定義來使用,以下為代碼:
from django.db import models import ast #自定義field class ListField(models.TextField): def __init__(self, *args, **kwargs): super(ListField, self).__init__(*args, **kwargs) def from_db_value(self, value, expression, connection, context): if not value: value = [] if isinstance(value, list): return value return ast.literal_eval(value) def get_prep_value(self, value): if value is None: return value return str(value) def value_to_string(self, obj): value = self._get_val_from_obj(obj) return self.get_db_prep_value(value)
修改 User 資料表,增加 clove 欄位,並儲存成 ListField 型態:
class User(models.Model): gender = ( ('male','男'), ('female','女'), ) name = models.CharField(max_length=128, unique = True) #唯一,不可有相同姓名 password = models.CharField(max_length=256) email = models.EmailField(unique=True) sex = models.CharField(max_length=32,choices=gender,default="男") ctime = models.DateTimeField(auto_now_add=True) clove = ListField(blank=True) def __str__(self): return self.name
資料庫模型建立完成後,必須將建立資料表的架構和版本記錄下來,以利以後的追蹤。
如果專案伺服器是啟動中的,請按 CTRL+C 關閉伺服器,回到game 目錄,並以「python manage.py makemigrations」更新migrations目錄。
接著以「 python manage.py migrate」進行migrate,migrate會根據migrations紀錄將模型同步到資料庫。
接著使用Django的後台管理系統,請開啟 gamesapp 目錄底下的 <admin.py> ,在UserAdmin 中添加 'clove')
2.添加urls.py 路徑
總共新增了兩個路徑:我的最愛頁面、加入、刪除我的最愛
根據傳入的字串決定執行加入最愛或刪除最愛的程式
from django.contrib import admin from django.urls import path,re_path from gameapp import views urlpatterns = [ path('admin/', admin.site.urls), path('crawler/',views.crawler), path('index/',views.index), path('login/',views.login), path('logout/',views.logout), path('register/',views.register), path('lovepage/',views.lovepage), #我的最愛頁面 re_path('detail/(\d+)/$',views.detail), re_path('adtlovelist/(\w+)/(\d+)/$',views.adtlovelist) #加入、刪除我的最愛 ]
3.我的最愛列表 前台頁面
這邊跟主頁設計得大同小異,表格新增了刪除我的最愛文章的選項欄位
刪除的id是使用forloop計數器的方式傳遞串列的項數
{% extends "base.html" %} {% load staticfiles %} {% block title %}<title>我的最愛列表</title>{% endblock %} {% block content %} <div class="table-responsive"> <table border="3" cellpadding="2" cellspacing="2" class="table"> <th>標題</th> <th>作者</th> <th>連結網址</th> <th>刪除</th> <!-- 從我的最愛列表串列迴圈 --> {% for un in ut.clove %} <tr> <td><a href="/detail/{{un.4}}">{{un.1}}</a></td> <td>{{un.0}}</td> <td><a href="{{un.3}}">巴哈姆特討論串</a></td> <!-- 使用forloop計數器 選取要刪除的串列項數 --> <td><a href="/adtlovelist/delete/{{forloop.counter0}}" class="btn btn-danger" role="button">刪除</a></td> </tr> {% endfor %} </table> </div> {% endblock %}
4.我的最愛列表 頁面(後台)
這邊比較複雜些,詳細的註解都寫在裡面了
#我的最愛列表 def lovepage(request): ut = models.User.objects.get(id=request.session['user_id']) #提取使用者資料表 #將會在前台使用 ut.clove 我的最愛串列 return render(request,"lovepage.html",locals()) #加入、刪除我的最愛文章 def adtlovelist (request, ctype=None, loveid = None): # ctype決定加入或是刪除,loveid則是傳入的文章id userinfo = models.User.objects.get(id=request.session['user_id']) #讀取使用者id lovelist = userinfo.clove #將我的最愛列表暫時存入lovelist串列 redetail = '/detail/' + loveid #取得當前詳細頁網址 if ctype == 'add': #加入最愛文章 love = gamee.objects.get(id=loveid) #讀取遊戲資訊列表 flag = True #設定檢查旗標 for unit in userinfo.clove: #檢查是否已有相同標題 if love.cTitle == unit[1]: #如果遊戲資訊標題==使用者我的最愛標題 就不存入 flag = False break if flag: #如果文章不存在 temlist = [] #暫時串列 temlist.append(love.cAuthor) temlist.append(love.cTitle) temlist.append(love.cLink) temlist.append(love.id) lovelist.append(temlist) #將暫時串列加入我的最愛串列 userinfo.clove = lovelist #將我的最愛串列寫入資料庫 userinfo.save() #儲存資料庫 return redirect(redetail) #跳轉回詳細頁面網址 elif ctype == 'delete': #刪除我的最愛文章 del lovelist[int(loveid)] #從我的最愛中移除該文章,用計數器的方式取得list的項數 userinfo.clove = lovelist #將刪除後的我的最愛串列寫入資料庫 userinfo.save() #儲存資料庫 return redirect('/lovepage/') #跳轉到我的最愛列表頁面