【Mac】Django アプリ作成 & MySQL 初回 migrate まで

  1. Python 仮想環境の作成
  2. Django プロジェクト&アプリケーションの作成
  3. MySQL へ初回 migrate
  4. 管理ユーザーの作成

Python 仮想環境の作成

任意の場所に Python 仮想環境を作成、起動します。

% python3 -m venv justjam
% cd justjam
% source bin/activate

Django プロジェクト&アプリケーションの作成

仮想環境内で django をインストール、そしてプロジェクトとアプリケーションを作成します。

% pip install --upgrade pip
% pip install django
% django-admin startproject justjam_proj
% cd justjam_proj
% python manage.py startapp api

runserver を実行し、ブラウザで動作確認します。

% python manage.py runserver

MySQL へ初回 migrate

settings.py の DATABASES を変更し MySQL 対応にします。

# settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'justjam',
    }
}

MySQL でデータベースを作成し、migrate を実行します。

% mysql
mysql> create database justjam;
mysql> exit
% pip install mysql
% python manage.py migrate

Django のテーブルが作成されていることを確認します。

% mysql
mysql> show tables
    -> ;
+----------------------------+
| Tables_in_justjam          |
+----------------------------+
| auth_group                 |
| auth_group_permissions     |
| auth_permission            |
| auth_user                  |
| auth_user_groups           |
| auth_user_user_permissions |
| django_admin_log           |
| django_content_type        |
| django_migrations          |
| django_session             |
+----------------------------+
10 rows in set (0.00 sec)

mysql> exit;

管理ユーザーの作成

% python manage.py createsuperuser
Username (leave blank to use 'username'): admin
Email address: admin@justjam.com
Password: 
Password (again): 
Superuser created successfully.

取り急ぎ Django でログイン機能を作った方法(クラスベースビュー)

とりあえず Django のクラスベースビューでログイン機能を追加した時の内容をまとめます。

  1. views.py で LoginRequiredMixin の継承
  2. forms.py でログインフォームを作成
  3. views.py で ログインビューの作成
  4. urls.py でパスと view の紐付け
  5. ログイン用テンプレートの作成
  6. ログインの動きのまとめ
  7. ログアウトページの作成

views.py で LoginRequiredMixin の継承

非ログインユーザーに表示させたくない View 全てで LoginRequiredMixin を継承します。

複数のクラスを継承している場合、下記の例の様に必ず一番目に記述します。

# views.py
class ClassList(LoginRequiredMixin, generic.ListView):
    model = ClassMaster
    template_name = 'class_list.html'

class ClubList(LoginRequiredMixin, generic.ListView):
    model = ClubMaster
    template_name = 'club_list.html'

非ログインユーザーが LoginRequiredMixin を継承したビューに当たった場合、デフォルトで accounts/login/ にリダイレクトされますが、下記の様に settings.py に明示的に LOGIN_URL としてリダイレクト先を設定することも可能です。

# settings.py
LOGIN_URL = 'http://ドメイン名/login' # 一例です。

上記いずれかのパスでログインビューを表示する様、後ほど設定します。

また、リダイレクトされる際、元々アクセスしようとしていたパスを next パラメータとして URL に保持した状態で飛んでいきます。これも後ほどログイン後のリダイレクトに利用します。

forms.py でログインフォームを作成

AuthenticationForm クラスを継承してフォームを作成します。とりあえず username と password のみ認証対象とします。

# forms.py

from django.contrib.auth.models import User
from django.contrib.auth.forms import AuthenticationForm

class LoginForm(AuthenticationForm):
    class Meta:
        model = User
        fields = ['username', 'password']

views.py で ログインビューの作成

ログイン用のビューは LoginView クラスを継承して作成します。

ログインフォームの表示と、ユーザーから submit されたログイン情報の認証、そしてユーザーが元々アクセスしようとしていたページへのリダイレクト等を担ってくれます。

# views.py

class MyLoginView(LoginView):
    form_class = LoginForm

template_name を指定しなくてもデフォルトで registration/login.html になっているので上記の例では明記していませんが、他の view 同様 「template_name = 'login.html'」の様に設定することも可能です。

また、LoginView クラスは POST 時に next URL を渡すとログイン後その URL にリダイレクトしてくれます

"If called via POST with user submitted credentials, it tries to log the user in. If login is successful, the view redirects to the URL specified in next. If next isn’t provided, it redirects to settings.LOGIN_REDIRECT_URL (which defaults to /accounts/profile/). If login isn’t successful, it redisplays the login form."

後ほどテンプレートを作成する際に form からの POST 時に next 渡す様設定します。

urls.py でパスと view の紐付け

アプリケーションレベルの urls.py にログインページのパスとログイン用 view の紐付けを記載します。

# urls.py

from django.urls import path
from . import views

app_name = 'sample_app'
urlpatterns = [
    #〜省略〜
    path('accounts/login/', views.MyLoginView.as_view(), name="login"),
    #〜省略〜
]

ログイン用テンプレートの作成

作成したログインフォームを表示するテンプレートを作ります。

Form 部分の記述は下記の様な感じ。

<!--login.html-->
 
<form action="{% url 'sample_app:login' %}" method="post">
    {% csrf_token %}
        {{ form.as_p }}
        <p><input type="hidden" name="next" value="{{next}}"></p>
        <p><input type="submit" value="ログイン"></p>
    </form>

いくつかポイントがあります。

  • ログインフォーム自身に対して POST する。
  • input の一つとして、リダイレクトされた時に持ってきた next URL を裏で取っておく(type="hidden" で非表示にしています)。

こうしておくことで、ログイン成功時に LoginView がリダイレクトしてくれます。

ログインの動きのまとめ

というわけで、ログイン部分を一通り作りました。

非ログインユーザーがアクセスした場合、下記の様な流れになります。

  1. 非ログインユーザーがページへアクセスしようとする。
  2. LoginRequiredMixin によって、アクセスしようとしていたページのパスを next に保持したままログインページ(LoginView)へリダイレクトされる。
  3. リダイレクト先でのログインフォームでユーザー名とパスワードを Submit する際、next も一緒に LoginView に対して POST する。
  4. 認証に問題がなければ、ログインされ、next のパスにリダイレクトされる。

ログアウトページの作成

一応ログアウト部分もやっつけで書いておきます。

サイトのヘッダー部分など、どこかしらにログアウト用リンクを作って、ログアウトビューとつなげる感じです。

views.py

LogoutView クラスを継承したビューを作成し、任意の template_name を指定します。

# views.py

class MyLogoutView(LogoutView):
    template_name = 'テンプレート名.html'

urls.py

ログアウト用のパスとログアウト用の view を繋げるだけです。

# urls.py

from django.urls import path
from . import views

app_name = 'sample_app'
urlpatterns = [
    #〜省略〜
    path('accounts/login/', views.MyLoginView.as_view(), name="login"),
    path('accounts/logout/', views.MyLogoutView.as_view(), name="logout"),
    #〜省略〜
]

テンプレート

テンプレートでは、サイトの任意の場所にハイパーリンクでログアウト用 URL に飛ぶ様にすれば大丈夫です。

Django の ForeignKey や ManyToManyField で取った情報をテンプレートに表示したい

Django の models.py で ForeignKey や ManyToManyField で他のモデルを参照する形になるとき、テンプレートでその他モデルのデータを引っ張って表示したい事も多いと思います。

と言うかそのために他テーブルを参照する事がほとんどかと思います。

いわゆる Join した形をイメージしますが、実は Join するために models.py で特別なことをする必要はほとんどありません。テンプレート上の記述を少し変更すれば完了します。

  1. 芸人、事務所、番組の例
  2. ForeignKey(外部キー)の場合
  3. ManyToManyField の場合

1. 芸人、事務所、番組の例

芸人、事務所、番組の例で説明します。

#models.py

class JimushoMaster(models.Model): # 芸人の所属事務所
    jimusho_name = models.CharField(max_length=20)

class BangumiMaster(models.Model): # 芸人の出演番組 
    bangumi_name = models.CharField(max_length=60, null=False, unique=True)

class GeininMaster(models.Model):
    name = models.CharField(max_length=20, null=False) # 芸人の名前
    jimusho = models.ForeignKey(JimushoMaster, on_delete=models.PROTECT) # 事務所との紐付き(一対多)
    bangumi = models.ManyToManyField(BangumiMaster) # 番組との紐付き(多対多)

芸人と事務所は一体多の関係、芸人と番組は多対多の関係になります。たぶん。

2. ForeignKey(外部キー)

まずは ForeignKey のケースから。

GeininMaster を渡しているテンプレート上で、JimushoMaster の jimusho_name の値を表出したいとします。

