圖書管理系統
出版社的管理
源碼位置:https://gitee.com/machangwei-8/learning_materials/tree/master/%E9%A1%B9%E7%9B%AE/bookmanager
1.設計URL
urlpatterns = [ url(r'^publisher_list/', views.publisher_list), ]
1.1環境准備
創建項目bookmanager。app名字修改成對應的。
settings
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) #項目目錄
DEBUG = True #debug模式,測試的時候使用true
創建數據庫: create database bookmanager;
#settings.py設置數據庫配置 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'bookmanager', 'HOST':'127.0.0.1', 'PORT':3306, 'USER':'root', 'PASSWORD':'123', } } #init中使用pymysql import pymysql pymysql.install_as_MySQLdb() #models.py中創建表 from django.db import models # Create your models here. class Publisher(models.Model): pid=models.AutoField(primary_key=True) #修改主鍵名字,使用自己創建的主鍵 name=models.CharField(max_length=32)
剛剛配置將USER配置成了USR,導致執行makegrations報錯:
遷移文件生成:
遷移文件中生成表名,字段名等:
再添加一個字段。它提示添加個默認值或者退出:
退出后,添加默認值再操作,添加字段,出錯
最終執行的遷移文件是這個內容,我再這里面添加一下這個字段:
然后再models里面添加這個,相當於一開始就在models里加了這個字段然后執行了makegragions。這樣表里的內容和遷移文件對應上了。
似乎執行改變了一次就生成一個文件:
剛才出問題了,將這些刪掉,再執行試試。
生成數據庫表:
添加並上傳:
展示出版社,設置url
from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^publisher_list/', views.publisher_list), ]
def publisher_list(request): #顯示一個含出版社信息的頁面 return render(request,'publisher_list.html')

