(轉)python 全棧開發,Day72(昨日作業講解,昨日內容回顧,Django多表建立)
昨日作業講解
1.圖書管理系統
實現功能:book單表的增刪改查
1.1 新建一個專案bms,建立應用book。過程略...
1.2 手動建立static目錄,並在目錄裡面建立css資料夾,修改settings.py,設定static的目錄位置
STATICFILES_DIRS=[ os.path.join(BASE_DIR,"static") ]View Code
修改templates的目錄位置
TEMPLATES = [ { 'BACKEND': 'View Codedjango.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
註冊app
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypesView Code', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'book', ]
下載bootstrap.css放到static目錄下的css目錄裡面
https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css
修改models.py,設計表
from django.db import models # Create your models here. class Book(models.Model): title=models.CharField(max_length=32,unique=True) price=models.DecimalField(max_digits=8,decimal_places=2,null=True) pub_date=models.DateField() publish=models.CharField(max_length=32) is_pub=models.BooleanField(default=True)View Code
注意:這裡沒有加id欄位,django會自動建立id欄位,並設定主鍵自增,型別為int
預設的是sqlite3資料庫,直接用它就可以了。它是基於檔案的,不需要使用者名稱和密碼。
執行2個命令
python manage.py makemigrations
python manage.py migrate
就可以建立sqlite資料庫
點選Pycharm右側的資料庫,新增sqlite
點選下載驅動
開始安裝
選擇file檔案,點選測試連線,點選Ok
展開右邊的資料庫,開啟book_book
編輯urls.py,新增url
from django.contrib import admin from django.urls import path,re_path from book import views urlpatterns = [ path('admin/', admin.site.urls), path('', views.index), path('index/', views.index), path('books/add/', views.add), path('books/manage/', views.manage), re_path('books/delete/(?P<id>\d+)', views.delete), re_path('books/modify/(?P<id>\d+)', views.modify), ]View Code
path('', views.index),表示輸入url: http://127.0.0.1:8001 可以直接跳轉首頁
修改views.py,增加檢視函式
from django.shortcuts import render,redirect,HttpResponse from book import models # Create your views here. def index(request): #首頁 ret = models.Book.objects.all().exists() # 判斷表是否有記錄 if ret: book_list=models.Book.objects.all() # 查詢表的所有記錄 return render(request,"index.html",{"book_list":book_list}) else: hint = '<script>alert("沒有書籍,請新增書籍");window.location.href="/books/add"</script>' return HttpResponse(hint) # js跳轉到新增頁面 def add(request): # 新增 if request.method=="POST": # print(request.POST) title=request.POST.get("title") price=request.POST.get("price") pub_date=request.POST.get("pub_date") publish=request.POST.get("publish") is_pub=request.POST.get("is_pub") #插入一條記錄 obj=models.Book.objects.create(title=title,price=price,publish=publish,pub_date=pub_date,is_pub=is_pub) print(obj.title) hint = '<script>alert("新增成功");window.location.href="/index/"</script>' return HttpResponse(hint) # js跳轉到首頁 return render(request,"add.html") # 預設渲染新增頁面 def delete(request,id): # 刪除 ret = models.Book.objects.filter(id=id).delete() # 返回元組 if ret[0]: # 取值為1的情況下 hint = '<script>alert("刪除成功");window.location.href="/index/"</script>' return HttpResponse(hint) else: # 取值為0的情況下 hint = '<script>alert("刪除失敗");window.location.href="/index/"</script>' return HttpResponse(hint) def manage(request): # 管理頁面 ret = models.Book.objects.all().exists() if ret: book_list = models.Book.objects.all() #載入管理頁面 return render(request, "manage.html", {"book_list": book_list}) else: hint = '<script>alert("沒有書籍,請新增書籍");window.location.href="/books/add"</script>' return HttpResponse(hint) def modify(request,id): # 修改 if request.method == "POST": title = request.POST.get("title") price = request.POST.get("price") pub_date = request.POST.get("pub_date") publish = request.POST.get("publish") is_pub = request.POST.get("is_pub") #更新一條記錄 ret = models.Book.objects.filter(id=id).update(title=title, price=price, publish=publish, pub_date=pub_date, is_pub=is_pub) # print(ret) if ret: # 判斷返回值為1 hint = '<script>alert("修改成功");window.location.href="/index/"</script>' return HttpResponse(hint) # js跳轉 else: # 返回為0 hint = '<script>alert("修改失敗");window.location.href="/index/"</script>' return HttpResponse(hint) # js跳轉 book = models.Book.objects.get(id=id) # 預設獲取id值 return render(request, "modify.html", {"book": book}) # 渲染指定id的記錄View Code
建立templates目錄,在templates目錄下建立base.html
注意:這個是模板頁面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> {% block title %} <title>title</title> {% endblock title %} <link rel="stylesheet" href="/static/css/bootstrap.min.css"> <style> * { margin: 0; padding: 0; } .header { width: 100%; height: 60px; background-color: #369; } .title { line-height: 60px; color: white; font-weight: 100; margin-left: 20px; font-size: 20px; } .container{ margin-top: 20px; } .table th, .table td { text-align: center; vertical-align: middle!important; } </style> </head> <body> <div class="header"> <p class="title"> 圖書管理系統 </p> </div> <div class="container"> <div class="row"> <div class="col-md-3"> <div class="panel panel-danger"> <div class="panel-heading"><a href="/index/">檢視書籍</a></div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-success"> <div class="panel-heading"><a href="/books/add/">新增書籍</a></div> <div class="panel-body"> Panel content </div> </div> <div class="panel panel-warning"> <div class="panel-heading"><a href="/books/manage/">管理書籍</a></div> <div class="panel-body"> Panel content </div> </div> </div> <div class="col-md-9"> {% block content %} {% endblock %} </div> </div> </div> </body> </html>View Code
建立index.html,它是首頁
{% extends 'base.html' %} {% block title %} <title>檢視書籍</title> {% endblock title %} {% block content %} <h3>檢視書籍</h3> <table class="table table-hover table-striped "> <thead> <tr> <th>名稱</th> <th>價格</th> <th>出版日期</th> <th>出版社</th> <th>是否出版</th> </tr> </thead> <tbody> {% for book in book_list %} <tr> <td>{{ book.title }}</td> <td>{{ book.price }}</td> <td>{{ book.pub_date|date:"Y-m-d" }}</td> <td>{{ book.publish }}</td> <td> {% if book.is_pub %} 已出版 {% else %} 未出版 {% endif %} </td> </tr> {% endfor %} </tbody> </table> {% endblock content %}View Code
建立add.html,它是新增頁面
{% extends 'base.html' %} {% block title %} <title>新增書籍</title> {% endblock title %} {% block content %} <h3>新增書籍</h3> <form action="" method="post"> {% csrf_token %} <div class="form-group"> <label for="">書籍名稱</label> <input type="text" name="title" class="form-control"> </div> <div class="form-group"> <label for="">價格</label> <input type="text" name="price" class="form-control"> </div> <div class="form-group"> <label for="">出版日期</label> <input type="date" name="pub_date" class="form-control"> </div> <div class="form-group"> <label for="">出版社</label> <input type="text" name="publish" class="form-control"> </div> <div class="form-group"> <label for="">是否出版</label> <select name="is_pub" id="" class="form-control"> <option value="1">已出版</option> <option value="0" selected="selected">未出版</option> </select> </div> <input type="submit" class="btn btn-success pull-right" value="新增"> </form> {% endblock content %}View Code
建立manage.html,它是管理頁面
{% extends 'base.html' %} {% block title %} <title>管理書籍</title> {% endblock title %} {% block content %} <h3>管理書籍</h3> <table class="table table-hover table-striped "> <thead> <tr> <th>名稱</th> <th>價格</th> <th>出版日期</th> <th>出版社</th> <th>是否出版</th> <th>刪除</th> <th>編輯</th> </tr> </thead> <tbody> {% for book in book_list %} <tr> <td>{{ book.title }}</td> <td>{{ book.price }}</td> <td>{{ book.pub_date|date:"Y-m-d" }}</td> <td>{{ book.publish }}</td> <td> {% if book.is_pub %} 已出版 {% else %} 未出版 {% endif %} </td> <td> <a href="/books/delete/{{ book.id }}" > <button type="button" class="btn btn-danger" data-toggle="modal" id="modelBtn">刪除</button> </a> </td> <td> <a href="/books/modify/{{ book.id }}"> <button type="button" class="btn btn-success" data-toggle="modal">編輯</button> </a> </td> </tr> {% endfor %} </tbody> </table> {% endblock content %}View Code
建立modify.html,它是修改頁面
{% extends 'base.html' %} {% block title %} <title>修改書籍</title> {% endblock title %} {% block content %} <h3>修改書籍</h3> <form action="" method="post"> {% csrf_token %} <div class="form-group"> <label for="">書籍名稱</label> <input type="text" name="title" class="form-control" value="{{ book.title }}"> </div> <div class="form-group"> <label for="">價格</label> <input type="text" name="price" class="form-control" value="{{ book.price }}"> </div> <div class="form-group"> <label for="">出版日期</label> <input type="date" name="pub_date" class="form-control" value="{{ book.pub_date|date:"Y-m-d" }}"> </div> <div class="form-group"> <label for="">出版社</label> <input type="text" name="publish" class="form-control" value="{{ book.publish }}"> </div> <div class="form-group"> <label for="">是否出版</label> <select name="is_pub" id="" class="form-control"> {% if book.is_pub %} <option value="1" selected="selected">已出版</option> <option value="0">未出版</option> {% else %} <option value="1">已出版</option> <option value="0" selected="selected">未出版</option> {% endif %} </select> </div> <input type="submit" class="btn btn-default pull-right" value="修改"> </form> {% endblock content %}View Code
啟動django專案,訪問首頁
新增一條資料
提示新增成功
效果如下:
點選左側的管理書籍
點選編輯按鈕,修改價格和日期
提示修改成功
檢視首頁,發現數據更改過來了!
2.查詢練習
答案:
1 查詢蘋果出版社過的價格大於200的書籍 Book.objects.filter(price__gt=200,publish="蘋果出版社",is_pub=True) 2 查詢2017年6月出版的所有以py開頭的書籍名稱 Book.objects.filter(title__startswith="py",pub_date__year=2017,pub_date__month=6) 3 查詢價格為50,100或者150的所有書籍名稱及其出版社名稱 Book.objects.filter(price__in=[50,100,150]).values("title","publish") 4 查詢價格在100到200之間的所有書籍名稱及其價格 Book.objects.filter(price__range=[100,200]).values("title","price") 5 查詢所有人民出版社出版的書籍的價格(從高到低排序,去重) Book.objects.filter(publish="人民出版社").order_by("prcie").reverse().values("prcie").distinct() 6 查詢價格大於200的書籍的個數 Book.objects.filter(price__gt=200).count() 7 查詢價格不等於100的所有書籍 Book.objects.exclude(prcie=100) 8 查詢蘋果出版社出版的書籍中的第3-7本(前提存在足夠數量的書籍) Book.objects.filter(publish="蘋果出版社")[2:7]View Code
昨日內容回顧:
單表操作 class Book(models.Model): title=models.CharField(max_length=32,unique=True) price=models.DecimalField(max_digits=8,decimal_places=2,null=True)# 999999.99 pub_date=models.DateField() publish=models.CharField(max_length=32) is_pub=models.BooleanField(default=True) def __str__(self): return self.title 新增記錄: 新增記錄方式1: Book.objects.create(title="三體",....) 新增記錄方式1: book=Book(title="三體",....) book.save() 查詢記錄: 查詢API: 1 Book.objects.all() # querysey [obj,....] 2 Book.objects.filter(title="三體") # querysey [obj,....] queryset.filter() # 返回值 queryset 3 Book.objects.exclude(title="三體") # querysey [obj,....] 4 Book.objects.all().first() # obj 5 Book.objects.all().last() # obj 6 Book.objects.all()[0] # obj 7 Book.objects.get(title="三體") # obj ################################################# 8 queryset.order_by() # 返回值 queryset 9 queryset.reverse() # 返回值 queryset 10 queryset.count() # 返回值 int (queryset的終止函式) Book.objects.all().filter(price__gt=100).order_by("pirce").count() 11 queryset.exist() # 返回值是布林值 ################################################# 12 queryset.values("price") # 返回值 queryset [{"price":123},{"price":124},{"price":13}] 13 queryset.valueslist("price") # 返回值 queryset [(123,),(124,),(345,)] 14 queryset.distinct("price") # 返回值 queryset 模糊查詢 __ Book.objects.filter(price__in=[100,200,300]) Book.objects.filter(price__gt=100) Book.objects.filter(price__lt=100) Book.objects.filter(price__range=[100,200]) Book.objects.filter(title__contains="x") Book.objects.filter(title__icontains="x") Book.objects.filter(title__startswith="py") Book.objects.filter(pub_date__year=2012)View Code
models.py裡面的Book類,就是一個model物件。
物件可以呼叫屬性欄位,queryset可以呼叫對應的方法
下面這種,就屬於鏈式操作。因為queryset可以呼叫API介面,只要前一個介面的返回值是queryset,它可以可以一直呼叫API介面,除非遇到返回值不是queryset的情況下,鏈式操作,才可以終止。因為count的返回值是int,所以到這裡,就結束了!不能再呼叫API介面了!
Book.objects.all().filter(price__gt=100).order_by("pirce").count()
distinct是去重操作,看下面的程式碼,執行是沒有意義的。因為每一條記錄,都是唯一的。
Book.objects.all().distinct()
icontains表示不區分大小寫。
三、Django多表建立
如何確定表關係?
表關係是在2張表之間建立的,沒有超過2個表的情況。
那麼相互之間有2條關係線,先來判斷一對多的關係。
如果其中一張表的記錄能夠對應另外一張表的多條記錄,那麼關係線成立!
如果只有一條線成立,那麼就是一對多的關係。
如果有2條線成立,那麼就是多對多的關係。
一對多
比如book和publish。一本書不能對應多個出版社(常規是這樣的,否則就盜版了),那麼不成立。
一個出版社可以對應多本書,關係線成立。所以book和publish表的關係是一對多的關係
多對多
多對多的關係,就是2張表互相對應多條記錄。
比如book和author。一本書可以有多個作者,一個作者可以寫多本!
一對一
一對一的關係,就很簡單了,彼此唯一。
比如author和authordetail是一對一的關係。
如何建立關聯
一對多:
一旦確定一對多的關係:在多的表中建立關聯欄位
多對多:
一旦確定多對多的關係:建立第三張關係表
一對一:
一旦確定一對一的關係 : 建立關聯欄位(任意一張表建立都可以)
一般情況下,在重要的表建立關聯欄位
3種表關係的演進
一對多
看下面一張book表
id | title | price | pub_date | publish |
1 | 西遊記 | 123 | 1743-04-12 | 蘋果出版社 |
如果需要查詢出版社的郵箱呢?再加一列
id | title | price | pub_date | publish | |
1 | 西遊記 | 123 | 1743-04-12 | 蘋果出版社 | [email protected] |
查詢出版社的地址呢?再加一列
id | tiitle | price | pub_date | publish | addr | |
1 | 西遊記 | 123 | 1743-04-12 | 蘋果出版社 | [email protected] | 北京 |
新增2條記錄
id | title | price | pub_date | publish | addr | |
1 | 西遊記 | 123 | 1743-04-12 | 蘋果出版社 | [email protected] | 北京 |
2 | 水滸傳 | 456 | 1743-04-12 | 蘋果出版社 | [email protected] | 北京 |
3 | 紅樓夢 | 678 | 1743-04-12 | 橘子出版社 | [email protected] | 北京 |
問題來了:出版社,郵箱,地址,都重複了!這邊非常浪費磁碟空間!
怎麼解決這個問題呢?那麼多表,就是為了解決問題,而誕生的。
將上面的單表,拆分成2張表。
book表,關聯欄位為publish_id
id | title | price | pub_date | publish_id |
1 | 西遊記 | 123 | 1743-04-12 | 1 |
2 | 水滸傳 | 456 | 1743-04-12 | 1 |
3 | 紅樓夢 | 678 | 1743-04-12 | 2 |
publish表
id | name | addr | |
1 | 蘋果出版社 | [email protected] | 北京 |
2 | 橘子出版社 | [email protected] | 南京 |
這樣就可以節省空間
舉例:查詢西遊記的出版社的郵箱
使用子查詢(一次查詢結果作為另一個查詢的條件)
多對多
book和author是多對多的關係
一本書可以有多個作者,一個作者可以寫多本!
常規做法,可能是這樣的
book表
id | title | price | pub_date | publish | author_id |
1 | 西遊記 | 123 | 1743-04-12 | 1 | 1,2 |
2 | 水滸傳 | 456 | 1743-04-12 | 1 | 1,2 |
author表
id | name | age | book_id |
1 | xiao | 23 | 1,2 |
2 | zhang | 24 | 1,2 |
上面的方案不好,為什麼呢?取欄位的值,還得用split方法,用逗號分隔。它的返回結果是列表,再對列表進行迴圈。這樣太麻煩了!所以,必須得建立第三張關係表
book表
id | title | price | pub_date | publish_id |
1 | 西遊記 | 123 | 1743-04-12 | 1 |
2 | 水滸傳 | 456 | 1743-04-12 | 2 |
author表
id | name | age | ad_id |
1 | xiao | 23 | 1 |
2 | zhang | 24 | 2 |
book_author(關係表)
關係表有且只有3個欄位,分別是id,book_id(book表主鍵),author_id(author表主鍵)
id | book_id | author_id |
1 | 1 | 1 |
2 | 1 | 1 |
3 | 2 | 2 |
4 | 2 | 2 |
舉例:查詢西遊記所有的作者
思路:先查詢西遊記的主鍵id,在關係表中查詢id=book_id。最後通過author_id查詢author表的作者!
一對一
author和authordetail是一對一的關係
一個作者對應唯一的詳細資訊
由於authordetail表是author表的延伸,所以在author表建立關聯欄位
author表
ad_id是關聯欄位,關聯欄位的值必須唯一,需要設定唯一屬性
id | name | age | ad_id |
1 | xiao | 25 | 1 |
2 | zhang | 26 | 2 |
authordetail表
gf表示女朋友,tel表示電話。
id | gf | tel |
1 | 趙麗穎 | 110 |
2 | 劉詩詩 | 111 |
看下面一張圖,這是5個表的關係圖
它是一個完整的系統,包含了3種表關係。
三、多表建立
建立模型
例項:我們來假定下面這些概念,欄位和關係
作者模型:一個作者有姓名和年齡。
作者詳細模型:把作者的詳情放到詳情表,包含生日,手機號,家庭住址等資訊。作者詳情模型和作者模型之間是一對一的關係(one-to-one)
出版商模型:出版商有名稱,所在城市以及email。
書籍模型: 書籍有書名和出版日期,一本書可能會有多個作者,一個作者也可以寫多本書,所以作者和書籍的關係就是多對多的關聯關係(many-to-many);一本書只應該由一個出版商出版,所以出版商和書籍是一對多關聯關係(one-to-many)。
有5個表,這裡面有3種關係,分別是一對一,一對多,多對多
正常的sql建表
#作者表 CREATE TABLE `author` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(32) NOT NULL, `age` int(11) NOT NULL, `ad_id` int(11) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ad_id` (`ad_id`), CONSTRAINT `author_ad_id_384abbeb_fk_authordetail_id` FOREIGN KEY (`ad_id`) REFERENCES `authordetail` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; #作者詳情表 CREATE TABLE `authordetail` ( `id` int(11) NOT NULL AUTO_INCREMENT, `gf` varchar(32) NOT NULL, `tel` varchar(32) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; #書籍表 CREATE TABLE `book` ( `id` int(11) NOT NULL AUTO_INCREMENT, `title` varchar(32) NOT NULL, `price` decimal(8,2) DEFAULT NULL, `pub_date` date NOT NULL, `publish_id` int(11) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `title` (`title`), KEY `book_publish_id_d96d3535_fk_publish_id` (`publish_id`), CONSTRAINT `book_publish_id_d96d3535_fk_publish_id` FOREIGN KEY (`publish_id`) REFERENCES `publish` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; #書籍和作者關係表 CREATE TABLE `book_authors` ( `id` int(11) NOT NULL AUTO_INCREMENT, `book_id` int(11) NOT NULL, `author_id` int(11) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `book_authors_book_id_author_id_36f1e11a_uniq` (`book_id`,`author_id`), KEY `book_authors_author_id_5acae95a_fk_author_id` (`author_id`), CONSTRAINT `book_authors_author_id_5acae95a_fk_author_id` FOREIGN KEY (`author_id`) REFERENCES `author` (`id`), CONSTRAINT `book_authors_book_id_19c7077f_fk_book_id` FOREIGN KEY (`book_id`) REFERENCES `book` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; #出版社表 CREATE TABLE `publish` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(32) NOT NULL, `email` varchar(32) NOT NULL, `addr` varchar(32) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;View Code
使用ORM建立表
ORM如果遇到關聯欄位,會自動新增_id字尾
一對一關係表
authordetail表
class AuthorDetail(models.Model): gf=models.CharField(max_length=32) tel=models.CharField(max_length=32)View Code
authordetail表雖然沒有定義id欄位,但是ORM建立表時,會自動新增id欄位,設定主鍵。欄位型別為int,設定自增屬性!
author表
class Author(models.Model): name=models.CharField(max_length=32) age=models.IntegerField() # 與AuthorDetail建立一對一的關係 # ad=models.ForeignKey(to="AuthorDetail",to_field="id",on_delete=models.CASCADE,unique=True) ad=models.OneToOneField(to="AuthorDetail",to_field="id",on_delete=models.CASCADE,)View Code
OneToOneField 表示建立一對一關係。
to 表示需要和哪張表建立關係
to_field 表示關聯欄位
on_delete=models.CASCADE 表示級聯刪除。假設a表刪除了一條記錄,b表也還會刪除對應的記錄。
ad表示關聯欄位,但是ORM建立表的時候,會自動新增_id字尾。那麼關聯欄位為ad_id
注意:建立一對一關係,會將關聯字新增唯一屬性。比如:ad_id
一對多關係表
publish表
class Publish(models.Model): name=models.CharField(max_length=32) email=models.CharField(max_length=32