該項目主要練習使用Django開發一個Book單表的增刪改查頁面,經過這個項目鞏固本身這段時間學習Django知識。css
開發一個簡單的Book增刪改查頁面 要求: 實現一個書籍的增刪改查功能便可 儘可能規範化代碼 查詢操做: 1,查找A出版社出版過的書籍價格大於100 2,查詢某月出版的全部python書籍名稱 3,查詢價格爲100,或者150的全部書籍名稱及其出版社名稱 4,查詢價格在100-200之間的全部書籍名稱及其價格 5,查詢全部A出版社出版的書籍價格,(降序排列,去重)
編碼規範需求:15% 1. 代碼規範遵照pep8 (https://python.org/dev/peps/pep-0008/) 2. 函數有相應的註釋 3. 程序有文檔說明文件(README.md參考:https://github.com/csrftoken/vueDrfDemo) 4. 程序的說明文檔必須包含的內容:程序的開發環境(django版本)、程序的實現的功能、程序的啓動方式、登陸用戶信息、程序的運行效果 5. 程序設計的流程圖:
1,建立project django-admin startproject Book_single 2,建立APP python manage.py startapp app01 3,settings配置設置模板 TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'template')], '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', ], }, }, ] 4,將APP添加到settings.py裏面 INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01' ] 5,設置時區和語言 Django默認使用美國時間和英語,在項目的settings文件中,以下圖所示: LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True 咱們將其改成 亞洲/上海 時間和中文 LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Shanghai' USE_I18N = True USE_L10N = True USE_TZ = False
這裏將全局配置的urls.py文件和APP下面的urls.py分開寫。(本身在APP下面建立一個urls.py)這樣作的好處:html
from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('app01/', include('app01.urls')), ]
name的做用就是對本身的URL進行命名,讓本身可以處於Django的任意處,尤爲是模板內顯式的引用它,這是一個很是強大的功能,至關於給URL取了一個全局變量名,不會將url匹配地址寫死。前端
from django.urls import path, re_path from app01 import views app_name = 'app01' urlpatterns = [ path('books/', views.books, name='books'), path('addbooks/', views.addbooks, name='addbooks'), path('<int:id>/delete', views.delbook, name='delbook'), path('<int:id>/change/', views.changebook, name='changebook'), path('query/', views.query, name='query'), path('test/', views.test, name='test') ]
在二級路由(即APP的URls文件中),在urlpatterns後,應該加上app_name='app_name',不然有可能會發生報錯。vue
做爲一個單表增刪改查操做項目,很明顯,咱們至少須要一個Book表,用來保存下面信息:圖書標題,價格,出版日期,出版社等等,代碼以下:html5
Book_single/app01/models.pypython
from django.db import models # Create your models here. class Book_single(models.Model): id = models.AutoField(primary_key=True) title = models.CharField(max_length=32, unique=True) price = models.DecimalField(max_digits=8, decimal_places=2) pub_date = models.DateField() publish = models.CharField(max_length=32) def __str__(self): return self.title
各字段含義:mysql
在settings中修改DATABASES:jquery
1,註銷掉下面設置 # Database # https://docs.djangoproject.com/en/2.1/ref/settings/#databases # DATABASES = { # 'default': { # 'ENGINE': 'django.db.backends.sqlite3', # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), # } # } 2, 新增下面設置 import pymysql pymysql.install_as_MySQLdb() DATABASES = { 'default':{ 'ENGINE': 'django.db.backends.mysql', 'NAME': 'django', 'USER': 'root', 'PASSWORD': '123456', 'HOST': '127.0.0.1', 'PORT': '3306', } }
python manage.py makemigrations python manage.py migrate
結果展現:git
D:\Django\Book_single>python manage.py makemigrations Migrations for 'app01': app01\migrations\0001_initial.py - Create model Book_single D:\Django\Book_single>python manage.py migrate Operations to perform: Apply all migrations: admin, app01, auth, contenttypes, sessions Running migrations: Applying app01.0001_initial... OK
因爲是單表操做,並且視圖函數比較簡單,因此咱們不須要初步規劃視圖函數,直接寫便可。github
Book_single/app01/views.py
from django.shortcuts import render, HttpResponse, redirect # Create your views here. from app01 import models def books(request): book_list = models.Book_single.objects.all() return render(request, 'app01/books.html', locals()) def addbooks(request): # title = 'python1' # price = 100 # publish = '機械出版社' # pub_date = '2019-3-3' # book_obj = models.Book_single.objects.create(title=title, price=price, # publish=publish, pub_date=pub_date) # book_obj.save() # return HttpResponse("OK") 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') if title == '' or price == '' or pub_date == '' or publish == '': return render(request, 'app01/addbook.html', {'ret': '全部選項不能爲空'}) models.Book_single.objects.create(title=title, price=price, pub_date=pub_date, publish=publish) return redirect('/app01/books') else: return render(request, 'app01/addbook.html') def delbook(request, id): # return HttpResponse("OK") print(models.Book_single.objects.filter(id=id)) models.Book_single.objects.filter(id=id).delete() # 兩次請求 return redirect('/app01/books/') def changebook(request, id): book_obj = models.Book_single.objects.filter(id=id).first() print(book_obj) 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') if title == '' or price == '' or pub_date == '' or publish == '': return render(request, 'app01/addbook.html', {'ret': '全部選項不能爲空'}) models.Book_single.objects.filter(id=id).update(title=title, price=price, pub_date=pub_date, publish=publish) return redirect('/app01/books/') else: return render(request, 'app01/change.html', {'book_obj': book_obj}) def query(request): # 1, 查詢Publish1出版過價格大於100的書籍 book_list = models.Book_single.objects.filter(publish='Publish1', price__gt=100) print(book_list) # 2,查詢2019年1月出版的全部以py開頭的數據名稱 book_list = models.Book_single.objects.filter(title__startswith='py', pub_date__year=2019, pub_date__month=1).values('title') print(book_list) # 3,查詢價格爲50,100 或者150 的全部書籍的名稱及其出版社名稱 book_list = models.Book_single.objects.filter(price__in=[50, 100, 150]).values('title', 'publish') print(book_list) # 4,查詢價格在100到200之間全部書籍名稱及其價格 book_list = models.Book_single.objects.filter(price__range = [100, 200]).values('title', 'price') print(book_list) # 5, 查詢全部Publish1出版的書籍的價格(由高到底排序,去重) book_list = models.Book_single.objects.filter(publish='Publish1').values('price').distinct().order_by('-price') print(book_list) return HttpResponse('OK') def test(request): return HttpResponse("OK")
先不填充內容,建立便可。在項目的根路徑下建立一個template目錄,而後在template目錄下建立一個app01目錄,在裏面建立HTML,以下:
Bootstrap3.3.7的下載地址:請點擊我
JQuery 的下載地址:請點擊我
注意:{% static '相對路徑' %} 這個Django爲咱們提供的靜態文件加載方法,能夠將頁面與靜態文件連接起來。
根目錄下新建一個static目錄,並將解壓後的bootstrap-3.3.7目錄,總體拷貝到static目錄中,並且因爲Bootstrap依賴於JQuery,因此咱們須要引入JQuery,而且在static目錄下,新建一個CSS和JS目錄,做爲之後的樣式文件和JS文件的存放地,建立展現圖以下:
而後打開項目的settings文件,在最下面添加配置,用於指定靜態文件的搜索目錄:
STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static'), ]
既然要將前端頁面作的像個樣子,那麼就要各寫各的。一個網站要有本身的統一風格和公共部分,能夠將這部份內容集中到一個基礎模板base.html中。如今在根目錄下的template中新建一個base.html文件做爲站點的基礎模板。
在Bootstrap文檔中,爲咱們提供了一個很是簡單並且又實用的基礎模板,代碼以下:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 上述3個meta標籤*必須*放在最前面,任何其餘內容都*必須*跟隨其後! --> <title>Bootstrap 101 Template</title> <!-- Bootstrap --> <link href="css/bootstrap.min.css" rel="stylesheet"> <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!--[if lt IE 9]> <script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script> <script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script> <![endif]--> </head> <body> <h1>你好,世界!</h1> <!-- jQuery (necessary for Bootstrap's JavaScript plugins) --> <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script> <!-- Include all compiled plugins (below), or include individual files as needed --> <script src="js/bootstrap.min.js"></script> </body> </html>
將其總體拷貝到base.html文件中。
而後修改base內容,最後base.html文件的內容以下:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- 上述3個meta標籤*必須*放在最前面,任何其餘內容都*必須*跟隨其後! --> {% block title %} <title>base</title> {% endblock title %} <!-- Bootstrap --> <link href="/static/bootstrap-3.3.7/css/bootstrap.min.css" rel="stylesheet"> <!-- HTML5 shim 和 Respond.js 是爲了讓 IE8 支持 HTML5 元素和媒體查詢(media queries)功能 --> <!-- 警告:經過 file:// 協議(就是直接將 html 頁面拖拽到瀏覽器中)訪問頁面時 Respond.js 不起做用 --> <!--[if lt IE 9]> <script src="https://cdn.bootcss.com/html5shiv/3.7.3/html5shiv.min.js"></script> <script src="https://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script> <![endif]--> <link rel="stylesheet" href="/static/CSS/base.css"> </head> <body> <div class="container my-con"> <div class="col-md-2"> {% block operation %} <h2>operation</h2> {% endblock operation %} </div> <div class="col-md-10"> {% block con %} <h2>content</h2> {% endblock con %} </div> </div> <!-- jQuery (Bootstrap 的全部 JavaScript 插件都依賴 jQuery,因此必須放在前邊) --> <script src="/static/JS/jquery-3.2.1.min.js"></script> <!-- 加載 Bootstrap 的全部 JavaScript 插件。你也能夠根據須要只加載單個插件。 --> <script src="/static/bootstrap-3.3.7/js/bootstrap.min.js"></script> </body> </html>
{% extends 'base.html' %} {% block title %} <title>addbook</title> {% endblock title %} {% block operation %} <h2>添加書籍</h2> {% endblock operation %} {% block con %} <form action="" method="post"> {% csrf_token %} <div class="form-group"> <label for="title">書籍名稱</label> <input type="text" class="form-control" id="title" name="title"> </div> <div class="form-group"> <label for="price">價格</label> <input type="text" class="form-control" id="price" name="price"> </div> <div class="form-group"> <label for="pub-date">出版日期</label> <input type="text" class="form-control" id="pub-date" name="pub-date"> </div> <div class="form-group"> <label for="publish">出版社</label> <input type="text" class="form-control" id="publish" name="publish"> </div> <button type="submit" class="btn btn-success pull-right">提交</button> </form> <p style="color: red;">{{ ret }}</p> {% endblock con %}
{% extends 'base.html' %} {% block title %} <title>books</title> {% endblock title %} {% block operation %} <h2>查看書籍</h2> {% endblock operation %} {% block con %} <a href="{% url 'app01:addbook' %}" class='btn btn-primary' role='button'>添加書籍</a> <div class="table-responsive"> <table class="table table-striped table-bordered table-hover"> <thead> <tr class="active"> <td><strong>書籍名單</strong></td> <td><strong>價格</strong></td> <td><strong>出版日期</strong></td> <td><strong>出版社</strong></td> <td><strong>刪除操做</strong></td> <td><strong>編輯操做</strong></td> </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><a href="/app01/{{ book.pk }}/delete" class="btn btn-danger" role="button" >刪除</a></td> <td><a href="/app01/{{ book.pk }}/change" class="btn btn-info" role="button" >編輯</a></td> </tr> {% endfor %} </tbody> </table> </div> {% endblock con %}
{% extends 'base.html' %} {% block title %} <title>change</title> {% endblock title %} {% block operation %} <h2>編輯書籍</h2> {% endblock operation %} {% block con %} <form action="" method="post"> {% csrf_token %} <div class="'form-group"> <label for="title">書籍名稱</label> <input type="text" class="form-control" id="title" name="title" value="{{ book_obj.title }}"> </div> <div class="'form-group"> <label for="price">價格</label> <input type="text" class="form-control" id="price" name="price" value="{{ book_obj.price }}"> </div> <div class="'form-group"> <label for="pub-date">出版日期</label> <input type="text" class="form-control" id="pub-date" name="pub-date" value="{{ book_obj.pub_date|date:'Y-m-d' }}"> </div> <div class="'form-group"> <label for="publish">出版社</label> <input type="text" class="form-control" id="publish" name="publish" value="{{ book_obj.publish }}"> </div> <button type="submit" class="btn btn-success pull-right">提交</button> </form> <p style="color: red;">{{ ret }}</p> {% endblock con %}
從上面的HTML代碼,咱們能夠發現出現了許多模板語言。最多的就是使用大括號和百分比的組合來表示。
下面學習一下標籤的使用。標籤看起來像下面的格式。可是標籤比變量更加複雜:一些在輸出中建立文本,一些經過循環或者邏輯來控制流程,一些加載其後的變量將使用到的額外信息到模板中。一些標籤須要開始和結束標籤等等。
csrf_token 標籤,用於生成csrf_token的標籤,用於防治跨站攻擊驗證。注意若是你的view的index使用的是render_to_response方法,則不會生效。
其實這隻會生成一個input 標籤,和其餘表單標籤一塊兒提交給後臺。
在實際應用中,咱們將用到Django模板系統來建立整個HTML頁面。這就帶來了一個常見的Web開發問題:在整個網站中,如何減小共有頁面區域(好比站點導航)所引發的重複和冗餘代碼?
解決該問題的傳統作法是使用服務器端的includes,咱們能夠在HTML頁面中使用該指令將一個網頁嵌入到另外一箇中。事實上,DJango經過 {% include %} 支持了這種方法。
可是Django解決此類問題的首先方法是使用更加優雅的策略——模板繼承。
本質上來講,模板繼承就是先構造一個基礎框架模板,然後在其子模板中對它所包含站點共用部分和定義塊進行重載。
讓咱們經過修改 current_datetime.html 文件,爲 current_datetime 建立一個更加完整的模板來體會一下這種作法:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <title>The current time</title> </head> <body> <h1>My helpful timestamp site</h1> <p>It is now {{ current_date }}.</p> <hr> <p>Thanks for visiting my site.</p> </body> </html>
這看起來很好,可是若是咱們要爲hours_ahead視圖建立另外一個模板會發生什麼呢?
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <title>Future time</title> </head> <body> <h1>My helpful timestamp site</h1> <p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p> <hr> <p>Thanks for visiting my site.</p> </body> </html>
很顯然,咱們剛纔重複了大量的HTML代碼。詳細一下,若是有一個更典型的網站,它由導航條,樣式表,可能還會有一些JavaScript 代碼,事情必將以向每一個模板填充各類冗餘的HTML而了結。
解決這個問題的服務器端include方案是找出兩個模板中的共同部分,將其保存爲不一樣的模板片斷,而後在每一個模板中進行include。也許你能夠把模板頭部的一些代碼保存爲header.html 文件:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head>
你可能也會將底部保存到文件 footer.html:
<hr> <p>Thanks for visiting my site.</p> </body> </html>
對基於include 的策略,頭部和底部的包含很簡單。麻煩的是中間部分。在此範例中,每一個頁面都有一個My helpful timestamp site</h1> 標題,可是這個標題不能放在 header.html 中,由於每一個頁面的 <title> 是不一樣的。 若是咱們將 <h1> 包含在頭部,咱們就不得不包含 <title> ,但這樣又不容許在每一個頁面對它進行定製。 何去何從呢?
Django的模板繼承系統解決了這些問題,你能夠將其視爲服務器端include的逆向思惟版本。你能夠對那些不一樣的代碼進行定義,而不是共同代碼段。
第一步是定義基礎模板,該框架以後將由子模板所繼承。如下是咱們目前所講述範例的基礎模板:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <title>{% block title %}{% endblock %}</title> </head> <body> <h1>My helpful timestamp site</h1> {% block content %}{% endblock %} {% block footer %} <hr> <p>Thanks for visiting my site.</p> {% endblock %} </body> </html>
這個叫作base.html 的模板定義了一個簡單的HTML框架文檔,咱們將本站點的全部頁面中使用。子模板的做用就是重載,添加護着保留那些塊的內容。(這個文件能夠保存在template目錄下,命令爲base.html)
咱們將使用模板標籤{% block %} 。 全部的 {% block %} 標籤告訴模板引擎,子模板能夠重載這些部分。 每一個{% block %}標籤所要作的是告訴模板引擎,該模板下的這一塊內容將有可能被子模板覆蓋。
如今咱們已經有了一個基本模板,咱們能夠修改 current_datetime.html 模板來 使用它:
{% extends "base.html" %} {% block title %}The current time{% endblock %} {% block content %} <p>It is now {{ current_date }}.</p> {% endblock %}
再爲hours_ahead 視圖建立一個模板,看起來是這樣的:
{% extends "base.html" %} {% block title %}Future time{% endblock %} {% block content %} <p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p> {% endblock %}
看起來很漂亮是否是?每一個模板只包含對本身而言獨一無二的代碼。無需多餘的部分。若是想要對站點級進行設計修改,僅僅須要修改base.html,全部其餘模板會當即反映出所作的修改。
如下是其工做方式:
在加載 current_datetime.html 模板時,模板引擎發現了 {% extends %} 標籤, 注意到該模板是一個子模板。 模板引擎當即裝載其父模板,即本例中的 base.html 。此時,模板引擎注意到 base.html 中的三個 {% block %} 標籤,並用子模板的內容替換這些 block 。所以,引擎將會使用咱們在 { block title %} 中定義的標題,對 {% block content %} 也是如此。 因此,網頁標題一塊將由{% block title %}替換,一樣地,網頁的內容一塊將由 {% block content %}替換。
注意因爲子模板並無定義 footer 塊,模板系統將使用在父模板中定義的值。 父模板 {% block %} 標籤中的內容老是被看成一條退路。繼承並不會影響到模板的上下文。 換句話說,任何處在繼承樹上的模板均可以訪問到你傳到模板中的每個模板變量。你能夠根據須要使用任意多的繼承次數。 使用繼承的一種常見方式是下面的三層法:
<1> 建立 base.html 模板,在其中定義站點的主要外觀感覺。 這些都是不常 修改甚至從不修改的部分。 <2> 爲網站的每一個區域建立 base_SECTION.html 模板(例如, base_photos.html 和 base_forum.html )。這些模板對base.html 進行拓展,幷包含區域特定的風格與設計。 <3> 爲每種類型的頁面建立獨立的模板,例如論壇頁面或者圖片庫。 這些模板拓展 相應的區域模板。
這個方法可最大限度的重用代碼,並使得向公共區域(如區域級的導航)添加內容稱爲一件輕鬆的工做。
<1>若是在模板中使用 {% extends %} ,必須保證其爲模板中的第一個模板標記。 不然,模板繼承將不起做用。 <2>通常來講,基礎模板中的 {% block %} 標籤越多越好。 記住,子模板沒必要定義 父模板中全部的代碼塊,所以你能夠用合理的缺省值對一些代碼塊進行填充,而後只對 子模板所需的代碼塊進行(重)定義。 俗話說,鉤子越多越好。 <3>若是發覺本身在多個模板之間拷貝代碼,你應該考慮將該代碼段放置到父模板的 某個 {% block %} 中。若是你須要訪問父模板中的塊的內容,使用 {{ block.super }} 這個標籤吧,這一個魔法變量將會表現出父模板中的內容。 若是隻想在上級代碼塊基 礎上添加內容,而不是所有重載,該變量就顯得很是有用了。 <4>不容許在同一個模板中定義多個同名的 {% block %} 。 存在這樣的限制是因 爲block 標籤的工做方式是雙向的。 也就是說,block 標籤不只挖了一個要填的坑,也定義了在父模板中這個坑所填充 的內容。若是模板中出現了兩個相同名稱的 {% block %} 標籤,父模板將無從得知 要使用哪一個塊的內容。
在數據庫中刪除了一張表,從新執行python manage.py migrate 時報錯,提示不存在這張表
主要是由於Django通常在第一次遷移的時候新建表,後面都不會新建表,而是隻檢查字段等等的變化,因此咱們既然已經刪除了這張表,django檢查這張表的字段變化的時候天然報錯了。
解決方法仍然是執行數據庫遷移命令
python manage.py makemigrations python manage.py migrate
只不過在執行上面代碼以前,把第一次執行遷移建立的那個記錄刪除掉,不然它檢查到已經執行過第一次了,那麼它後面就不會建立表了。
在該APP模塊下,有一個migtations文件夾,除了前兩個文件外,其餘的文件都刪除,其實每一次若是有變化的話,這邊就會生成一個文件,下面的這個001_initial.py看名字就知道是第一次遷移的時候生成的,也就是由於有它的存在,因此之後每次再執行就再也不建立表了。
其次,在數據庫裏面也有相應的記錄,也要刪除。咱們仔細看看數據庫裏面存的是什麼,在Django_migrations裏面,這表裏面存的都是每次遷移的記錄,固然記錄的是什麼模塊以及對應的文件名字,好比咱們這裏的模塊是dtheme,這裏的文件名叫作001_initial,和咱們文件夾裏面是一一對應的,一樣,刪除這條記錄。
而後在執行數據庫遷移命令便可。
注意:若是這個APP模塊下面還有其餘的model的話,那麼其餘的model建立的表也要刪除掉,至關於咱們這樣的解決方案是針對整個APP模塊的,要執行就會所有從新生成,否則會提示部分表已經存在錯誤。
這個問題,在MySQL中修改配置文件。
具體請參考博客:請點擊我
上面代碼咱們用到了正則匹配,下面學習一下正則匹配的用法。
(若是想全面學習正則表達式的使用:請點擊我)
這裏介紹與Django有關的知識點。咱們最經常使用的正則匹配就是下面兩個:
^ 表示會匹配行或者字符串的起始位置,有時還會匹配整個文檔的起始位置 $ 表示匹配行或者字符串的結尾
Django在檢查URL模式以前,移除每個申請的URL開頭的斜槓(/)。這意味着咱們寫的URL模式不用包括斜槓(/)。
舉個例子:
from django.urls import path, re_path from app01 import views urlpatterns = [ path('login/<int:id>/', views.login), # 上下兩個是一樣的效果 re_path(r'login/(\d+)/', views.login), ]
在新版Django2.X中,URL的路由表示用path 和re_path代替,模塊的導入由Django1.X版本的url變爲Django2.X中的path,re_path。
看下面兩幅圖:
並且該url() 函數傳遞了四個參數,兩個必需:regex和view,以及兩個可選:kwargs 和 name。也就是說正則表達式和視圖是兩個必填參數。
函數path() 具備四個參數,兩個必需參數 :route 和view,兩個可選參數:kwargs 和 name。即路由和視圖是必填參數。
因此二者的主要區別就在於url() 是要寫正則表達式(regex)的路由,而path()時寫非正則路由(route),接下來主要看一下path() 函數的四個參數含義。
1,path() 參數: route route 是一個匹配URL的準則(相似於正則表達式),當Django響應一個 請求時,它會從urlpaterns的第一項開始,按順序依次匹配列表中的項,直到 找到匹配的項。 這些準則不會匹配GET 和 POST 參數或域名。例如:URLconf在處理請求 https://www.example.com.myapp/時候,它會嘗試匹配myapp/。處理請 求https://www.example.com/myapp/?page=3時,也只會嘗試匹配myapp/ 2, path()參數: view 當django找到一個匹配的準則,就會調用這個特定的視圖函數,並傳入一個 HttpRequest對象做爲第一個參數,被「捕獲」的參數以關鍵字參數的形式傳入。 3, path()參數:kwargs 任意個關鍵字參數能夠做爲一個字典傳遞給目標視圖函數 4, path()參數: name 爲你的URL取名能使你在Django的任意地方惟一的引用它,尤爲是在模板中。 這個有用的特性運行你只改一個文件就能全局的修改某個URL模式。
上面介紹的path中,第一個參數route使用的是非正則表達式能夠表示的普通路由路徑。
注意:
默認狀況下,如下路徑轉換器可用:
好比要匹配一個視圖中的函數路由,該函數有兩個形參:
def peopleList(request,book_id)
第一個request是默認的,那麼路由自動匹配該函數的第二個形參,匹配格式:<int:book_id>,並返回一個正整數或者零值。
urlpatterns = [ path('booklist/', views.booklist, name='booklist'), path('<int:book_id>/', views.peopleList, name='peopleList'), ]
而若是趕上路徑和轉換器語法都不足以定義的URL模式,那麼就須要使用正則表達式,這時候就須要使用re_path() ,而非path()。
from django.urls import re_path
在Python正則表達式中 ,命名正則表達式組的語法是(?P<name>pattern),組name的名稱,而且pattern是要匹配的模式。
仍是以上面的爲例,採用正則表達式寫下來,以下:
re_path(r'^(\d+)/$',views.peopleList,name='peopleList'), # 相似於下面 path('<int:book_id>/', views.peopleList, name='peopleList'),
這樣也能夠匹配到views視圖中的peopleList函數的形參。
因此這兩種使用方式在使用上根據實際狀況自行使用。
1,re_path和path的做用都是同樣的。只不過re_path是在寫url的時候能夠用正則表達式,功能更增強大。
2,寫正則表達式都推薦使用原生字符串,也就是r開頭的字符串
3,在正則表達式中定義變量,須要使用圓括號括起來。這個參數是由名字的,那麼須要使用(?P<參數的名字>)。而後在後面添加正則表達式的規則。
4,若是不是特別需求,直接使用path就夠了,省的將代碼搞的很麻煩(由於正則表達式是很是晦澀的,特別是一些比較複雜的正則表達式,可能今天寫的明天就忘了),除非是URL中確實是須要使用正則表達式來解決才使用re_path。
urlpatterns = [ re_path(r"^list/(?P<year>\d{4})/$", views.article_list), re_path(r"^list/(?P<month>\d{2})/$", views.article_list_month) ]
第一個表達式以list開頭,中間須要有4個數字,一個都不能多也不能少,再以'/'結尾。形如list/2019/這樣的字符串才能被識別,同理,第二句是須要形如 list/02/這樣的字符串才能被識別。
增長成功,直接跳轉到查看界面
6.5 建立的數據庫以下:
傳送門:請點擊我(小編的GitHub)
感受上面代碼加上GitHub的代碼,就不須要展現了。。。。。。
算了,這裏仍是放上APP的urls.py
from django.urls import path, re_path from app01 import views app_name = 'app01' urlpatterns = [ path('books/', views.books, name='books'), path('addbooks/', views.addbooks, name='addbooks'), path('<int:id>/delete', views.delbook, name='delbook'), path('<int:id>/change/', views.changebook, name='changebook'), path('query/', views.query, name='query'), path('test/', views.test, name='test') ]
和views.py裏面的刪除和修改函數:
def delbook(request, id): # return HttpResponse("OK") print(models.Book_single.objects.filter(id=id)) models.Book_single.objects.filter(id=id).delete() # 兩次請求 return redirect('/app01/books/') def changebook(request, id): book_obj = models.Book_single.objects.filter(id=id).first() print(book_obj) 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') if title == '' or price == '' or pub_date == '' or publish == '': return render(request, 'app01/addbook.html', {'ret': '全部選項不能爲空'}) models.Book_single.objects.filter(id=id).update(title=title, price=price, pub_date=pub_date, publish=publish) return redirect('/app01/books/') else: return render(request, 'app01/change.html', {'book_obj': book_obj})
爲何重點放這裏呢,固然主要是由於本身犯錯了,這裏須要注意的。
由於這裏有一個匹配視圖中函數路由的函數,裏面又兩個形參:
path('<int:id>/delete', views.delbook, name='delbook'), path('<int:id>/change/', views.changebook, name='changebook'),
第一個request是默認的,因此路由會自動匹配該函數的第二個形參,匹配格式爲<int:id> , 並返回一個正整數或者零值。
(這裏也能夠用re_path進行匹配,這裏附上兩個相似的代碼)
urlpatterns1 = [ path('<int:id>/delete', views.delbook, name='delbook'), path('<int:id>/change/', views.changebook, name='changebook'), # 等同於下面 re_path(r'^(\d+)/delete', views.delbook, name='delbook'), re_path(r'^(\d+)/change', views.changebook, name='changebook'), ]
在settings.py中,添加配置,用於指定靜態文件的搜索目錄,注意代碼以下:
STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static'), ]
注意 將STATICFILES 不能寫錯!!!
book_list = models.Book_single.objects.all()
經過上面獲取數據,拿到數據後,渲染給前端;前端經過for循環的方式,取出數據。
目的就是經過Book_single(這個數據表)裏面的字段拿到對應的數據。
models.Book_single.objects.filter(id=id).delete()
經過上面的代碼,找到數據庫中對應的id字段,刪除這個ID字段對應的這行數據。
book_obj = models.Book_single.objects.filter(id=id).first()
經過上面代碼,拿到須要編譯的ID,而後在數據庫中找到對應的ID字段,而後對內容進行修改。
修改完以後,對數據庫對應字段的內容進行更新。
models.Book_single.objects.filter(id=id).update(title=title, price=price, pub_date=pub_date, publish=publish)
查詢部分代碼比較簡單,這裏回顧一下。
若是不懂:請點擊這裏
def query(request): # 1, 查詢Publish1出版過價格大於100的書籍 book_list1 = models.Book_single.objects.filter(publish='Publish1', price__gt=100) # 2, 查詢2019年1月出版的全部以pyth開頭的書籍名稱 book_list2 = models.Book_single.objetcs.filter(title__startwith='pyth', pub_date__year=2019, pub_date__month=1).values('title') # 3, 查詢價格爲50, 100或者 150 的全部書籍的名稱及其出版社名稱 book_list3 = models.Book_single.objects.filter(price__in[50, 100, 150]).values ('title, 'publish') #4, 查詢價格在100到200之間全部書籍名稱及其價格 book_list4 = models.Book_single.objects.filter(price__range= [100, 200]).values ('title', 'price') #5,查詢全部Publish1出版的書籍的價格(由高到低排序,去重) book_list5 = models.Book_single.objects.filter(publish="Publisher1").values ('price').distinct().order_by('-price')