<html lang="en"> <head> <meta charset="UTF-8"> <title>魔降風雲變</title> </head> <body> <h1>出版社列表</h1> </body> </html>
這樣就拿到頁面了,但是還要展示出數據庫中的數據。
渲染數據庫的數據,將數據以表格形式顯示出來:
from app01 import models #展示出版社 def publisher_list(request): # 從數據庫中查詢到出版社的信息 all_publishers = models.Publisher.objects.all() return render(request, 'publisher_list.html', {'all_publishers': all_publishers}) #publisher_list.html中使用的是字典中的鍵,鍵代替這個所有的對象
#render方法也可以傳{鍵值對},在html模板文件中使用鍵對傳入的值進行操作。
HTML頁面我們可以寫死了,但是我們需要的是根據數據庫的內容展示出來,用模板渲染出來。
在函數中傳進來的是對象列表。每一個都是對象,是表中一行數據:
每行數據庫的內容顯示在網頁,使用pk,那么無論主鍵是啥名字,都可以用pk代替
給表格加個邊框:
添加序號列:forloop.counter,從字典中每循環一次就計數一次,這樣就讓每行內容有個序號。而我們只需要將模板變量傳進去,用固定語法操作。
2.寫函數
from django.shortcuts import render
from app01 import models
# 展示出版社
def publisher_list(request):
# 從數據庫中查詢到出版社的信息
all_publishers = models.Publisher.objects.all()
# 返回一個包含出版社信息的頁面
return render(request, 'publisher_list.html', {'all_publishers': all_publishers})
過程放在1里面了
3.寫模板
<table border="1"> <thead> <tr> <th>序號</th> <th>ID</th> <th>名稱</th> </tr> </thead> <tbody> {% for publisher in all_publishers %} <tr> <td>{{ forloop.counter }}</td> <td>{{ publisher.pk }}</td> <td>{{ publisher.name }}</td> </tr> {% endfor %} </tbody> </table>
過程放在1里面了
3.1、新增
obj = models.Publisher.objects.create(name=publisher_name)
新增功能的效果:出版社列表頁面中一個按鈕,點擊跳轉頁面。這個頁面中輸入出版社的相關信息(出版社名稱),有個按鈕提交信息,后台要把輸入的信息插入到數據庫當中。插入之后瀏覽器需要再跳轉到出版社列表頁面。
這個按鈕直接是個a標簽就可以。也就是我在出版社列表中有個a標簽,點擊跳轉頁面add_publisher,里面要有form表單。
添加url,點擊views跳轉進入寫函數:
寫出添加數據庫記錄的頁面:這樣在框中輸入數據,后台將數據獲取到並寫入數據庫中
2處理的原理如下1處:
獲取出版社的名稱:是獲取這個提交請求的name,獲得post請求鍵值對中的值
# 使用ORM將數據插入到數據庫中,將獲取到的要添加的出版社名稱創建一個字段是輸入名稱的數據表記錄。pid不用寫,因為是自增
1處的請求2處沒有獲取到,然后后面的name=None,這樣前端3處跳轉報錯(1048, "Column 'name' cannot be null")
上面的報錯的原因是1處get的名字與2處不一致,所有沒有獲取到post提交的值,那么1處的變量就是None。解決方法,將它們改為一致,我將模板文件的2處改為publisher_name。
下面我做的操作是在3處寫入‘小馬出版社’,點擊提交之后,5處不變,重新進入模板文件的頁面。可看函數,函數中無論GET或者POST請求都是返回的同樣的頁面。然后再看
4處數據庫,函數拿到了我們提交的數據並在數據庫中已經創建了這條記錄。
網頁提交‘小馬過河2出版社’,創建一條數據,返回的是這個類的對象,就是那個數據表的一條記錄。可以通過創建一行數據時的返回值取出這行數據的各個字段的內容。
我們需要的是當我們從添加出版社的頁面提交添加之后,那么頁面跳轉查看所有出版社的頁面,這樣能看到新添加的出版社。那么使用重定向:
看下圖,在add_publisher頁面中添加進一行數據后,直接跳轉到publisher_list頁面並顯示新添加的一條記錄‘小熊出版社’。
添加數據頁面可以通過添加后進入展示所有數據的頁面,但是展示表中所有記錄的頁面還沒有進入添加數據的頁面,那么在展示表中所有記錄的頁面添加一個a標簽讓它跳轉到新增數據的頁面即可,如下圖
此處的a標簽地址應寫成路徑,在url路由中能找到,這里實現了點擊新增進入添加數據的頁面,添加數據又能返回這里的展示頁面。新增a標簽的跳轉實際上是點擊之后又發送了一次get請求。
我現在添加一個重復的出版社名稱,它會添加記錄到數據庫中,
出版社名字不應該重復的,下面就把這個字段設置成唯一鍵,然后執行數據遷移的那兩條命令結果報錯,原因是數據庫中已經有重復的數據了‘小熊出版社’。
django.db.utils.IntegrityError: (1062, "Duplicate entry '小熊出版社' for key 'app01_publisher_name_36f04192_uniq'")
pycharm中將這條數據刪除掉並提交
然后執行那兩條數據遷移的命令,成功修改這個字段為唯一鍵:
既然這個字段已經是唯一鍵了,那么我在頁面添加數據庫中已經有的這個字段內容,那么報錯如下:
(1062, "Duplicate entry '小熊出版社' for key 'app01_publisher_name_36f04192_uniq'")
數據庫有約束,我應該在數據庫插入之前,就進行判斷有沒有這個數據,而不是直接就往數據里面插入數據
這里添加了判斷條件之后還是報錯頁面,原因是,我們應該在數據插入之前就判斷是否存在相同的字段內容
1處插入數據前判斷是否有相同的,有就傳進error信息到,然后在返回的原網頁后面渲染上錯誤提示信息(2處)。這樣同名的數據插入網頁會提示已存在的提示
如果我不寫內容直接點擊提交
出現了一條空數據,這顯然不是我們想要的,所有我們需要在這個的函數上做判斷,如果獲取的值為空,那么返回這個頁面,並在輸入框后面渲染不能輸入為空的錯誤提示
這里顯示為已存在,剛剛我在里面插入了一個空的數據,現在將它刪除掉
現在顯示的是不能為空了,一開始error不是這個信息,到了不能為空這里,又被覆蓋了一次。
看下面的代碼,有三個render
我想要將三個render簡化成一個:
1)我可以最后返回一個render,其它的只是賦值error變量。然后render的頁面里渲染不同的error值。如果沒有錯誤即默認error是空字符串就可以。
2)然后判斷error信息是否非假(即非空),非假才執行創建數據記錄的操作並重定向頁面到查看所有出版社數據信息的頁面
當設置了一個error是空字符串的時候,正常返回的頁面是沒有顯示內容的。這樣雖然函數的結構修改了,但是功能沒有改變