この場合 Template 上では「object名.外部キー項目.参照先テーブルの項目」まで入れるとテンプレート上に値を表示させられます。

{{ object.jimusho.jimusho_name }}

この様な感じです。

models.py で def __str__(self): を記述した場合の注意点

ForeignKey の参照先のモデル(この例では JimushoMaster)のクラス定義で「def __str__(self):」を記述している場合、テンプレートで「object.jimusho」までを指定すると、
def __str__(self):で参照している値がそのまま表出されます。

逆に「object.jimusho.jimusho_name」まで記述しても何も表出されないので気をつけてください。

3. ManyToManyField の場合

続いて ManyToManyField で参照している値をテンプレートに表出したい場合。

これまた GeininMaster を渡しているテンプレート上で、BangumiMaster の bangumi_name の値を表出したいとします。

ListView の例になりますが、View から受け取るオブジェクトの一つ一つに対して複数の ManyRelatedManager オブジェクトが返ってくるので一つずつ表出します。

下記の様な感じです。

{% for geinin in object_list %} # 各芸人に対して
    {% for i in geinin.bangumi.all %} # 複数のオブジェクトが返ってくるので
        {{ i.bangumi_name }} # オブジェクトごとに番組名を表出
    {% endfor %}
{% endfor %}

DjangoのおすすめVPS(仮想専用サーバー)はOSから選ぶべし

唐突ですが結論から先にお伝えしておくと、自分のおすすめはConoHa VPSさくらのVPSになります。

その理由もこの記事で説明しますので是非参考にしてみてください。

共用サーバーと VPS

おすすめ VPS の前に念の為、レンタルサーバーでよく提供される「共用サーバー」と 「VPS」の違いについてお伝えします。「もうわかってるよ」という方は飛ばしてください。

共用サーバーVPS
root 権限なしあり
価格帯無料~数千円程度数百円~数千円程度
自由度×
OS×
保守サービス提供元ユーザー
構築サービス提供元ユーザー
スペック
外部影響受ける受けにくい

共用サーバー

共用サーバーは1台のサーバーを複数のユーザーで共有する「シェアハウス」の様な形態です。「レンタルサーバー」というとこちらの共用サーバーを指すことが多いです。

自由度が少ないですが最もリーズナブルで、更に様々な管理を提供者側がやってくれます。WordPress の運用など簡単にできるようになっているので初心者にも人気です。

反面、root 権限が付与されずカスタマイズ性が低いのがネックで、Django 開発もここが障壁となります。

VPS(仮想専用サーバー)

VPS はサーバーの中に自分の専用領域を持てるサービスで、OS やライブラリのインストールなど、ほとんどの事を自分で行います。

Django に限らずかなり自由に開発ができます。

自分の目的に合わせて設定を行う必要がある分、より多くの知識が必要となりますが、Django アプリを公開する上では必要なこととなります。

Django や Python なら VPS の方が楽

前述の通り WordPress やメールの運用では共用サーバーの方が簡単ですが、Django 開発となると難易度が逆転します。

共用サーバーは Django を想定していない

そもそもの話になりますが、共用サーバーは Django など Python で動くプログラミングをほとんど想定していない様に見受けられます。

使用欄に Python と書かれているのに必要なライブラリが入っていない場合が多く、追加でインストールしようにもroot 権限がないため困難で八方塞がりになることもあります。

▶︎レンタル共用サーバーで Python は茨の道!VPS が楽得でおすすめ

共用サーバーで Django を動かす意味もない

公平にするために一応触れておきますが、ネットで検索すれば共用サーバーで Django を動かす方法も一応あるにはあります。

ただ、共用サーバーでの Django サービス公開をやっとの思いで実現させたとしても、実際に業務でそのノウハウを使う機会はほぼありません。企業での開発やフリーランスとして第三者に提供するサービスでは、わざわざ共用サーバーで Django を動かす必要が無いからです。

より環境構築が簡単で、より実用的な知識が身に付く VPS が圧倒的におすすめです。

さて、一応ここまで「なぜ Django には VPS なのか」という点で書きましたが、その上で「じゃあどの VPS が良いのか」という疑問に答えていきたいと思います。

「おすすめ VPS」の前に「おすすめ OS」

実は Django 開発を行う上で、実は「どの会社の VPS か」で違いが出る部分は少ないです。

その理由ですが、VPS を契約して初めに OS をインストールしますが、その後はサーバーの OS にログインしてひたすらコマンドを打っていく作業になります。

つまり VPS のコントロールパネルを操作することはほとんどなくなるため、どの会社の VPS が使いやすいか、は関係なくなります。

パソコンの設定をする際も Windows なのか Mac なのかは大事ですが、どこの電器屋で買ったかは関係ないですよね。

ただ、VPS によって楽にインストールできる OS があるので、 まず「どの OS か」を考えてその上で「どの VPS なら OS インストールが楽か」を考える方が適切です。

Django 関連情報の多い OS がおすすめ

では「どの OS か」についてですが、基本的に Django は Python3 が使えるメジャーな OS なら動きます。ただ、本当にどの OS でもいいかというとそうは言えません。

ご存知の通り開発にはエラーが付き物なので、エラー発生時に参考になる既存情報が多い方が作業をスムーズに行うことができます

結論から言うと既存情報が多いのは Ubuntu、Debian、CentOS の 3 つ。

Google 上で Django の関連記事が多い OS を調べてみましたので見てみましょう。

Django インストール時を想定して基本的な検索クエリ「"Django" "インストール"」に Django プロジェクト作成時に必ず実行する「"startproject"」コマンドを組み合わせて検索します。

そして各 OS の検索結果が何件返ってくるかをみてみました。

OS 名検索結果数
Ubuntu16,200
Debian15,300
CentOS1,940
Fedora522
Windows Server514
その他(FreeBSD, Arch Linux, openSUSE, NetBSD 等)数件 〜 300 件程度
*数字は 2021 年 12 月時点のものです

OS によってかなり差がありますね。あくまで目安ですが、Ubuntu、Debian、CentOS あたりの情報はそれなりに多いのでおすすめです。

ただ、CentOS に関しては CentOS 8 のサポートが 2021 年 12 月に終了し、CentOS Stream への移行が推奨されています。CentOS 7 のサポート期間は 2024 年 6 月まで続きますのでこれから使うなら CentOS 7 か、新しいもので試してみたい方は CentOS Stream で試すのもありです。

Ubuntu、Debian、CentOS のインストールが簡単な VPS

Django 関連情報が多い OS が絞れたところで、「どの VPS か」に移ります。

先述したとおり VPS によって特定の OS インストールをほぼ自動でやってくれるものがあります。

自動インストールの手順は各社様々で、例えばさくらの VPS であればコントロールパネルで選択した OS を自動でインストールしてくれますし、GMO のクラウド VPS であれば契約時に選択した OS をインストールされた状態でサーバーが用意されます。

さらに ConoHa VPS に関しては Django のセットアップまである程度自動化されています。

各社の OS 自動インストールの対象は下記の通りになっています。

自動 OSConoHa VPSさくらの VPSお名前.com VPSGMO クラウド VPSKagoya Cloud VPS
Ubuntu
Debian
CentOS
その他Django テンプレ無料お試し

ではそれぞれの VPS をみていきましょう。

1. ConoHa VPS

ConoHa VPS
自動インストール対象 OSCentOS 7.1 〜 7.9
CentOS Stream 8
CentOS Stream 9
Ubuntu 18.04
Ubuntu 20.04
Debian 9.13
Debian 10.10
Debian 11.0
自動インストール方法サーバー追加時にテンプレートを選択することでサーバー作成と同時にOSのインストールやアプリケーションサーバーの構築が完了。

VPS を追加する
おすすめプラン1G(初期費用無料 + 月額 723 円〜)

解説

今回紹介する中で対応 OS の種類が最も豊富なのがこちらの ConoHa VPS。

「VPSをもっと速く、かんたんに」というキャッチコピーだけあって、OS だけでなく Django のテンプレートもあり、簡単にセットアップが可能です。「Django のインストールは自分でやりたい」という方はもちろんそれも可能です。

自動インストール対象の OS の種類も多いですし、下の記事の様に公式が親切に色々な情報を載せているのもポイントです。最も Django におすすめの VPS の一つです。

3 ステップで簡単に始められるとの事で、公式サイトに「たったの25秒で〜サーバーが作れます。」とありますので試してみてください。笑

公式サイトConoHa VPS

2. さくらの VPS

さくらのVPS
自動インストール対象 OSUbuntu 18.04
Ubuntu 20.04
CentOS 7
CentOS Stream 8
自動インストール方法VPSコントロールパネルからワンクリックで再インストールが可能。

