LoginSignup
18
31

More than 5 years have passed since last update.

静的ファイルを扱うDjangoアプリをPyCharmで作ってSECRET_KEYは秘匿しつつGitHub経由でHerokuにデプロイする

Last updated at Posted at 2018-12-09

はじめに

Pythonプログラミング歴7ヶ月の中で、Djangoで作ったWebアプリケーションをHerokuにデプロイすることは何度も実施してきましたが、DjangoやHerokuの知識を少しづつ得ていく中で手順も変遷していったため、自身の最新版として一度まとめてみることにしました。

3ヶ月後の自分でも「あれはどうするんだったっけ?:thinking:」とならずに問題無くデプロイを行えるようにするために作成しましたが、私以外のどなたかの役にも立てば幸いです。

この記事で取り扱うこと

  • 仮想環境(Virtualenv)を使ったプロジェクトをPyCharm CEで新規作成する方法
  • django-herokuまたはdjango-toolbeltを使ってHerokuのデプロイに必要なパッケージをインストールする方法
  • Djangoで静的ファイル(CSS)を使うための設定
  • GitHubのリモートリポジトリを経由してHerokuに自動デプロイする方法
  • settings.pyのSECRET_KEYをGitHubに公開しないようにする方法

前提

  • Pythonをインストール済み
  • PyCharm CE(無料版)をインストール済み
  • GitHubのアカウントを取得済み
  • Herokuのアカウントを取得済み
  • Gitをインストール済み

環境

  • macOS High Sierra 10.13.6
  • Python 3.7.0
  • Django 2.1.4
  • PyCharm 2018.1.4(Community Edition)

この記事固有の用語

似たような名称が出てきて私以外の方には紛らわしいと思うので、以下にまとめました。

  • sample : PyCharmプロジェクトを配置したディレクトリ名
  • django_pj : Djangoプロジェクトを配置したディレクトリ名
  • hello : 作成したDjangoアプリケーション名
  • hello_django : 作成したGitHub上のリモートリポジトリ名
  • django201812 : 作成したHerokuのアプリケーション名

1. PyCharmプロジェクトの作成

1.1. PyCharm CEの起動

PyCharmを起動し、以下の画面でCreate New Projectを選択します。

スクリーンショット 2018-12-04 3.20.16.png

1.2. PyCharmプロジェクトの設定

次にPyCharmプロジェクトの設定を行います。

スクリーンショット 2018-12-04 3.07.12.png

  1. 一番左上のLocationでPyCharmプロジェクトのディレクトリを指定します。ここでは〜/PycharmProjects/sampleとしています。

  2. Project Interpreterの左にある▽マークをクリックします。すると3以降のメニューが表示されます。

  3. 今回は仮想環境を新規作成するのでNew environment usingを選択します。また、仮想環境としてVirtualenvを選択します。

  4. 中段のLocationで仮想環境を作成するディレクトリを指定します。今回は、さきほど指定したPycharmプロジェクトディレクトリsampleの配下にmyvenvという名前で仮想環境を作成することにします。そのため、〜/PycharmProjects/sample/myvenvと指定します。

  5. Base interpreterでPythonのバージョンを指定します。ここではPython3.7にしています。

全て入力したら、画面右下のCreateを選択します。

スクリーンショット 2018-12-04 3.27.30.png

2. PyCharmのターミナルの起動

PyCharmのプロジェクトの作成が完了すると、以下のような画面が表示されます。
次に、各種の必要なコマンドを入力する為にPyCharmのターミナルを起動することにします。
画面下部のTerminalを選択します。

スクリーンショット 2018-12-08 20.41.08.png

すると、以下のようなターミナル画面が表示されます。この画面に各種のコマンドを入力していきます。

スクリーンショット 2018-12-08 20.48.48.png

3. Djangoのインストール

3.1. 仮想環境の確認

ターミナル画面には(myvenv)と表示されていますが、これはmyvenvという名前の仮想環境を起動させている状態であることを示しています。

(myvenv) sample $ 

以降はこの仮想環境myvenvにDjango等をインストールしていきますが、まず仮想環境myvenvに現時点で何がインストールされているかを確認してみます。

pip listと入力します。

(myvenv) sample $ pip list
Package    Version
---------- -------
pip        10.0.1 
setuptools 39.1.0 
You are using pip version 10.0.1, however version 18.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
(myvenv) sample $

すると、仮想環境myvenvにインストールされているパッケージが表示されます。