def add_publisher(request): error='' #返回一個包含from表單的出版社頁面 if request.method=='POST': publish_name=request.POST.get('publisher_name') # 判斷出版社名稱是否有重復的 if models.Publisher.objects.filter(name=publish_name): error='出版社名稱已存在' #判斷輸入值是否為空 if not publish_name: error="輸入不能為空" if not error: obj=models.Publisher.objects.create(name=publish_name) #跳轉到展示出版社的頁面 return redirect('/publisher_list/') return render(request,'add_publisher.html',{'error':error})
但是還是有個問題,就是取出來的數據id是亂序的
這是因為publiser_list這一頁的get請求出來的數據沒有排序,將從數據庫中取出來的所有數據給個排序就解決了
3.2、刪除
obj_list = models.Publisher.objects.filter(pk=pk) obj_list.delete() obj = models.Publisher.objects.get(pk=pk) obj.delete()
想要刪除某一條記錄,應該做什么樣的操作呢?那么在名稱列后面有個按鈕或者a標簽,點擊它就刪除掉這條記錄,這樣是可以的吧。
那么在展示頁中后面添加一列,使用a標簽
現在應該是我點擊一個刪除,然后發送一個請求 ,並在后端數據庫將這條數據刪除,刪除之后再跳轉到這個頁面
先設計刪除url,創建刪除函數,刪除函數邏輯如下;
如果我的函數中有一處存在錯誤,可能所有函數對應的網頁都不能訪問:檢查元素加載失敗
給刪除a標簽設置一個地址,已經實現點擊刪除訪問這個路徑了
但是點擊刪除的是哪條記錄,我需要獲取到才能在后台將這條記錄刪除掉。於是將它的跳轉路徑拼接一個這條記錄的主鍵id
刪除代碼:
點擊刪除:
這時候我是點擊刪除按鈕刪的,我也可能使用瀏覽器訪問地址進行刪除
id=15的已刪除:
如果自己拼接在瀏覽器地址欄訪問請求不存在的記錄:報錯:Publisher matching query does not exist.
那么我應該對這種情況做個判斷,因為是get獲取數據的,get不到就會顯示錯誤,那么將get改為過濾
過濾出來可能多條記錄,也可以對queryset 這個對象列表里的對象批量刪除

def del_publisher(request): # 獲取要刪除的數據 pk = request.GET.get('id') obj_list = models.Publisher.objects.filter(pk=pk) if not obj_list: # 沒有要刪除的數據 return HttpResponse('要刪除的數據不存在') # 刪除該數據 # obj.delete() obj_list.delete() # 跳轉到展示頁面 return redirect('/publisher_list/')
3.3、編輯(修改數據)
obj.name = publisher_name
obj.save() # 保存數據到數據庫中
想要進行編輯的話應該是什么樣的過程。點擊編輯a標簽之后可以跳轉到編輯頁面,這個編輯頁面包含了你要編輯的那條數據,直接在這上面進行修改,修改完了之后提交,后台修改了之后跳轉回來到所有出版社展示頁
創建url,創建函數,將編輯標簽和刪除標簽寫在同一列
點擊其中一個頁面往這個地址發送請求了,並且根據id獲取到了是哪條數據需要編輯
應該返回一個頁面,這個頁面中包含點擊編輯的那條原始數據。編輯頁面和添加頁面差不多。
編輯的頁面這樣的話么有帶上id=2這條數據的name
因為編輯按鈕a標簽跳轉的時候帶着對應數據的id,那么由此獲得這條數據並將它的內容name默認填入到編輯的input標簽里
通過點擊編輯按鈕傳到請求里的id查出滿足條件的數據庫記錄,並傳到html模板文件中進行調用其中的值進行渲染。使得編輯框里顯示要編輯的原數據內容
如圖,點擊這個id的就顯示出來要編輯的原數據。編輯的展示這樣就沒有問題
下面點擊提交,要進行以下的邏輯操作:
要修改id=2的數據:由於發送POST請求之前,也要獲取到obj對象,所有把POST請求處理放在后面
現在需要將可能遇到的問題寫上。傳進錯誤信息進行渲染
寫這些功能先寫正常的情況下,然后再考慮會出現的問題。
4、項目目錄結構