さくらの VPS 新規追加(コントロールパネル)
おすすめプラン1G(初期費用無料 + 月額 880 円〜)

解説

「Django VPS」で検索すると最も日本語記事が出てくるのが「さくらの VPS」。

かなり気軽に使える VPS で、Django を動かすのには私自身もさくらの VPS を使っています。ポートの開閉をコントロールパネル上のクリックで行えるパケットフィルター機能が地味に楽です。

ユーザーへのサポートをかなり意識していて、公式サイトでも VPS のセットアップの手順を詳しく説明してくれているのもありがたいです。こちらも 最も Django におすすめの VPS の一つです。

ネコでもわかる!さくらのVPS講座 ~第二回「サーバーをさわってみよう!」

公式サイトさくらのVPS

3. お名前.com VPS

お名前.com VPS
自動インストール対象 OSUbuntu 18.04
Ubuntu 20.04
CentOS 7.5 ~ 7.9
CentOS Stream 8
Debian 9.13
Debian 10.10
自動インストール方法VPSコントロールパネルのサーバーセットアップで OS とバージョンを選択可能。

サーバーを初期セットアップする
おすすめプラン1G(初期費用無料 + 月額 873 円)

解説

オンラインでのお申込みから最短10分で利用開始できるというお名前.com の VPS。

KVM という仮想化方式を採用していて、ネットワークやディスクI/Oのパフォーマンスを良くする Virtio ドライバにも対応するなどハイパフォーマンスを追求している VPS

OS の初期インストールの方法は下記の公式ページに記載があります。

公式サイトお名前.com VPS

4. クラウドVPS by GMO

GMOクラウドのVPS 詳細はこちら
自動インストール対象 OSCentOS 7.9
CentOS 8.2
Ubuntu 20.04
Debian 10.0
自動インストール方法契約時に選択した OS をインストールした状態でサーバーが用意される。

新規サーバー申し込み方法
おすすめプランV1(初期費用無料 + 月額 968 円〜)
お得な情報14 日間無料お試し

解説

コンテナ技術を採用していて、一般的な仮想マシンに比べて軽量で vCPU やメモリなどの負荷が小さく、リソースを効率的に利用できる VPS。どちらかというと法人ユーザーを想定しているようですが、14 日間の無料お試し期間があるのは他と違う点です 。

契約時の OS 選択について下記の公式ページで解説が載っています。

公式サイトGMOクラウドのVPS 詳細はこちら

5. KAGOYA Cloud VPS

カゴヤ・ジャパン
自動インストール対象 OSCentOS 7
CentOS 8
CentOS Stream 8
Ubuntu 18.04
Ubuntu 20.04
自動インストール方法専用コントロールパネルからテンプレートとアプリケーションを選択して環境構築可能。

インスタンス作成
おすすめプラン1コア/1GB(初期費用無料 + 月額 〜550 円)

解説

コントロールパネルから即座にスペック変更することができます

こちらもコントロールパネルからインスタンスを作成する際に任意の OS を指定することができます。

公式サイトカゴヤ・ジャパン

個人的には ConoHa VPS かさくらの VPS が Django におすすめ

OS インストールの手軽さを基準に Django のおすすめ VPS を 5 つ紹介しましたが、いかがだったでしょうか?

個人的なおすすめは ConoHa VPSさくらの VPS です。

ConoHa VPS は申し込み時の手軽さと、さくらの VPS は既に Django に利用している人がかなりいる & 私自身も利用していて全く不満がないです。

多くのユーザーがアクセスする大規模なアプリケーションを作成するのであれば GMO のクラウド VPSお名前.com の VPS(KVM)もいいかと思いますが、個人で小さい Django アプリを作っていくような場合はやはり前述の 2 つがおすすめだと思います。

さくらの VPS (Ubuntu 20.04) で Django アプリを作る(3/3)

Ubuntu 環境(さくらのVPS )で Django アプリケーションを作るまでに実際に辿ったステップ。こちらの記事の続編です。

今回はこれまでのステップで作成した Django アプリケーションの HTTPS 化、そして使用データベースを MySQL に変更します。

  1. ドメインの HTTPS 化
    • ポート 443 の開放
    • certbot と Nginx プラグインのインストール
    • certbot の設定
    • 変更内容の確認
  2. 使用データベースを MySQL に変更する
    • MySQL のインストール
    • 非 root ユーザーとデータベースの作成
    • mysqlclient インストールの準備
    • Python 仮想環境と MySQLdb の動作確認

1. サイトの HTTPS 化

サイトに https でアクセスできる様設定します。certbot を使えば Let's Encrypt の TLS/SSL 証明書の取得や設定を簡単に行うことができます。

ポート 443 の開放

さくらインターネットのパケットフィルタを使っている場合、ポート番号 443 が開放されている必要があります。

ファイアウォールで設定する場合は ufw コマンドで必要な設定を行ってください。

certbot と Nginx プラグインのインストール