なお、上の実行結果のように

You are using pip version XX.X, however version XX.X is available.
You should consider upgrading via the 'pip install --upgrade pip' command.`

と表示されている場合は、pipの最新バージョンをインストールすることが推奨されているので、記載の通り、
pip install --upgrade pip
を実行してpipの最新バージョンをインストールします。

(myvenv) sample $ pip install --upgrade pip
Collecting pip
  Using cached https://files.pythonhosted.org/packages/c2/d7/90f34cb0d83a6c5631cf71dfe64cc1054598c843a92b400e55675cc2ac37/pip-18.1-py2.py3-none-any.whl
Installing collected packages: pip
  Found existing installation: pip 10.0.1
    Uninstalling pip-10.0.1:
      Successfully uninstalled pip-10.0.1
Successfully installed pip-18.1
(myvenv) sample $

3.2. Djangoとその他必要なパッケージのインストール

DjangoアプリケーションをHerokuにデプロイするためには、Django以外にいくつかのパッケージが必要です。

django-herokuまたはdjango-toolbeltをインストールすると、Django本体とその他の必要なパッケージがまとめてインストールされるので便利なのですが、それぞれ一部足りないパッケージがあるので、不足分も併せてインストールする方法をそれぞれ以下に記載します。

パターンその1 django-herokuとgunicornをインストール

(myvenv) sample $ pip install django-heroku gunicorn
(myvenv) sample $ pip list
Package         Version
--------------- -------
dj-database-url 0.5.0 # 追加 
Django          2.1.4 # 追加 
django-heroku   0.3.1 # 追加 
gunicorn        19.9.0 # 追加
pip             18.1   
psycopg2        2.7.6.1 # 追加
pytz            2018.7 # 追加
setuptools      39.1.0 
whitenoise      4.1.2 # 追加 

パターンその2 django-toolbeltとwhitenoiseをインストール

(myvenv) sample $ pip install django-toolbelt whitenoise
(myvenv) sample $ pip list
Package         Version
--------------- -------
dj-database-url 0.5.0 # 追加 
dj-static       0.0.6 # 追加 
Django          2.1.4 # 追加 
django-toolbelt 0.0.1 # 追加 
gunicorn        19.9.0 # 追加
pip             18.1   
psycopg2        2.7.6.1 # 追加
pytz            2018.7 # 追加
setuptools      39.1.0 
static3         0.7.0 # 追加 
whitenoise      4.1.2 # 追加 

4. Djangoプロジェクト用のディレクトリの作成

PyCharmプロジェクトのディレクトリsampleの配下に、Djangoプロジェクトのディレクトリを作成します。ここではディレクトリ名をdjango_pjとします。

ターミナル画面からコマンドでディレクトリを作成することもできますが、ここではPyCharmのGUIを使ってディレクトリやファイルを作成する方法を説明します。

PyCharmでは、以下のようにディレクトリsampleを右クリックし、NEWを選択すると、ディレクトリsample配下にファイルやディレクトリなどを新規作成できます。
今回はディレクトリを作成するので、Directoryを選択します。

スクリーンショット 2018-12-08 22.38.30.png

すると、以下のウインドウが表示されるので、ディレクトリ名としてdjango_pjを入力し、OKを選択します。

スクリーンショット 2018-12-08 22.48.39.png

PyCharmプロジェクトのディレクトリsample配下に、Djangoプロジェクトのディレクトリdjango_pjが作成されました。

スクリーンショット 2018-12-08 22.59.18.png

5. Djangoプロジェクトの作成

ターミナル画面で、django_pjディレクトリに移動します。

(myvenv) sample $ cd django_pj

django_pjに移動したら、django-admin.py startproject config .を実行して、Djangoプロジェクトを作成します。

なお、configの部分はどんな名前でも構いませんが、Djangoプロジェクト全体の設定に関わるファイルが配置されるディレクトリの名称となるので、ここではconfigとしています。

(myvenv) django_pj $ django-admin.py startproject config .

Djangoプロジェクトが作成された結果、ディレクトリ・ファイル構成は以下のようになりました。

(myvenv) django_pj $ tree
.
├── config # 追加
│   ├── __init__.py # 追加
│   ├── settings.py # 追加
│   ├── urls.py # 追加
│   └── wsgi.py # 追加
└── manage.py # 追加

6. adminなどのDjango標準アプリケーション用のテーブルを開発環境に作成する

(myvenv) django_pj $ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying sessions.0001_initial... OK
(myvenv) django_pj $ 

7. 言語を日本語に、タイムゾーンを日本時間に変更する

config/settings.py
LANGUAGE_CODE = 'ja' # 変更

TIME_ZONE = 'Asia/Tokyo' # 変更

8. Djangoアプリケーションを構成する基本的なファイルを作成する

Djangoアプリケーションを構成するファイルを作成するため、python manage.py startapp (アプリ名)を実行します。
アプリ名は、ここではhelloとしています。

(myvenv) django_pj $ python manage.py startapp hello

helloディレクトリ配下に各種ファイルが作成されました。

(myvenv) django_pj $ tree
.
├── config
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── hello # 追加
│   ├── __init__.py # 追加
│   ├── admin.py # 追加
│   ├── apps.py # 追加
│   ├── migrations # 追加
│   │   └── __init__.py # 追加
│   ├── models.py # 追加
│   ├── tests.py # 追加
│   └── views.py # 追加
└── manage.py

9. DjangoアプリケーションをDjangoプロジェクトで有効にする

さきほど作成したDjangoアプリケーションhelloのディレクトリ配下のapps.pyを見ると、HelloConfigクラスが作成されていることが確認できます。

hello/apps.py
from django.apps import AppConfig


class HelloConfig(AppConfig):
    name = 'hello'

このHelloConfigクラスをsettings.pyのINSTALLED_APPSに追加することで、helloアプリがDjangoプロジェクト内で有効になります。

config/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'hello.apps.HelloConfig', # 追加
]

10. Djangoアプリケーションの作成 〜 テンプレートとcssを使ってHello Django!を表示する 〜

Djangoアプリケーションの具体的な内容はどのようなものでも構わないのですが、今後テンプレートファイルと静的ファイルを取り扱えるよう、それらを使ったアプリを作ることにします。

10.1. Djangoがテンプレートを探しに行くディレクトリを指定する

config/settings.py
TEMPLATES = [
    {
        'BACKEND': 'django.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',
            ],
        },
    },
]

10.2. Djangoがcssなどの静的ファイルを探しに行くディレクトリを指定する

config/settings.py
STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')] # 追加
STATIC_ROOT = 'staticfiles' # 追加

10.3. テンプレートの作成

テンプレートを配置するディレクトリを作成する

django_pjディレクトリの配下に、templatesディレクトリ、さらにその配下にhelloディレクトリを作成します。

(myvenv) django_pj $ mkdir -p templates/hello

テンプレートファイルを作成する

(myvenv) django_pj $ touch templates/hello/index.html

テンプレートファイルを編集する

templates/hello/index.html
{% load static %}
<!DOCTYPE html>
<html lang="ja">

<head>
  <link rel="stylesheet" href="{% static 'css/hello.css' %}">
</head>

<body>
  <h2 class="hello">Hello Django!</h2>
</body>

</html>

10.4. cssの作成

cssを配置するディレクトリを作成する

django_pjディレクトリの配下に、staticディレクトリ、さらにその配下にcssディレクトリを作成します。

(myvenv) django_pj $ mkdir -p static/css

cssのファイルを作成する

(myvenv) django_pj $ touch static/css/hello.css

cssを編集する

static/css/hello.css
.hello {
  color: red;
}

10.5. ビューを編集する

hello/views.py
from django.views.generic import TemplateView


class IndexView(TemplateView):
    template_name = 'hello/index.html'

10.6. ルーティングを設定する

helloアプリのディレクトリにurls.pyを新規作成します。

(myvenv) django_pj $ touch hello/urls.py
hello/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.IndexView.as_view(), name='index'),
]

次にDjangoプロジェクト全体のルーティング設定に対して、先ほど作成したhelloアプリのルーティング設定を組み込みます。

config/urls.py
from django.contrib import admin
from django.conf.urls import include  # 追加
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('hello.urls')),  # 追加
]

10.7. 開発環境でのHello Djangoの表示確認

開発サーバーの起動

(myvenv) django_pj $ python manage.py runserver

表示確認

http://127.0.0.1:8000にブラウザでアクセスすると、以下のように赤い色でHello Django!と表示されます。

スクリーンショット 2018-12-09 14.37.52.png

もし赤く表示されていなければstatic/css/hello.cssの読み込みに失敗しているので、

  • settings.py
  • template/hello/index.html
  • static/css/hello.css

の内容を確認してください。

11. Herokuへのデプロイに必要なファイルを準備する

11.1. requirements.txtの作成

(myvenv) django_pj $ pip freeze > requirements.txt
requirements.txt
dj-database-url==0.5.0
Django==2.1.4
django-heroku==0.3.1
gunicorn==19.9.0
psycopg2==2.7.6.1
pytz==2018.7
whitenoise==4.1.2

11.2. Procfileの作成

(myvenv) django_pj $ touch Procfile
Procfile
web: gunicorn config.wsgi

configの部分は、wsgi.pyの存在するディレクトリ名になります。ディレクトリ名がconfigでない場合は、適宜変更してください。

11.3. runtime.txtの作成

(myvenv) django_pj $ touch runtime.txt

Pythonのバージョンを記載します。

runtime.txt
python-3.7.0

11.4. 開発環境用のlocal_settings.pyを作成する

(myvenv) django_pj $ touch config/local_settings.py
config/local_settings.py

import os

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

DEBUG = True

SECRET_KEY = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'

SECRET_KEYの実際の内容は、settings.pyに書かれている内容と同じにしてください。

11.5. settings.pyをHeroku環境用に編集する

SECRET_KEYの削除

SECRET_KEYをGitHub上で公開しないよう、削除します(後で、環境変数から読み込むよう設定します)。

config/settings.py
SECRET_KEY = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' # この行を削除  

DEBUG, ALLOWED_HOSTSの変更

config/settings.py
DEBUG = False # 変更

ALLOWED_HOSTS = ['*'] # 変更

Whitenoiseを利用するための設定

Whitenoiseのバージョンが4以降の場合は、以下の通り設定します。

config/settings.py
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware', # 追加
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

データベース設定の変更

config/settings.py

# 以下を削除
'''
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
'''

# 代わりに以下を追加
import dj_database_url
DATABASES = {
    'default': dj_database_url.config(),
}

開発環境ではlocal_settings.pyを読み込み、Heroku環境ではSECRET_KEYを環境変数から読み込むよう設定

settings.pyの最終行に以下を追記します。

config/settings.py
try:
    from .local_settings import *
except ImportError:
    pass

if not DEBUG:
    SECRET_KEY = os.environ['SECRET_KEY']

local_settings.pyを置かないHeroku環境では、local_settings.py内のDEBUG = Trueを通らないので、DEBUG = Falseのままとなり、SECRET_KEYを環境変数から読み込みます。

Herokuの環境変数にSECRET_KEYを設定する方法は、後ほど説明します。

12. GitHub上にリモートリポジトリを作成する

GitHub上にリモートリポジトリを作成します。
リポジトリ名は、ここではhello_djangoにしました。
Create reopsitoryを選択すると、リモートリポジトリが作成されます。

スクリーンショット 2018-12-09 9.08.47.png

13. Herokuの設定

13.1. Herokuアプリケーションの作成

Heroku上にアプリケーションを作成します。
アプリケーション名は、ここではdjango201812にしました。
Create appを選択すると、アプリケーションが作成されます。

スクリーンショット 2018-12-09 9.16.51.png

13.2. HerokuのアプリケーションとGitHubのリモートリポジトリを連携させる

Herokuアプリケーションdjango201812Deployの画面で、HerokuとGitHubを連携させるための設定を行います。

スクリーンショット 2018-12-09 9.19.53.png

上記の画面で、

  1. GitHubを選択します
  2. さきほどGitHubで作成したリモートリポジトリ名を入力します
  3. Searchを選択します
  4. リモートリポジトリ名が表示されたらConnectを選択します

すると、以下の画面が表示されます。

スクリーンショット 2018-12-09 9.23.21.png

これで、Herokuアプリケーションdjango201812とGitHubのリモートリポジトリhello_djangoが連携されるようになりました。

13.3. GitHubのリモートリポジトリにpushしたら、自動でHerokuにデプロイするよう設定する

同じく、Herokuアプリケーションdjango201812Deployの画面でEnable Automatic Deploysを選択します。

スクリーンショット 2018-12-09 9.28.35.png

すると、以下の表示になります。

スクリーンショット 2018-12-09 9.29.37.png

これで、GitHubのリモートリポジトリhello_djangopushすると、Herokuのアプリケーションdjango201812が自動でデプロイされるようになりました。

13.4. SECRET_KEYを環境変数に設定する

Herokuアプリケーションdjango201812settingsの画面で、環境変数の設定を行います。

以下の画面で、Reveal Config Varsを選択します。

スクリーンショット 2018-12-09 11.30.12.png

すると、環境変数の名前とその値を入力する画面が表示されるので、

  1. 左側のフォームにSECRET_KEYと入力
  2. 右側のフォームに、もともとはsettings.pyに記述されていて、いまはlocal_settings.pyに記述しているSECRET_KEYの値を入力(前後のシングルクォーテーションは入力不要です)
  3. Addを選択

します。

スクリーンショット 2018-12-09 11.31.56.png

これで、Herokuアプリケーションdjango201812上に環境変数SECRET_KEYが設定されました。

14. ローカルリポジトリの作成

ここからは、PyCharmのターミナル画面に戻って、ローカルリポジトリの作成を行います。

14.1. .gitignoreの作成

GitHub上のリモートリポジトリで管理する必要の無いファイルやディレクトリを指定します。

(myvenv) django_pj $ touch .gitignore
.gitignore
myvenv
__pycache__
staticfiles
local_settings.py
db.sqlite3
*.py[co]$

14.2. ローカルリポジトリの作成

以下を順次実行していきます。

(myvenv) django_pj $ git init
Initialized empty Git repository in /Users/xxxxxxxx/PycharmProjects/sample/django_pj/.git/
(myvenv) django_pj $ git add .
(myvenv) django_pj $ git commit -m "first commit"
[master (root-commit) 35b4c58] first commit
 20 files changed, 224 insertions(+)
 create mode 100644 .DS_Store
 create mode 100644 .gitignore
 create mode 100644 Procfile
 create mode 100644 config/__init__.py
 create mode 100644 config/settings.py
 create mode 100644 config/urls.py
 create mode 100644 config/wsgi.py
 create mode 100644 hello/__init__.py
 create mode 100644 hello/admin.py
 create mode 100644 hello/apps.py
 create mode 100644 hello/migrations/__init__.py
 create mode 100644 hello/models.py
 create mode 100644 hello/tests.py
 create mode 100644 hello/urls.py
 create mode 100644 hello/views.py
 create mode 100755 manage.py
 create mode 100644 requirements.txt
 create mode 100644 runtime.txt
 create mode 100644 static/css/hello.css
 create mode 100644 templates/hello/index.html

14.3. このローカルリポジトリとGitHub上のリモートリポジトリを紐付ける

git remote add origin GitHub上のリモートリポジトリのURLを実行します。

GitHub上のリモートリポジトリのURLは、GitHubのリモートリポジトリのCode画面から確認できます。

スクリーンショット 2018-12-09 14.51.49.png

(myvenv) django_pj $ git remote add origin https://github.com/shonansurvivors/hello_django.git

15. GitHub上のリモートリポジトリへのPushと、Herokuへの自動デプロイを行う

15.1 GitHub上のリモートリポジトリへのPush

git push -u origin masterを実行します。

(myvenv) django_pj $ git push -u origin master
Counting objects: 27, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (18/18), done.
Writing objects: 100% (27/27), 4.20 KiB | 2.10 MiB/s, done.
Total 27 (delta 0), reused 0 (delta 0)
remote: 
remote: Create a pull request for 'master' on GitHub by visiting:
remote:      https://github.com/shonansurvivors/hello_django/pull/new/master
remote: 
To https://github.com/shonansurvivors/hello_django.git
 * [new branch]      master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.

成功すると、GitHubのリモートリポジトリhello_djangoCodeの画面から、各種ファイルが登録されていることが確認できます。

スクリーンショット 2018-12-09 14.11.33.png

15.2 Heokuへの自動デプロイ結果の確認

Herokuアプリケーションdjango201812の管理画面右上にあるOpen appを選択します。

スクリーンショット 2018-12-09 14.16.32.png

すると、Herokuアプリケーションdjango201812がブラウザで表示されます。

スクリーンショット 2018-12-09 14.43.42.png

開発環境と同じく赤い文字でHello Django!と表示されていればデプロイ成功です。

おつかれさまでした:relieved:

15.3 うまく表示されない場合

デプロイやビルドが失敗している可能性があります。

Herokuアプリケーションdjango201812Activity画面で、デプロイやビルドに失敗した時のログを見ることができるので、そこから原因を調査してください。

以下の画面例ではビルドが失敗しており、View build logを選択するとそのログを確認できます。

スクリーンショット 2018-12-09 11.21.16.png

参考

DjangoBrothers BLOG - DjangoアプリをHerokuでデプロイする手順

18
31
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
31