from django.shortcuts import render, redirect, HttpResponse from app01 import models # 展示出版社 def publisher_list(request): # 從數據庫中查詢到出版社的信息 all_publishers = models.Publisher.objects.all().order_by('pk') # 返回一個包含出版社信息的頁面 return render(request, 'publisher_list.html', {'all_publishers': all_publishers}) # 新增出版社 def add_publisher(request): # 對請求方式進行判斷 if request.method == 'POST': # 處理POST請求 # 獲取到出版社的名稱 publisher_name = request.POST.get('publisher_name') # 判斷出版社名稱是否有重復的 if models.Publisher.objects.filter(name=publisher_name): return render(request, 'add_publisher.html', {'error': '出版社名稱已存在'}) # 判斷輸入的值是否為空 if not publisher_name: return render(request, 'add_publisher.html', {'error': '不能輸入為空'}) # 使用ORM將數據插入到數據庫中 obj = models.Publisher.objects.create(name=publisher_name) # 跳轉到展示出版社的頁面 return redirect('/publisher_list/') # 返回一個包含form表單的頁面 return render(request, 'add_publisher.html') # 新增出版社 def add_publisher(request): error = '' # 對請求方式進行判斷 if request.method == 'POST': # 處理POST請求 # 獲取到出版社的名稱 publisher_name = request.POST.get('publisher_name') # 判斷出版社名稱是否有重復的 if models.Publisher.objects.filter(name=publisher_name): error = '出版社名稱已存在' # 判斷輸入的值是否為空 if not publisher_name: error = '不能輸入為空' if not error: # 使用ORM將數據插入到數據庫中 obj = models.Publisher.objects.create(name=publisher_name) # 跳轉到展示出版社的頁面 return redirect('/publisher_list/') # 返回一個包含form表單的頁面 return render(request, 'add_publisher.html', {'error': error}) # 刪除出版社 def del_publisher(request): # 獲取要刪除的數據 pk = request.GET.get('id') obj_list = models.Publisher.objects.filter(pk=pk) if not obj_list: # 沒有要刪除的數據 return HttpResponse('要刪除的數據不存在') # 刪除該數據 # obj.delete() obj_list.delete() # 跳轉到展示頁面 return redirect('/publisher_list/') # 編輯出版社 def edit_publisher(request): error = '' # 查找要編輯的數據 pk = request.GET.get('id') # url上攜帶的參數 不是GET請求提交參數 obj_list = models.Publisher.objects.filter(pk=pk) if not obj_list: return HttpResponse('要編輯的數據不存在') obj = obj_list[0] if request.method == 'POST': # 處理POST請求 # 獲取新提交的出版的名稱 publisher_name = request.POST.get('publisher_name') if models.Publisher.objects.filter(name=publisher_name): # 新修改的名稱已存在 error = '新修改的名稱已存在' if obj.name == publisher_name: error = '名稱未修改' if not publisher_name: error = '名稱不能為空' if not error: # 修改數據 obj.name = publisher_name obj.save() # 保存數據到數據庫中 # 跳轉到出版社的展示頁面 return redirect('/publisher_list/') # 返回一個包含原始數據的頁面 return render(request, 'edit_publisher.html', {'obj': obj,'error':error})