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/') #跳轉到我的最愛列表頁面

嗨 大大最近在學爬蟲 不知道能不能跟你請益一下你這篇爬蟲利用django寫出來的code
可以參考看看使用 django-q 設定排程的方式運行 https://django-q.readthedocs.io/en/latest/
有試過用一個db分別將 user pk 和 每一則文章的pk 存入DB中 之後user進入我的最愛頁面中 會依照現在user.id判斷 db中有那些是這個user加入過得並顯示 然後可以刪除這樣?