1. 程式人生 > >python程式設計:從入門到實踐學習筆記-基於Django框架的Web開發-設計樣式和部署(二)

python程式設計:從入門到實踐學習筆記-基於Django框架的Web開發-設計樣式和部署(二)

部署學習筆記

接下來我們將使用Heroku(基於Web的平臺)管理Web應用程式的部署。

建立Heroku賬戶

訪問https://signup.heroku.com註冊一個帳號。

安裝Heroku Toolbelt

安裝Heroku Toolbelt,對部署到Heroku伺服器的專案進行管理。訪問https://toolbelt.heroku.com/進行安裝。

安裝必要的包

在虛擬環境中使用pip安裝dj-database-url、dj-static、gunicorn,幫助在伺服器上支援django專案提供的服務。
其中dj-database-url幫助django與Heroku使用的資料庫進行通訊,dj-static幫助django正確的管理靜態檔案,而gunicorn是一個伺服器軟體,能夠在線上環境中支援應用程式提供的服務。

建立包含列表的檔案requirements.txt

Heroku需要知道專案依賴那些包,因此我們使用pip來生成一個檔案,其中列出依賴包。在虛擬環境中執行pip freeze > requirements.txt.

#requirements.txt
dj-database-url==0.5.0
dj-static==0.0.6
Django==1.11.7
django-bootstrap3==9.1.0
gunicorn==19.7.1
pytz==2018.3
static3==0.7.0

我們部署時,Heroku將會安裝以上列出的所有包。接著我們在包列表中新增 psycopg2,它幫助Heroku管理活動資料庫。

#requirements.txt
dj-database-url==0.5.0
dj-static==0.0.6
Django==1.11.7
django-bootstrap3==9.1.0
gunicorn==19.7.1
pytz==2018.3
static3==0.7.0
psycopg2>=2.6.1

指定Python版本

如果沒有指定python的版本,heroku將使用當前預設的版本。在虛擬環境中執行python --version.

接著在manage.py所在的資料夾 中仙劍一個名為runtime.txt:

#runtime.txt
python-3.6.4

記住小寫和減號!

為部署到Heroku而修改settings.py

在settings.py末尾新增

#settings.py
#--snip--
# Heroku設定
if os.getcwd() == '/app':
    import dj_database_url
    DATABASES = {
        'default': dj_database_url.config(default='postgres://localhost')
    }   
         
    # 讓request.is_secure()承認X-Forwarded-Proto頭
    SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
    
    # 支援所有的主機頭(host header) 
    ALLOWED_HOSTS = ['*']

    # 靜態資產配置
    BASE_DIR = os.path.dirname(os.path.abspath(__file__))
    STATIC_ROOT = 'staticfiles'
    STATICFILES_DIRS = (
        os.path.join(BASE_DIR, 'static'),
    )

首先我們使用了函式getcwd(),用來獲取當前的工作目錄。在Heroku部署中,這個目錄總是/app。在本地部署中,通常是專案資料夾的名稱。
這個if測試確保僅當專案被部署到Heroku時,才執行這個程式碼塊。這種結構讓我們能夠將同一個設定檔案用於本地開發環境和線上伺服器。
接著我們匯入了dj_database_url,用於在Heroku配置伺服器。Heroku使用PostgreSQL作為資料庫。這些設定對專案進行配置,使其在Heroku上使用PostgreSQL。其他設定思維作用分別是:支援HTTPS請求;讓djang能夠使用Heroku的URL來提供服務;設定專案,使其能夠在Heroku上正確提供靜態檔案。

建立啟動程序的Procfile

在manage.py的檔案中建立Procfile(無後綴)

web: gunicorn learning_log.wsgi --log-file -

這行程式碼讓Heroku將gunicorn用作伺服器,並使用learning_log/wsgi.py裡的設定來啟動應用程式。標誌log-file告訴Heroku應將那些型別的事件寫入日誌。

為部署到Heroku而修改wsgi.py

修改wsgi.py

import os
from django.core.wsgi import get_wsgi_application
from dj_static import Cling

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "learning_log.settings")

application = Cling(get_wsgi_application())

我們匯入了幫助正確地提供靜態檔案的Cling,並使用他來啟動應用程式。

建立用於儲存靜態檔案的目錄

在目錄
接著在manage.py的同一目錄下建立資料夾static,並在static資料夾中新建一個placeholder.txt當作佔位檔案,因為專案被推送到Heroku時不會包含空資料夾。

This file ensures that learning_log/static/ will be added to the project.
Django will collect static files and place them in learning_log/static/.

使用Git跟蹤專案檔案

1.安裝Git
如果你安裝了Heroku Toolbelt,則裡面已經包含Git了。
2.配置Git
執行

  • git config --global user.name "ehmatthes"

  • git config --global user.email "[email protected]"

3.忽略檔案
記住不要上傳資料庫檔案!!!在manage.py所在的目錄建立一個名為.gitignore的檔案。

venv/
__pycache__/
*.sqlite3

4.提交專案