certbot(Let's Encrypt 使用の HTTPS 自動設定パッケージ)と python3-certbot-nginx(Certbot 用 Nginx プラグイン)をインストールします。

$ sudo apt install certbot python3-certbot-nginx

certbot の設定

Nginx プラグインを使用して自動設定を走らせます。このプロセスによって自動的に sites-available ディレクトリ内の Nginx 設定ファイルに変更が加えられます。

コマンド内「-d」の後に HTTPS 化するドメインを入力します。Nginx の設定ファイル内、server_name に指定されているドメインと一致しているのを確認してください。

$ sudo certbot --nginx -d example.com -d www.example.com

これを実行すると緊急連絡用のメールアドレスの入力を求められ、入力して続けると利用規約への同意、お知らせメール送信の可否、そして最後に HTTP へのリクエストを HTTPS へリダイレクトするか否かを尋ねられるのでそれぞれ回答して進めていきます。

以上で完了です。https でのアクセスが可能になっているはずです。

変更内容の確認

完了後、Nginx 設定ファイルを確認すると「# managed by Certbot」のコメントと共に自動で追記された内容を確認できます。

既存の server ブロック内への追記内容

    listen [::]:443 ssl; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/graffuhs.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/graffuhs.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

新規作成された server ブロックの内容

server {
    if ($host = example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


        listen 80;
        listen [::]:80;

        server_name example.com;
    return 404; # managed by Certbot


}
server {
    if ($host = www.example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


        listen 80;
        listen [::]:80;

        server_name www.example.com;
    return 404; # managed by Certbot


}

この設定によって「http://example.com」と「http://www.example.com」両方とも HTTPS へリダイレクトされる様になりました。

2. 使用データベースを MySQL に変更する

MySQL のインストール

コマンド「sudo apt install mysql-server」を実行し、MySQL をインストールします。

adminvps@xx1-234-56789:~$ sudo apt install mysql-server
adminvps@xx1-234-56789:~$ 

コマンド「sudo mysql_secure_installation」を実行。

adminvps@xx1-234-56789:~$ sudo mysql_secure_installation
adminvps@xx1-234-56789:~$ 

コマンド「sudo mysql」で MySQL へアクセスしてみます。

vpsadmin@xx1-234-56789:~$ sudo mysql
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 11
Server version: 8.0.23-0ubuntu0.20.04.1 (Ubuntu)

Copyright (c) 2000, 2021, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 

無事アクセスできました。コマンド「exit」で MySQL から出ます。

mysql> exit
Bye
vpsadmin@xx1-234-56789:~$ 

非 root ユーザーとデータベースの作成

root で接続

sudo mysql -u root
create user ユーザー名 identified by 'パスワード';
GRANT ALL PRIVILEGES ON demodb.* to demouser@'%';
flush privileges;

ここで作成したユーザー名とパスワードを Django の settings.py で使います。

mysqlclient インストールの準備

libmysqlclient-dev と default-libmysqlclient-dev をインストールします。

adminvps@xx1-234-56789:~$ sudo apt install libmysqlclient-dev default-libmysqlclient-dev

Python 仮想環境と MySQLdb の動作確認

(sample) adminvps@xx1-234-56789:~/sample/bin$ pip install wheel
Collecting wheel
  Downloading wheel-0.36.2-py2.py3-none-any.whl (35 kB)
Installing collected packages: wheel
Successfully installed wheel-0.36.2
(sample) adminvps@xx1-234-56789:~/sample/bin$ pip install mysqlclient
Requirement already satisfied: mysqlclient in /home/adminvps/sample/lib/python3.9/site-packages (2.0.3)
(sample) adminvps@xx1-234-56789:~/sample/bin$ 

ここで、Python の仮想環境と MySQLdb の動作確認をしてみます。

Python 仮想環境を作り、起動します。

vpsadmin@ss1-234-56789:~$ python3.9 -m venv testvenv
vpsadmin@ss1-234-56789:~$ source testvenv/bin/activate
(testvenv) vpsadmin@ss1-234-56789:~$

wheel をインストールします。

(testvenv) vpsadmin@ss1-234-56789:~$ pip install wheel
Collecting wheel
  Using cached wheel-0.36.2-py2.py3-none-any.whl (35 kB)
Installing collected packages: wheel
Successfully installed wheel-0.36.2
(testvenv) vpsadmin@ss1-234-56789:~$

mysqlclient をインストールします。

(testvenv) vpsadmin@ss1-234-56789:~$ pip install mysqlclient
Processing ./.cache/pip/wheels/43/55/d9/a2243d4b624c18c5cba30bf88e0521147498368068cb302532/mysqlclient-2.0.3-cp39-cp39-linux_x86_64.whl
Installing collected packages: mysqlclient
Successfully installed mysqlclient-2.0.3
(testvenv) vpsadmin@ss1-234-56789:~$ 

*pip install は「mysqlclient」ですが、python で import するときは「import MySQLdb」になります。(import mysqlclient ではない)誰も間違わないかもしれませんが僕はこれでハマってました。。。

Python を起動し、import MySQLdb ができるか確認します。

(testvenv) vpsadmin@ss1-234-56789:~$ python3.9
Python 3.9.0+ (default, Oct 20 2020, 08:43:38) 
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import MySQLdb
>>> 

無事 import できたので大丈夫そうです。

他の行程へ

▶︎まずは2週間無料でお試し♪さくらのVPS

さくらの VPS (Ubuntu 20.04) で Django アプリを作る(2/3)

Ubuntu 環境(さくらのVPS )で Django アプリケーションを作るまでに実際に辿ったステップ。こちらの記事の続編です。

今回は実際に Django のプロジェクト・アプリケーションを作って公開するところまで。Web サーバーとして Nginx、アプリケーションサーバーとして Gunicorn の設定も行います。

また、今後同じサーバーで複数の Django プロジェクトを作っても大丈夫な様に設定していきます。

Django + Nginx + Gunicorn での設定

全体はとりあえずこんな感じのイメージです。

  1. サイトをホストするディレクトリの作成
    • ルートディレクトリ作成
    • 管理者の変更
  2. Django を動かす
    • Python 仮想環境作成
    • Django インストール
    • Django プロジェクトと Django アプリケーションを作成
    • settings.py の設定
    • Django の動作確認
  3. Web サーバーの設定(Nginx)
    • Web サーバー: Nginx
    • 設定ファイル(nginx.conf)
  4. アプリケーションサーバーの設定(Gunicorn)
    • アプリケーションサーバー: Gunicorn
    • 仮想環境内で pip install gunicorn
    • systemd .socket ファイル
    • systemd .service ファイル
    • Nginx の設定ファイルでソケットを使用する様設定する
  5. 静的ファイルの設定

1. サイトをホストするディレクトリの作成

サーバー上にサイトホスト用ディレクトリを作成しますが、今後を見越し、複数のドメインを同じサーバー上でホスト出来る様に配慮して作業を進めていきます。

ルートディレクトリ作成

サイトをホストするルートディレクトリを作成します。

Nginx はデフォルトで「/var/www/html」というディレクトリを作っていますが、今回は /var/www/ の配下にドメイン名のディレクトリ、そしてその配下に html というディレクトリを作ります。(イメージ:/var/www/example.com/html)

vpsadmin@xx1-234-56789:/var/www$ sudo mkdir -p example.com
[sudo] password for vpsadmin: 
vpsadmin@xx1-234-56789:/var/www$ ls
html  meatthezoo.org
vpsadmin@xx1-234-56789:/var/www$ cd example.com
vpsadmin@xx1-234-56789:/var/www/example.com$ sudo mkdir -p html

管理者の変更

ディレクトリの管理者を変更します。

vpsadmin@xx1-234-56789:/var/www$ sudo chown -R $USER:$USER /var/www/example.com/html

2. Django を動かす

Python 仮想環境作成

ルートディレクトリ(/var/www/example.com/html)直下で「python3 -m venv 仮想環境名」を実行しPython の仮想環境を作成します。

$ python3 -m venv djangovenv

Django インストール

仮想環境を起動し、「pip install django」で Django をインストールします。

% cd djangovenv
% source bin/activate
(djangovenv) % pip install django

Django プロジェクトと Django アプリケーションの作成

Django プロジェクトと Django アプリケーションを作成していきます。

(djangovenv) % django-admin startproject djangoprod
(djangovenv) % cd djangoprod
(djangovenv) % python manage.py startapp djangoapp

settings.py の設定

settings.py の ALLOWED_HOSTS にサイトのドメイン名を登録し、INSTALLED_APS にアプリケーションを追加します。

ALLOWED_HOSTS = ['example.com', 'www.example.com']

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'djangoapp.apps.DjangoAppConfig' # 追記分
]

Django の動作確認

コマンド「python manage.py runserver 0.0.0.0:8000」を実行し、ブラウザの URL バーに「ドメイン名:8000」と打ち込んでみます。

Django デフォルトのロケットの画面が出たら大丈夫です。ちなみにこの画面は settings.py の DEBUG = True の時だけ表示されます。

Djangoのデフォルトエラーページが DEBUG=Falseだと見れないのはなんでか調べた

今現在、Django の runserver を起動した場合に限り、ポート 8000 でなら Django が動くという状態です。

というわけで Web サーバー(Nginx)をアプリケーションサーバー(Gunicorn)を使って下記を解決していきます。

問題解決方法
URL バーでポート 8000 を指定しないとアクセスできないNginx の設定ファイルで、該当するドメイン名にアクセスされたら Django に繋ぐ様変更する
runserver を起動していないとアクセスできないGunicorn を使う

3. Web サーバーの設定(Nginx)

設定ファイル(nginx.conf)

デフォルトの設定ファイルは「/etc/nginx/nginx.conf」

初めから下記の二行が書いてあればこのファイルはノータッチで良さそうです。

  • include /etc/nginx/conf.d/*.conf;
  • include /etc/nginx/sites-enabled/*;

複数ドメイン対応

一つのサーバーで複数のウェブサイトを運営しようとした場合、一つの IP アドレスに複数のドメイン名が紐づく事になります。

そのため、リクエストされた URL に応じて適切なサイトの情報を返せる様設定していきます。

  1. sites-available ディレクトリにドメイン毎の設定ファイルを置く
  2. 各ドメインの設定ファイルでドメインとルートディレクトリを紐付け
  3. それらファイルのシンボリックリンクを sites-enabled ディレクトリに作成
  4. sites-enabled を nginx.conf 本体に include

設定ファイルの複製と編集

「sites-available」ディレクトリの「default」ファイルを個別サイト用にコピーします。

sudo cp /etc/nginx/sites-available/default /etc/nginx/sites-available/example.com

コピーした方のファイルを確認します。下の方に Virtual Host のための設定というところがあるのでそちらを使います。上半分は Default Server のための設定なので全てコメントアウトします。

server_name と location / の部分を編集しました。

example.com ファイルの Virtual Host 設定部分
# Virtual Host configuration for example.com
#
# You can move that to a different file under sites-available/ and symlink that
# to sites-enabled/ to enable it.
#

server {
        listen 80;
        listen [::]:80;

        server_name www.example.com;
        return 301 http://example.com$request_uri;
}


server {
        listen 80;
        listen [::]:80;

        server_name example.com;

        root /var/www/example.com/html;
        index index.html;

        location / {
                try_files $uri $uri/ =404;
                include proxy_params;
                proxy_pass http://127.0.0.1:8000;
        }
}
ファイル内の概念解説
listenリクエストを受け入れる IP アドレスやポートの指定。
UNIX ドメインソケットのパスでも可。
server_nameリクエストを受け入れるサーバー名(ドメイン名)の指定。
複数のサーバー名をスペース区切りで設定可。
try_filesファイルの存在を指定された順に確認。
$uriリクエストの URI。リダイレクト時など、処理の中で元々のリクエストから書き変わることもある。
include proxy_params/etc/nginx/proxy_params」にプロキシの設定が記載してあるので include。
proxy_passプロキシサーバーの指定。
参照 Nginx Documentation

初めの部分は「www.example.com」へのアクセスを自動的に「example.com」へリダイレクトする設定です。

シンボリックリンクを作成します。

sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/

Nginx を再起動します。

sudo systemctl restart nginx

そして runserver を実行して、反映を確かめます。

python manage.py runserver 0.0.0.0:8000

これで一つ解決です。ポート 8000 を URL に含めなくても Django に飛ぶ様になりました。

次は runserver を起動していないとアクセスできないという部分を、Gunicorn を使って解決します。

問題解決方法
runserver を起動していないとアクセスできないGunicorn を使う

4. アプリケーションサーバーの設定(Gunicorn)

Nginx から飛んできたリクエストを、待ち受けているソケットから Django アプリケーションに伝える様に設定します。

言い方を変えると Nginx からのリクエストを .socket ファイルが受け、.service ファイルを起動し Gunicorn の処理を実行します。

Gunicorn のインストール

仮想環境内で pip install gunicorn を実行します。

pip install gunicorn

.socket と .service の作成

「/etc/systemd/system/」ディレクトリに .socket と .service を作成します。今回はファイル名をそれぞれ「sample_django.socket」、「sample_django.service」としています。

systemd .socket ファイル

Nginx の設定ファイルで指定したポートでリクエストを待ち受け、 リクエストがあったら指定されているサービスに接続を渡します。

# sample_django.socket

[Unit]
Description=gunicorn socket
[Socket]
ListenStream=/run/sample_django.sock
[Install]
WantedBy=sockets.target
項目メモ
Descriptionログ出力の際などに使われる
ListenStreamポートの指定。Nginx 設定ファイルの pass_proxy で指定
WantedBysockets.target

systemd .service ファイル

サービスの依存関係や実際の実行内容を定義します。

# sample_django.service

[Unit]
Description=gunicorn daemon
Requires=sample_django.socket
After=network.target
[Service]
User=root
Group=root
WorkingDirectory=/var/www/example.com/html/djangovenv/djangoprod
ExecStart=/var/www/example.com/html/djangovenv/bin/gunicorn --workers 3 --bind unix:/run/sample_django.sock djangoprod.wsgi:application
[Install]
WantedBy=multi-user.target
項目メモ
Descriptionログ出力の際などに使われる
Requires対応する .socket ファイル
After
User
Group
WorkingDirectorymanage.py があるディレクトリのフルパス?
ExecStartsystemctl start した時に実行するコマンド。
.sock と wsgi を bind。
venv 内の gunicorn のフルパス(?)を指定
WantedBy大抵 multi-user.target で大丈夫

サービスを起動します。

systemctl start sample_django.socket
systemctl start sample_django.service

下記を実行すると Gunicorn のステータスを確認できます。

sudo systemctl status sample_django

Nginx 設定ファイルの変更

Nginx 設定ファイルの proxy_pass の部分を下記の様に変更しました。.socket ファイルの ListenStream で設定したポートを指定しています。

location / {
        try_files $uri $uri/ =404;
        include proxy_params;
        # proxy_pass http://127.0.0.1:8000;
        proxy_pass http://unix:/run/sample_django.sock;
}

Nginx の設定を検証し再起動します。

$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
$ sudo systemctl restart nginx

これで runserver を実行しなくても URL を叩けば Django アプリケーションへアクセスできる様になったはずです。

大雑把ですがなんとなく全体像は下記の様なイメージです。

Django + Nginx + Gunicorn での設定

*作業時は何かにつけて「pkill gunicorn」で gunicorn を終わらせた方が良いです。エラーが起きていくらコードを修正しても治らないまま数時間経って、「pkill gunicorn」一発で治ったこともありました。

静的ファイルの設定

実はこの時点では CSS や JavaScript のいわゆる静的ファイルはうまく反映されません。ドメイン名/admin にブラウザでアクセスすると CSS が抜けている状態だと思います。

Django で静的ファイルとうまくやる

本番環境でちゃんと表示される様、settings.py と Nginx の設定ファイルを編集します。

settings.py の編集

settings.py 内では STATICFILES_DIRS と STATIC_ROOT を設定します。

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),
]
STATIC_ROOT = '/var/www/example.com/html/static'
項目メモ
STATICFILES_DIRScollectstatic が静的ファイルを追加検索する対象のディレクトリ(特定のアプリケーションに属さないものが対象)
STATIC_ROOTcollectstatic 実行時に静的ファイルの集約先となるディレクトリ

collectstatic を実行します。

python manage.py collectstatic

Nginx の設定ファイルの編集

server ディレクティブの中に STATIC_ROOT 指定したパスを追記しました。

        location /static {
                alias /var/www/example.com/html/static;
        }

これでドメイン名/admin を再度確認すると CSS が反映されていると思います。

他の行程へ

▶︎まずは2週間無料でお試し♪さくらのVPS

さくらの VPS (Ubuntu 20.04) で Django アプリを作る(1/3)

Ubuntu 環境(さくらのVPS )で Django アプリケーションを作るまでに実際に辿ったステップを記録します。

手順がかなり多いので途中あまり詳しく書いていないところもありますがご了承ください。

まずは VPS の契約から Ubuntu OS の設定Python のインストールWeb サーバーのインストールまで。

  1. VPS の契約(さくらの VPS)
  2. 独自ドメインの取得
  3. IP アドレスとドメイン名を紐付け
  4. OS (Ubuntu 20.04) のインストール
  5. Ubuntu の初期設定
    • ssh でサーバーへ接続
    • 非 root ユーザーの作成
    • パッケージリストを最新版へ更新
    • パッケージのアップグレード
    • サーバーの再起動
  6. FTP サーバーのインストール
  7. 開発パッケージのインストール
  8. Python と関連パッケージのインストール
    • 依存関係のインストール
    • Python 3.9 のインストール
    • python3.9-dev のインストール
    • 仮想環境(venv)のインストール
  9. Web サーバー(Nginx)のインストール

ちなみに、僕は初め下記の書籍に沿って Django の全体像を勉強しました。書籍の中では AWS を使ったデプロイにも触れられていますので参考にしてみてください。

動かして学ぶ! Python Django開発入門 (NEXT ONE)

1. VPS の契約(さくらの VPS)

まずは VPS の契約。私は月額 880 円〜の さくらのVPS 1G プランにしました。

公式ページさくらのVPS 1G

さくらの VPS 以外でも大丈夫

とはいうものの、実はさくらの VPS でなくても Ubuntu OS であればこちらの記事の手順で特に問題ないです。

コントロールパネルの見た目やファイアウォールの設定の部分だけは少し違いますが、それは一番初めの部分でそれ以降は OS に依存するので他の VPS でも変わりません。

Django におすすめの OS と VPS を下記の記事で紹介しているのでぜひ参考にしてみてください。

▶︎ Django のおすすめ VPS(仮想専用サーバー)は OS から選ぶべし
▶︎ Ubuntu を手軽に使える VPS おすすめ 7 選

2. 独自ドメインの取得

サイト公開用の独自ドメインを取得します。こちらもさくらのドメインで取得しました。

3. IP アドレスとドメイン名を紐付け

VPS コントロールパネルの「ネームサーバ登録」をクリックします。

遷移先のページで「ドメインリスト」をクリック後、対象のドメインの「ゾーン編集」をクリックし、情報を登録します。

ゾーン情報の設定に関しては下記の記事にもまとめているので参考にしてみてください。

4. OS (Ubuntu 20.04) のインストール

VPS コントロールパネルの「各種設定」そして「OSインストール」をクリックします。

今回は標準 OS の Ubuntu 20.04 をインストールします。

サーバーのファイアウォールを設定する代わりに、さくら VPS のパケットフィルタの機能でポートの開閉を設定しています。

ファイアウォールで設定する場合はパケットフィルタをオフにします。

「インストールを実行する」をクリックし、Ubuntu のインストールを開始します。

先ほどと重複しますが、Ubuntu 以外にも Django におすすめの OS を下記の記事で紹介しているのでもしよければ見てみてください。

▶︎ Django におすすめの OS と VPS の選び方

5. Ubuntu の初期設定

ssh でサーバーへ接続

ターミナルで「ssh 管理ユーザー名@ホスト名」と打ち込み接続します。ホスト名は下記の通りコントロールパネルの「ネットワーク情報」タブで確認できます。

下記の様にコマンド「ssh 管理ユーザー名@ホスト名」を入力します。

% ssh ubuntu@xx1-234-56789.vs.sakura.ne.jp

初めて接続する際に下記の様に「The authenticity of host 'xx1-234-56789.vs.sakura.ne.jp (123.456.78.90)' can't be established.」とメッセージが出ることがありますが特に問題ないので yes と打ち込みます。

The authenticity of host 'xx1-234-56789.vs.sakura.ne.jp (123.456.78.90)' can't be established.
ECDSA key fingerprint is XXX123:ABCDEFGHIJKLMNOPQRSTUVWXYZ.
Are you sure you want to continue connecting (yes/no/[fingerprint])? 
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'xx1-234-56789.vs.sakura.ne.jp.123.456.78.90' (ECDSA) to the list of known hosts.
% ssh ubuntu@xx1-234-56789.vs.sakura.ne.jp
ubuntu@xx1-234-56789.vs.sakura.ne.jp's password: 

パスワードを入力し無事 ssh で サーバーへログインできました。

% ssh ubuntu@xx1-234-56789.vs.sakura.ne.jp
ubuntu@xx1-234-56789.vs.sakura.ne.jp's password: 
Welcome to Ubuntu 20.04.1 LTS (GNU/Linux 5.4.0-52-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Fri Apr  9 08:11:04 JST 2021

  System load:  0.0               Processes:             105
  Usage of /:   2.0% of 94.43GB   Users logged in:       0
  Memory usage: 16%               IPv4 address for ens3: 123.456.78.90
  Swap usage:   0%


66 updates can be installed immediately.
0 of these updates are security updates.
To see these additional updates run: apt list --upgradable


The list of available updates is more than a week old.
To check for new updates run: sudo apt update


The programs included with the Ubuntu system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Ubuntu comes with ABSOLUTELY NO WARRANTY, to the extent permitted by
applicable law.


SAKURA internet [Virtual Private Server SERVICE]

To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

ubuntu@xx1-234-56789:~$ 

3行目に「Welcome to Ubuntu 20.04.1 LTS」と書かれてあります。それ以降の部分はサーバーの容量やアップデートなどの案内事項です。

非 root ユーザーの作成

ログインしたユーザーを root ユーザーへ昇格し、新たな sudo ユーザーを作成します。詳しくは下記の記事を参照してください。

パッケージリストを最新版へ更新

コマンド「sudo apt update」でパッケージリストを最新版に更新します。

ubuntu@xx1-234-56789:~$ sudo apt update
ubuntu@xx1-234-56789:~$ 

パッケージのアップグレード

コマンド「sudo apt upgrade」を実行し、サーバーに元々インストールされているパッケージに対してアップグレードを実施します。

ubuntu@xx1-234-56789:~$ sudo apt upgrade
ubuntu@xx1-234-56789:~$ 

サーバーの再起動

コマンド「sudo reboot」を実行し、サーバーを再起動します。

ubuntu@xx1-234-56789:~$ sudo reboot
Connection to xx1-234-56789.vs.sakura.ne.jp closed by remote host.
Connection to xx1-234-56789.vs.sakura.ne.jp closed.
% 

6. FTP サーバーのインストール

FTP 接続ができる様にしたいので vsftpd をインストールします。コマンド「sudo apt install vsftpd」を実行します。

ubuntu@ik1-437-50827:~$ sudo apt install vsftpd
ubuntu@ik1-437-50827:~$ 

Cyberduck で接続できました。

7. 開発パッケージのインストール

adminvps@xx1-234-56789:~$ sudo apt install build-essential
adminvps@xx1-234-56789:~$ 

8. Python と関連パッケージのインストール

コマンド「python3 -V」で Ubuntu 20.04 にもともと入っている Python のバージョンを確認します。

adminvps@xx1-234-56789:~$ python3 -V
Python 3.8.5
adminvps@xx1-234-56789:~$

Python 3.8.5 が入っていました。この後 Python 3.9 をインストールします。

依存関係のインストール

Python 自体をインストールする前に必要な依存関係をインストールします。コマンド「sudo apt install 〜」で様々なパッケージをインストールします。

adminvps@xx1-234-56789:~$ sudo apt install zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev libsqlite3-dev wget libbz2-dev
adminvps@xx1-234-56789:~$ 

Python 3.9 のインストール

依存関係のインストールが完了したら、Python 3.9 をインストールします。

adminvps@xx1-234-56789:~$ sudo apt install python3.9
adminvps@xx1-234-56789:~$ 

python3.9-dev のインストール

adminvps@xx1-234-56789:~$ sudo apt install python3.9-dev
adminvps@xx1-234-56789:~$ 

仮想環境(venv)のインストール

Python の仮想環境である venv をインストールするためコマンド「sudo apt-get install python3.9-venv」を実行します。

adminvps@xx1-234-56789:~$ sudo apt-get install python3.9-venv
adminvps@xx1-234-56789:~$ 

9. Web サーバー(Nginx)のインストール

コマンド「sudo apt install nginx」で Nginx をインストールします。

adminvps@xx1-234-56789:~$ sudo apt install nginx
adminvps@xx1-234-56789:~$ 

ブラウザの URL バーにサーバーの IP アドレスを入れてみて、下記のページが出れば無事インストールされています。

とりあえず VPS に Ubuntu が入りPython と Nginx がインストールされた状態です。

他の行程へ

▶︎まずは2週間無料でお試し♪さくらのVPS

【Django】独自 ID の自動インクリメントが行われていなかった

問題

Django でモデルを扱う際、デフォルトで作成される id ではないプライマリキーで管理をしたかったので、なんとなくネットでささっと見た情報で 「models.AutoField(primary_key=True)」を設定していました。

ただ、データベース上での設定が正しくできていなかった様で自動インクリメントが機能していませんでした。

そのため Django の admin 画面からデータを登録する際、すでに存在する値が id として登録されていました。

ちなみに、models.py では「models.AutoField(primary_key=True)」となっていたもののテーブル上ではカラムがプライマリキーになっていなかったため、エラーにもなっていませんでした。

修正作業

この時点でのテーブルは下記の状態。「id」カラムを修正していきます。

mysql> describe ig_mst_product;
+-------------------+--------------------------+------+-----+---------+-------+
| Field             | Type                     | Null | Key | Default | Extra |
+-------------------+--------------------------+------+-----+---------+-------+
| id                | int(6) unsigned zerofill | NO   |     | 000000  |       |
| product_nm        | varchar(60)              | YES  |     | NULL    |       |
| product_category  | varchar(20)              | YES  |     | NULL    |       |
| brand_cd          | int(6) unsigned zerofill | YES  |     | NULL    |       |
| product_url       | text                     | YES  |     | NULL    |       |
| product_image_url | text                     | YES  |     | NULL    |       |
+-------------------+--------------------------+------+-----+---------+-------+

プライマリキーとして設定

既存の値が再度使われてしまうのはおかしいので MySQL 上でプライマリキーを設定。

mysql> alter table ig_mst_brand add primary key (id);

値の重複の解決

admin 画面からレコードを追加すると「1062, "Duplicate entry '000000' for key 'ig_mst_product.PRIMARY'"」という感じで値の重複が発生。

「000000」を id として使用しようとしているので、おそらく値の自動インクレメントがされていない状態

デフォルト値の設定削除

デフォルト値として「000000」を設定していたのでとりあえずなくしてみます。

mysql> alter table ig_mst_product alter id drop default;

auto_increment の設定

その上で MySQL でカラムに auto_increment を設定しようとしたところ、また値の重複で拒否されました。

mysql> alter table テーブル名 modify カラム名 int(6) unsigned zerofill not null auto_increment;
ERROR 1062 (23000): ALTER TABLE causes auto_increment resequencing, resulting in duplicate entry '000001' for key 'テーブル名.PRIMARY'

この時点で id カラムには「000000」〜「000069」まで存在していたにもかかわらず、なぜか「000001」を使おうとして重複エラーが返ってきている状況。

調べてみたところ、既存データの対象のカラムの中に 0 もしくは -1 以下の値が入っているとこのエラーになる様です。既存データで「000000」が存在したのでこれを別の値に変更して再度実行したら成功しました。

mysql> alter table ig_mst_product modify id int(6) unsigned zerofill not null auto_increment;
Query OK, 69 rows affected, 2 warnings (0.04 sec)
Records: 69  Duplicates: 0  Warnings: 2

正しい設定をまとめると…

Id として使用するカラムがデータベース上で下記の条件を満たす必要がある様です。

  • デフォルト値なし
  • プライマリキーとして設定
  • AUTO_INCREMENT を設定
    • 既存データで 0 もしくはそれ以下の値が入っていないことを確認

一応 Before / After も載せておきます。Key, Default, Extra が変わっています。

Before

mysql> describe ig_mst_product;
+-------------------+--------------------------+------+-----+---------+-------+
| Field             | Type                     | Null | Key | Default | Extra |
+-------------------+--------------------------+------+-----+---------+-------+
| id                | int(6) unsigned zerofill | NO   |     | 000000  |       |
| product_nm        | varchar(60)              | YES  |     | NULL    |       |
| product_category  | varchar(20)              | YES  |     | NULL    |       |
| brand_cd          | int(6) unsigned zerofill | YES  |     | NULL    |       |
| product_url       | text                     | YES  |     | NULL    |       |
| product_image_url | text                     | YES  |     | NULL    |       |
+-------------------+--------------------------+------+-----+---------+-------+

After

mysql> describe ig_mst_product;
+-------------------+--------------------------+------+-----+---------+----------------+
| Field             | Type                     | Null | Key | Default | Extra          |
+-------------------+--------------------------+------+-----+---------+----------------+
| id                | int(6) unsigned zerofill | NO   | PRI | NULL    | auto_increment |
| product_nm        | varchar(60)              | YES  |     | NULL    |                |
| product_category  | varchar(20)              | YES  |     | NULL    |                |
| brand_cd          | int(6) unsigned zerofill | YES  |     | NULL    |                |
| product_url       | text                     | YES  |     | NULL    |                |
| product_image_url | text                     | YES  |     | NULL    |                |
+-------------------+--------------------------+------+-----+---------+----------------+

【Django】Nginx + Gunicorn でサブドメインを設定する方法

「example.com」というドメイン名ですでに運用しているところに追加で「sub.example.com」も使いたいという想定で書きます。

また、Django の処理には Gunicorn を使っています。

  1. ネームサーバーでサブドメインの設定をする
  2. サブドメイン用のディレクトリを作る
  3. 仮想環境と Django プロジェクトを作成する
  4. Gunicorn をインストールする
  5. UNIX ドメインソケットを設定する
  6. Nginx の設定ファイルにサブドメインの処理を追記する
  7. とりあえず図にしてみた

1. ネームサーバーでサブドメインの設定をする

ネームサーバーでサブドメインを登録します。

フィールド
エントリ名sub
種別別名 (CNAME)
@
DNSチェックする
TTLの設定なし

さくらインターネットを使っている場合は会員メニュー > ドメイン > ゾーン表示の画面で登録できます。

2. サブドメイン用のディレクトリを作る

任意の場所にサイトをホストするためのディレクトリを作成します。

例えば「example.com」というディレクトリの配下にさらにサブドメイン用に「sub」ディレクトリ、その配下に「html」と作る場合は下記の様な形。

$ cd var/www/example.com
$ mkdir sub
$ cd sub
$ mkdir html

ディレクトリの管理者を変更します。

$ sudo chown -R $USER:$USER /var/www/example.com/sub/html/

注意

一度上記の管理者の変更を飛ばした際、後で Django の「python manage.py startapp アプリ名」を実行した時に「ImportError: Couldn't import Django. Are you sure it's installed and available on your PYTHONPATH environment variable? Did you forget to activate a virtual environment?」というエラーが出ました。

「pip install django」を実行した際に django モジュールが仮想環境内に上手くインストールされなかったことが原因です。

3. 仮想環境と Django プロジェクトを作成する

Python の仮想環境を作成、起動します。

$ cd var/www/example.com/sub/html/
$ python3.9 -m venv dj_proj_venv
$ cd dj_proj_venv
$ source bin/activate

Django を pip install し、プロジェクトとアプリケーションを作成します。

注)ローカルPCで開発したものを Git で本番環境にクローンする様な場合はこのステップは不要です。

(dj_proj_venv) $ pip install django
(dj_proj_venv) $ django-admin startproject dj_proj
(dj_proj_venv) $ cd dj_proj
(dj_proj_venv) $ python manage.py startapp dj_app

settings.py の INSTALLED_APPS にアプリケーションを追記します。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    'dj_app.apps.DjAppConfig', # 追記分
]

settings.py の ALLOWED_HOSTS にサブドメインを含むドメインを記述します。

ALLOWED_HOSTS = ['sub.example.com']

4. Gunicorn をインストールする

Gunicorn をインストールします。

$ pip install gunicorn

5. UNIX ドメインソケットを設定する

そして Nginx と Gunicorn の接続に Unix ソケットを使える様、Linux サーバーの Systemd という機能で設定します。

.socket ファイルの作成

サブドメイン用に「example_sub.socket」というファイルを作成します。

# example_sub.socket
[Unit]
Description=gunicorn socket
[Socket]
ListenStream=/run/example_sub.sock
[Install]
WantedBy=sockets.target
項目メモ
Descriptionログ出力の際などに使われる
ListenStreamリクエストを待ち受けるポートの指定。
Nginx 設定ファイルの pass_proxy で指定するものと同じ。
WantedBysockets.target

.service ファイルの作成

サブドメイン用に「example_sub.service」というファイルを作成します。

上記の「example_sub.socket」へ送られてきたリクエストの引き渡し先を定義します。

# example_sub.service
[Unit]
Description=gunicorn daemon
Requires=example_sub.socket
After=network.target
[Service]
User=root
Group=root
WorkingDirectory=/var/www/example.com/sub/html/dj_proj_venv/dj_proj
ExecStart=/var/www/example.com/sub/html/dj_proj_venv/bin/gunicorn --workers 3 --bind unix:/run/sample_django.sock dj_proj.wsgi:application
[Install]
WantedBy=multi-user.target
項目メモ
Descriptionログ出力の際などに使われる
Requires対応する .socket ファイル
Afterユニットが開始する順序 (ユニットの起動する順番) を設定。
After で指定したユニットがアクティブになると、このユニットを開始する。
User
Group
WorkingDirectorymanage.py があるディレクトリのフルパス?
ExecStartsystemctl start した時に実行するコマンド。
「$ gunicorn [OPTIONS] [WSGI_APP]」のフォーマットで記述。
上記ファイルでは venv 内の gunicorn のフルパスを指定し、オプションとして --workers と --bind を指定した上で Django の wsgi を実行している。
--workers:worker プロセスの数
--bind:bind 対象のサーバーソケット(.sock)。gunicorn のデフォルトは 127.0.0.1:8000
WantedBy大抵 multi-user.target で大丈夫

socket の待ち受けを開始します。

$ systemctl start example_sub.socket
$ systemctl start example_sub.service

6. Nginx の設定ファイルにサブドメインの処理を追記する

「sub.example.com」と「example.com」の処理が記述されています。

それぞれの proxy_pass の記述を見るとわかりますが、「sub.example.com」へのリクエストの場合は example_sub.sock のソケット、「example.com」へのリクエストの場合は example.sock のソケットへ処理が回されます。

server {
        listen 80;
        listen [::]:80;

        server_name sub.example.com;

        root /var/www/example.com/sub/html;
        index index.html;

        location / {
                proxy_set_header Host $http_host;
                proxy_set_header X-Forwarder-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_pass http://unix:/run/example_sub.sock;
        }

        location /static {
                alias /var/www/example.com/sub/html/static;
        }

}

server {
        listen 80;
        listen [::]:80;

        server_name example.com;

        root /var/www/example.com/html;
        index index.html;

        location / {
                proxy_set_header Host $http_host;
                proxy_set_header X-Forwarder-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_pass http://unix:/run/example.sock;
        }

        location /static {
                alias /var/www/example.com/html/static;
        }

}
ファイル内の概念解説
listenリクエストを受け入れる IP アドレスやポートの指定。
UNIX ドメインソケットのパスでも可。
server_nameリクエストを受け入れるサーバー名(ドメイン名)の指定。
複数のサーバー名をスペース区切りで設定可。
proxy_set_headerプロキシサーバーへ送るリクエストヘッダーの各フィールドの追加や書き換え。
$proxy_add_x_forwarded_forクライアントからのリクエストヘッダーの X-Forwarded-For フィールドに $remote_addr が追加されたもの。
クライアントからのリクエストヘッダーに X-Forwarded-For が無かった場合は $remote_addr と同じ。
$schemeリクエストスキーム。http か https。
proxy_passプロキシサーバーの指定。Gunicorn につなげるための UNIX ソケットを指定。
参照 Nginx Documentation

7. とりあえず図にしてみた

自分の理解のためにいろんなケースを図にしてみました。

1 つのアプリを Gunicorn のデフォルトポートで実行する場合

まずは一番シンプルな形。サブドメインとかなく、一つのドメインで一つのアプリを公開する形。

Gunicorn のデフォルトの bind 先は 127.0.0.1:8000 だそうなので、そこに対して Nginx から proxy_pass した場合です。

Django + Nginx + Gunicorn での設定

1 つのアプリを UNIX ドメインソケットで実行する場合

UNIX ドメインソケットの方が処理が速いということで(?)大半の記事で UNIX ドメインソケットを使用しているっぽいです。なので自分もこの方法を取っています。

この場合、当記事にも書いてある様に Systemd の .socket ファイルと .service ファイルを作成する必要があります。たぶん。実は必須ではないのかも。わかりません。

Django + Nginx + Gunicorn での設定

2 つのアプリを別々の UNIX ドメインソケットで実行する場合

複数のアプリをサブドメインで分けるという事でこの記事で紹介した方法がこちらです。

別々のソケットを用意し、そこに各サブドメインから proxy_pass で繋いであげれば OK です。

Django + Nginx + Gunicorn での設定

2 つのアプリを別々のポートで実行する場合

ただ、上記が出来るならそもそも UNIX ドメインソケットを使わなくても localhost(127.0.0.1)のポートを分ければ良いんじゃないかと思いました。自分では実際に試してませんが多分いける気がします。

Django + Nginx + Gunicorn での設定

ローカル環境の Django を本番環境(Ubuntu)へデプロイする方法

Mac のローカル環境で作成した Django プロジェクトをさくらのVPSで用意した Ubuntu 環境へ反映する際の流れをメモしています。

▶︎ Django のおすすめ VPS(仮装専用サーバー)はOSから決めるべし

下記が完了していることを想定しています。

  • Web サーバーの設定が完了済み(筆者は Nginx を使用)
  • 仮想環境を作るディレクトリが決めてある
  1. データベースを本番環境にコピーする
    • ローカル環境での作業
    • 本番環境での作業
  2. ローカル環境のパッケージを本番環境で再現
    • requirements.txt の作成
    • requirements.txt を本番環境へコピー
    • 本番環境で Python 仮想環境を作成
    • requirements.txt によるパッケージインストール
  3. settings.py の編集
    • ファイルを分割する必要性
    • 開発環境の runserver 実行時に settings_dev.py を指定
    • settings.py(本番環境用)の編集
  4. Django ソースコードを本番環境に配置
    • 本番環境への git のインストール
    • リモートリポジトリから本番環境へファイルをコピー
  5. gunicorn のインストール
  6. データベースのログイン情報を別ファイル記述
  7. 静的ファイルの配置

1. データベースを本番環境にコピーする

検証環境で準備したデータベース、テーブル、そしてデータを本番環境でも反映します。

ここはうまくやれば一行スクリプト書いて終了する方法もある様ですが、色々とエラーが出て逆にめんどくさそうだったのでステップバイステップでやっていきます。

ローカル環境での作業

まずローカル環境でコマンド「mysqldump データベース名 > dump.txt」を実行し、dump ファイルを作成します。

% mysqldump データベース名 > dump.txt                                                              
%

それを FTP か何かで本番環境へアップロードします。

本番環境での作業

本番環境に接続し、本番環境の MySQL でデータベースを作成します。

mysql> create database データベース名;

そしてコマンド「sudo mysql データベース名 < dump.txt」を実行します。

$ sudo mysql データベース名 < dump.txt
$

これで本番環境の MySQL にもローカル環境と同じデータベース、テーブル、そしてデータがコピーされました。

2. ローカル環境のパッケージを本番環境で再現

ローカル環境の Python 関連パッケージを本番環境でも再現するため、ローカル環境で requirements.txt ファイルを作成し、それを元に本番環境でパッケージインストールをさせます。

requirements.txt の作成

ローカル環境の Python 仮想環境へ入り、コマンド「pip freeze > requirements.txt」を実行します。

% source bin/activate
% pip freeze > requirements.txt

requirements.txt を本番環境へコピー

コマンド「scp requirements.txt 本番環境のユーザー名@本番環境のホスト名(もしくは IP アドレス):保存先のディレクトリ」を実行します。

だいぶ長いですが下記の例だとすると。。。

  • 本番環境のユーザー名: vpsadmin
  • 本番環境のホスト名: xx1-234-56789.vs.sakura.ne.jp
  • 保存先のディレクトリ: /var/www/example.com/html

下記の様になります。

scp requirements.txt vpsadmin@xx1-234-56789.vs.sakura.ne.jp:/var/www/example.com/html

無事本番環境に requirements.txt がコピーされました。

$ ls
requirements.txt

本番環境で Python 仮想環境を作成

本番環境のディレクトリへ移動し、Python の仮想環境を作成します。

$ python3.9 -m venv pythonvenv
$ ls
pythonvenv  requirements.txt

requirements.txt によるパッケージインストール

本番環境の Python 仮想環境に入った状態で、コマンド「pip install -r requirements.txt」を実行します。

(pythonvenv) $ pip install -r requirements.txt

すると requirements.txt の内容を元に Django を含めローカル環境で使っていたものと同じパッケージがインストールされます。

3. settings.py の編集

ファイルを分割する必要性

settings.py は本番環境と開発環境で内容が異なる部分が出てくるので、下記の様にファイルを分ける必要があります。(ファイル名は任意)

  • settings_common.py:開発環境と本番環境で共通の部分を記述
  • settings.py:本番環境でのみ適用する部分を記述
  • settings_dev.py:開発環境でのみ適用する部分を記述

settings.py と settings_dev.py それぞれに「from .settings_common import *」と記述し、共通部分を settings_common.py から読み込む様設定します。

開発環境の runserver 実行時に settings_dev.py を指定

runserver 実行時、デフォルトでは settings.py が設定ファイルとして読み込まれますが、今後開発環境(ローカル環境)では settings_dev.py を使う必要があります。

そのため、今後開発環境で runserver を実行する際は settings_dev.py を「runserver --settings=プロジェクトディレクトリ名.settings_dev」の形式で渡して実行します。

(pythonvenv) % python manage.py runserver --settings=some_project.settings_dev

settings.py(本番環境用)の編集

DEBUG の変更

False に変更

DEBUG = False

ALLOWED_HOSTS の変更

ドメイン名を設定。

ALLOWED_HOSTS = ['example.com']

Web サーバーで www.example.com から example.com のリダイレクトを行っていれば example.com のみを設定するだけで大丈夫です。

静的ファイルの本番環境での配置場所

STATIC_ROOT = '/var/www/examlpe.com/html/static'

4. Django ソースコードを本番環境に配置

本番環境への git のインストール(インストール済みの場合は不要)

本番環境でコマンド「sudo apt update」を実行しパッケージリストを更新、その後「sudo apt install git」で git をインストールします。

$ sudo apt update
$ sudo apt install git
Reading package lists... Done
Building dependency tree       
Reading state information... Done
git is already the newest version (1:2.25.1-1ubuntu3.1).
git set to manually installed.
0 upgraded, 0 newly installed, 0 to remove and 18 not upgraded.

リモートリポジトリから本番環境へファイルをコピー

プロジェクトフォルダをリポジトリに設定しているので、プロジェクトフォルダを配置したい場所へ移動した上で git clone を実行します。

$ git clone https://github.com/ユーザー名/リポジトリ名.git

これで本番環境にもプロジェクトフォルダがコピーされました。

ローカル環境からリモートリポジトリへファイルを反映させる方法はこちら↓

Github で開発ローカル → リモートリポジトリ → 本番ローカルに反映させる手順

5. gunicorn のインストール

本番環境でのみ必要となるものとして、アプリケーションサーバーがあります。今回は gunicorn を使います。

Python 仮想環境内でアプリケーションサーバーの gunicorn をインストールします。

(pythonvenv) $ pip install gunicorn

socket の待ち受けを開始します。

(pythonvenv) $ systemctl start example_sub.socket
(pythonvenv) $ systemctl start example_sub.service

今後作業する際は何かにつけて「pkill gunicorn」で gunicorn を終わらせた方が良いです。エラーが起きた時にいくらコードを修正しても治らないまま数時間経って、「pkill gunicorn」一発で治ったこともありました。

6. データベースのログイン情報を別ファイルに記述

MySQL のデータベースを使用したんですが、Git で管理する性質上、settings.py にデータベースのユーザー名とパスワード直接記述するのは危なそうなので、別ファイルに分けました。

Git で管理しない local_settings.py というファイル(ファイル名は自由)を作成し、そちらに機密情報をまとめます。

.gitignore ファイルに local_settings.py を含めた上で、local_settings.py を作ります。

# local_settings.py

DB_USER = 'DB ユーザー名'
DB_PASSWORD = 'DB パスワード'

その上で settings.py にそれら情報を import し、DATABASES の設定に使います。

# settings.py

from .local_settings import DB_USER as dbusr
from .local_settings import DB_PASSWORD as dbpw

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'sa',
        'USER': dbusr,
        'PASSWORD': dbpw,
        'HOST': '',
        'PORT': '',
    }
}

上記と別に、下記の様にしてサーバーの環境設定から取ってくる方法もあると思いますが、環境設定の上手い設定方法がわからなかったので今回はやっていないです。

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'データベース名',
        'USER': os.environ.get('DB_USER'),
        'PASSWORD': os.environ.get('DB_PASSWORD'),
        'HOST': '',
        'PORT': '',
    }
}

7. 静的ファイルの配置

コマンド「python manage.py collectstatic」を実行し、settings.py で STATIC_ROOT に設定したパスに静的ファイルを集約します。

$ python manage.py collectstatic

これでとりあえず本番環境でも動くと思います。

▶︎ Django のおすすめ VPS(仮装専用サーバー)はOSから決めるべし