{"path":"51966","ref":"dynamic_url.md","title":"Dynamic URL","content":"# Dynamic URL\n\n除了在首頁顯示文章的摘要外,通常也會希望每篇文章能有獨立的網址與頁面。例如,我們可能會希望 `http:\/\/127.0.0.1\/post\/5\/` 能夠是 **id=5 **那篇文章的網址,而頁面內容則是此篇日記的詳細資訊,而非摘要。\n\n在這個章節,我們會學到如何設定動態網址的 URLConf。讓每篇旅遊日記,擁有獨一無二的網址與頁面。\n\n### 建立單篇文章的 View\n\n首先建立單篇文章所使用的 View Function。在 *trips\/views.py* 中新增 **post_detail** 這個 View 如下:\n\n~~~\n# trips\/views.py\n\ndef post_detail(request, id):\n post = Post.objects.get(id=id)\n return render(request, 'post.html', {'post': post})\n~~~\n> 我們以訪客瀏覽 `http:\/\/127.0.0.1:8000\/post\/5\/` 的例子,來解釋以上程式:\n\n- \n** 目前瀏覽文章的 id 會傳入 View 中:** 當訪客瀏覽 `http:\/\/127.0.0.1\/post\/5\/` 時,傳入 View 的 id 會是 5。\n\n- URL 與 id 的對應,會在稍後設定。這裡只需知道 View 中傳入的,會是當前瀏覽文章 id 即可。\n\n- \n** 取得傳入 id 的那篇 Post 資料:** 當傳入的 id=5,代表訪客想看到 id=5 那篇文章。我們可以利用之前學過的 ORM 語法 `get`, 取得該篇日記的 **Post** 物件:\n\n~~~\n post = Post.objects.get(id=id) # 此時 id = 5\n~~~\n\n- ** 回傳 HttpResponse:** 將取得的 post ( *id=5* ) 傳入 Template *( post.html )*,並呈現 Render 後的結果。\n\n### 設定動態網址的對應\n\n日記單頁的 View Function 完成後,我們來設定網址與 View 的對應。修改 *mysite\/urls.py* ,加入以下內容:\n\n~~~\n# mysite\/urls.py\nfrom trips.views import hello_world, home, post_detail\n\nurlpatterns = patterns('',\n ...\n url(r'^post\/(?P\\d+)\/$', post_detail, name='post_detail'),\n)\n~~~\n\n上面的修改完成後,只要連至`http:\/\/127.0.0.1\/post\/5\/` 就會對應到 `post_detail()` 這個 View,並且**傳入的 id = 5** 。\n\n### 使用 Regex 提取部份 URL 為參數\n\n我們前面提過,Django 的 URL 是一個 *Regular Expression (Regex)*。Regular expression 可用來描述一個字串的樣式。 除了可以表示固定字串之外,還可以用來表示不確定的內容。我們一步一步解釋文章單頁所使用的 URL 設定:\n\n~~~\n(?P\\d+)\n~~~\n\n1. \n`\\d` 代表一個阿拉伯數字。\n\n1. \n`+` 代表「一個以上」。\n\n 所以 `\\d+` 代表一個以上的阿拉伯數字,例如「0」、「99」、「12345」。可是像「8a」就不符合,因為「a」不是數字。\n\n1. \n`(?P)` 代表「把這一串東西抓出來,命名為 id。\n\n 所以 `(?P\\d+)` 代表:抓出一個以上阿拉伯數字,並把抓出來的東西取名為 id。\n\n所以 `r'^post\/(?P\\d+)\/$'` 會達成以下的效果:\n\n| URL | 符合結果 | \n|-----|-----|\n| [http:\/\/127.0.0.1\/](http:\/\/127.0.0.1\/)**posts\/** | 不符合,因為前面不是 **post\/** 開頭。 | \n| [http:\/\/127.0.0.1\/](http:\/\/127.0.0.1\/)**post\/** | 不符合,因為後面抓不到數字。 | \n| [http:\/\/127.0.0.1\/](http:\/\/127.0.0.1\/)**post\/1\/** | 符合,抓到的 id 是 1。 | \n| [http:\/\/127.0.0.1\/](http:\/\/127.0.0.1\/)**post\/1234\/** | 符合,抓到的 id 是 1234。 | \n| [http:\/\/127.0.0.1\/](http:\/\/127.0.0.1\/)**post\/12ab\/** | 不符合,因為後面有不是數字的東西。 | \n\n### 建立單篇日記頁的 Template\n\n回顧一下之前寫的 View Function ( *post_detail* ) 的內容\n\n~~~\nreturn render(request, 'post.html', {'post': post})\n~~~\n\n我們取得所需 post 物件後,傳入 `post.html` 這個 template 中 render,現在我們就來完成這個 Template。建立 *post.html* 如下:\n\n~~~\n\n\n\n\n\n \n A Django Girl's Adventure<\/title>\n <link href=\"\/\/fonts.googleapis.com\/css?family=Lemon\" rel=\"stylesheet\" type=\"text\/css\">\n <link href=\"\/\/maxcdn.bootstrapcdn.com\/font-awesome\/4.2.0\/css\/font-awesome.min.css\" rel=\"stylesheet\" type=\"text\/css\">\n <link href=\"\/\/djangogirlstaipei.github.io\/assets\/css\/style.css\" rel=\"stylesheet\" type=\"text\/css\">\n<\/head>\n<body>\n <div class=\"header\">\n <h1 class=\"site-title text-center\">\n <a href=\"\/\">A Django Girl's Adventure<\/a>\n <\/h1>\n <\/div>\n <div class=\"container post post-detail\">\n <div class=\"post-heading\">\n <h1 class=\"title\"><a href=\"{% url 'post_detail' id=post.id %}\">{{ post.title }}<\/a>\n <\/h1>\n <div class=\"date\">{{ post.created_at|date:'Y \/ m \/ d' }}<\/div>\n <\/div>\n <div class=\"location\">\n <i class=\"fa fa-map-marker\"><\/i>\n <span id=\"location-content\">{{ post.location }}<\/span>\n <\/div>\n <div id=\"map-canvas\" class=\"map\"><\/div>\n <div class=\"post-content\">\n {{ post.content }}\n <\/div>\n <hr class=\"fancy-line\">\n <img class=\"photo\" src=\"{{ post.photo }}\" alt=\"\">\n <\/div>\n <script src=\"\/\/maps.googleapis.com\/maps\/api\/js?v=3.exp&libraries=places&sensor=false\"><\/script>\n <script src=\"\/\/djangogirlstaipei.github.io\/assets\/js\/map.js\"><\/script>\n<\/body>\n<\/html>\n~~~\n\n**說明:**\n\n- \n將 post 物件的屬性 (e.g. 標題、內文、時間......等),利用 `{{ var }}` 與 Template Filter 顯示並格式化於 HTML 中\n\n- \n若資料庫裡有 id=5 的 Post,現在連至 [http:\/\/127.0.0.1:8000\/post\/5\/](http:\/\/127.0.0.1:8000\/post\/5\/) 即可看到此日記的單頁\n\n### 加入到單篇日記頁的連結\n\n最後,我們還需在首頁加上單篇日記的連結。我們可以使用 `{% url %}`這個 Template Tag 達成,需要加入的地方有:\n\n1. 每篇日記的標題\n1. 每篇日記的 Read More 按鈕\n\n##### {% url %}\n\n連結到特定 View 的 Template Tag\n\n使用方法:\n\n| 語法 | 說明 | \n|-----|-----|\n| {% url '`<view_name>`' %} | 使用在 urls.py 中設定的 name | \n\n也可以傳入參數,如:\n\n~~~\n{% url '<view_name>' arg1=<var1> arg2=<var2> ...%}\n~~~\n\n其餘用法可參考 [Template Tag: **url**](https:\/\/docs.djangoproject.com\/en\/1.7\/ref\/templates\/builtins\/#url)\n\n現在我們開始加入詳細頁的連結,需要加入的地方有\n\n1. 每篇日記\n1. 每篇日記的 Read More 按鈕\n\n#### 設定標題連結\n\n打開 *home.html*,找到下面的內容:\n\n~~~\n<!-- home.html -->\n\n<h2 class=\"title\">\n <a href=\"#\">{{ post.title }}<\/a>\n<\/h2>\n~~~\n\n將它改成\n\n~~~\n<!-- home.html -->\n\n<h2 class=\"title\">\n <a href=\"{% url 'post_detail' id=post.id %}\">{{ post.title }}<\/a>\n<\/h2>\n~~~\n\n#### 設定 Read More 按鈕的連結\n\n在 *home.html* 中找到以下內容:\n\n~~~\n<!-- home.html -->\n\n<a class=\"read-more\" href=\"#\">\n Read More <i class=\"fa fa-arrow-right\"><\/i>\n<\/a>\n~~~\n\n修改如下:\n\n~~~\n<!-- home.html -->\n\n<a class=\"read-more\" href=\"{% url 'post_detail' id=post.id %}\">\n Read More <i class=\"fa fa-arrow-right\"><\/i>\n<\/a>\n~~~\n\n### 驗收成果\n\n連至 [http:\/\/127.0.0.1:8000\/](http:\/\/127.0.0.1:8000\/) ,現在只要點擊各個日記的標題或 **Read more** 按鈕,就會顯示那該篇日記的詳細頁面。\n\n![Django Girls Trips - Post page](https:\/\/box.kancloud.cn\/2015-08-18_55d2cc1ac1833.png)"}