命令git init表示在“學習筆記”所在的目錄中初始化一個空倉庫。命令git add .表示將未被忽略的檔案都新增到這個倉庫。命令git status輸出表明當前位於分支master中,而工作目錄是乾淨的。

推送到Heroku

在虛擬環境中執行

接著執行命令heroku ps

接著執行命令heroku open

在Heroku上建立資料庫

為建立線上資料庫,我們需要再次執行命令migrate。要對Heroku專案執行django和python命令,可使用heroku run命令

現在可以將這個應用程式的URL分享給你的小夥伴了~

改進Heroku部署

在這部分,我們將建立超級使用者來改進部署。為了專案更安全,將DEBUG設定為False,防止他人看到錯誤資訊。
1.在Heroku上建立超級使用者

2.在Heroku上建立對使用者友好的URL

確保專案的安全

修改settings.py

# Heroku設定
if os.getcwd() == '/app':
    import dj_database_url
    DATABASES = {
        'default': dj_database_url.config(default='postgres://localhost')
    }    
    # 讓request.is_secure()承認X-Forwarded-Proto頭
    SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
    
    # 只允許Heroku託管這個專案
    ALLOWED_HOSTS = ['learning-log.herokuapp.com']

    DEBUG = False

    # 靜態資產配置
    BASE_DIR = os.path.dirname(os.path.abspath(__file__))
    STATIC_ROOT = 'staticfiles'
    STATICFILES_DIRS = (
        os.path.join(BASE_DIR, 'static'),
    )

提交併推送修改

現在將settings.py的修改提交到Git倉庫,在將修改推送到Heroku。

Heroku發現倉庫發生了變化,因此重建專案,但不會重建資料庫,所以不用在遷移資料庫。

建立自定義錯誤頁面

當用戶遇到404或者500的錯誤時,會返回錯誤頁面。404表示程式碼沒錯,但請求的物件不存在。500表示程式碼有問題。現在我們將自定義錯誤頁面。
1.建立自定義模板
在learning_log/learning_log中新建一個 templates資料夾,建立404.html、505.html

#404.html
{% extends "learning_logs/base.html" %}

{% block header %}
  <h2>The item you requested is not available. (404)</h2>
{% endblock header %}
#500.html
{% extends "learning_logs/base.html" %}

{% block header %}
  <h2>The item you requested is not available. (404)</h2>
{% endblock header %}

這些新檔案要求對settings.py做修改。

#--snip--
TEMPLATES = [
    {        
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'learning_log/templates')],        
        'APP_DIRS': True,
        #--snip--
    },
]
#--snip--

2.在本地檢視錯誤頁面
本地的settings.py進行修改

#--snip--
# 安全警告,不要在線上環境中啟用除錯
DEBUG = False
ALLOWED_HOSTS = ['localhost']
#--snip--

DUBUG
被設定False時,必須在ALLOWED_HOSTS中指定一個主機。
檢視錯誤頁面之後,將DUBUG
設定True,方便進一步開發。(在settings.py中用於Heroku部署的部分中,確保DEBUG = False)。
3.將修改推送到Heroku

4.使用方法get_object_or_404()
現在當用戶手動請求不存在的主題或條目,將導致500錯誤。這種情形應該視為404錯誤合適一點。因此我們使用函式get_object_or_404()。修改views.py

#--snip--
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponseRedirect, Http404
#--snip--

@login_required
def topic(request, topic_id):
    """顯示單個主題及其所有的條目"""
    topic = get_object_or_404(Topic, id=topic_id)
    # 確定主題屬於當前使用者    #--snip--

修改之後記得提交。

繼續開發

首先,對本地專案做必要的修改,如果在修改過程中建立了新檔案,使用命令git add .將他們加入到Git倉庫中。如果有修改要求遷移資料庫,也需要執行這個命令,因為每個遷移都將生成新的遷移檔案。
然後,使用命令git commit -am "commit message"將修改提交到倉庫,在使用命令git push heroku master將修改推送到Heroku。如果你在本地遷移了資料庫,也需要遷移線上資料庫。為此,你可以執行一次性命令heroku run python manage.py migrate,也可以使用heroku run bash開啟一個遠端終端,在其中執行python manage.py migrate。然後訪問線上專案,確認修改生效。

設定SECRET_KEY

Django根據settings.py中設定SECRET_KEY的值來實現大量的安全協議。

將專案從Heroku刪除

1.在官網上刪除。
2.執行heroku apps:destroy --app appname

本學習筆記到尾聲啦,如果遇到什麼解決不了的問題可以在評論提出來哦。
即將再見!

2018.3.31
修復筆記entry.text文字過長導致的溢位。現已自動換行。
開啟base.html並新增

<!--snip-->
    <title>Learning Log</title>

    {% bootstrap_css %}
    {% bootstrap_javascript %}
    <style>
      div.lbreak{word-wrap: break-word;}
    </style>

  </head>
<!--snip-->

開啟topic.html並新增

<!--snip-->
      <div class="panel-body">
        <div class='lbreak'>{{ entry.text|linebreaks }}</div>
      </div>
    </div> <!-- panel -->
<!--snip-->

done!