Django を AWS ECS(FARGATE) + ELB で動かすときの ALLOWED_HOSTS の設定
めちゃくちゃ苦戦したのでメモ。
困ったこと
Django のアプリを ECS(FARGATE) 上で動かすと Invalid HTTP_HOST header: '10.0.X.Y'. You may need to add '10.0.X.Y' to ALLOWED_HOSTS.
みたいなエラーが出て、 ELB のヘルスチェックで落ちる。
だからといって、 ALLOWED_HOSTS = ['*']
みたいにするのはセキュリティ的にも気持ち的にもやりたくない。
解決策
Stack overflow に同じ問題にぶち当たっている人がいた。
通常の ALLOWED_HOSTS
の設定の下に、以下を追加すれば良いみたい。
try: resp = requests.get('http://169.254.170.2/v2/metadata') data = resp.json() container_meta = data['Containers'][0] EC2_PRIVATE_IP = container_meta['Networks'][0]['IPv4Addresses'][0] ALLOWED_HOSTS.append(EC2_PRIVATE_IP) except requests.exceptions.RequestException: pass
これで当該のエラーが出なくなって、タスクが再起動されまくる悪夢から解放されました!
参考URL
Djangoでユーザがグループに所属しているかを判定する
Djangoで、特定のグループに所属しているユーザのみにリンクやページを表示する場合に、以下のような方法があります。
テンプレートタグを用いる方法
テンプレートタグを用いると、ユーザの所属するグループによってページ内の情報やリンクを出しわけることができます。
使い方
以下のin_group.py
というスクリプトを、アプリ内のtemplatetags/
ディレクトリに格納。
hoge/templatetags/in_group.py
from django.template import Library register = Library() @register.filter def in_group(user, team_name): if user.groups.filter(name=team_name).exists(): return True else: return False
テンプレート内でin_group
を呼び出します。
{% load in_group %} {% if user|in_group:"developer" %} <!-- developerというグループに所属するユーザ用のリンク --> <a href="#">開発者向けページ</a> {% else %} <!-- その他のユーザ用のリンク --> <a href="#">権限を申請</a> {% endif %}
if
文と組み合わせることで、特定のグループのユーザのみに情報を表示したり、隠したりすることができます。
user_passes_testを利用する方法
上記のテンプレートタグを利用する方法では、リンクや情報を特定のグループのユーザのみに表示することはできますが、特定ページへのアクセスを制限することはできません。
そこで、user_passes_test
というデコレータを利用することで、特定の条件を満たしたユーザのみがページにアクセスできるようにします。
使い方
views.py
from django.contrib.auth.decorators import login_required, user_passes_test from django.shortcuts import render @login_required @user_passes_test(lambda user: user.groups.filter(name='developer').exists()) def developer_page(request): u'''ログイン済みでdeveloperグループに所属するユーザのみアクセス可能なページ''' return render(request, 'developer.html', {})
user_passes_test
の条件を満たさないユーザはログインページへ飛ばされます。スタッフ権限を持たないユーザが管理サイトへログインしようとした際の挙動と同じですね。
参考URL
Django 1.8にAxesを追加する
連続ログイン失敗時にアカウントをロックする機能を追加できるプラグインdjango-axesのインストール手順です。
環境
- Django 1.8
django-axes のインストール
$ pip install django-axes
settings.pyの編集
vi settings.py
INSTALLED_APPS = ( # 以下の1行を追記 'axes', ) MIDDLEWARE_CLASSES = ( # 以下の1行を追記 'axes.middleware.FailedLoginMiddleware' ) # ログイン失敗は連続5回まで AXES_LOGIN_FAILURE_LIMIT = 5 # ログインを連続失敗した場合は24時間アカウントロック AXES_COOLOFF_TIME = 24 # AXES_LOGGERはデフォルトでは'axes.watch_login'となっているので、 # ログイン失敗をログ出力するためには、 # 'axes.watch_login'というロガーを作成するか # 独自のロガーインスタンスにて行う AXES_LOGGER = 'custom_logger'
urls.pyの編集
vi urls.py
from django.conf.urls import include, url from django.contrib.auth import views as auth_views from axes.decorators import watch_login urlpatterns = [ url(r'^$', hoge_page), url(r'^hoge/', include('hoge.urls')), ... # ログイン画面でwatch_loginデコレータを設定 url(r'^login/$', watch_login(auth_views.login), {'template_name': 'auth/login.html'}, name='login'), ]
templateの編集
vi auth/login.html
<!-- ログインフォームのPOST先URLを以下のように設定 --> <!-- url 'django.contrib.auth.views.login' とするとエラーを吐くので注意 --> <form role="form" method="post" action="{% url 'login' %}?next={{ next }}"> {% csrf_token %} {{ form.non_field_errors }} <div> <input type="text" placeholder="ユーザ名" id="id_username" name="username" maxlength="254" required autofocus> </div> <div> <input type="password" placeholder="パスワード" id="id_password" name="password" required> </div> <input type="submit" value="ログイン"> </form>
マイグレーションの実行
$ ./manage.py migrate
マイグレーションを実行すると、管理サイト内にAxesというAppとAccess attempts, Access logsという項目が表示されます。
参考サイト
djangoのペジネータで、現在のページの前後数ページのみをリンクとして表示する
Djangoのペジネータを使ってリンクを表示するときに、Google検索みたいに前後数ページのリンクを表示する方法です。
Googleの場合は前5ページ、後4ページを表示していますね。
環境
- Django 1.8
ペジネータで前後数ページを表示
views.py
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger def some_page(request): some_list = SomeObject.objects.all() page = request.GET.get('page') # 現在表示中のページを取得 # 1ページに10件ずつ表示 paginator = Paginator(some_list, 10) try: paged_list = paginator.page(page) except PageNotAnInteger: paged_list = paginator.page(1) except EmptyPage: paged_list = paginator.page(paginator.num_pages) return render(request, 'some_page.html', { 'paged_list ': paged_list })
some_page.html
<!-- ペジネータここから --> {{ paged_list.start_index }}件目〜{{ paged_list.end_index }}件目を表示中 (全{{ paged_list.paginator.count }}件)</h4> <div class="paginator"> {% if paged_list.has_previous %} <a href="?page={{ paged_list.previous_page_number }}">前へ</a> {% endif %} {% for page in paged_list.paginator.page_range %} <!-- 現在のページへはリンクを貼らない --> {% if page == paged_list.number %} <strong>{{ page }}</strong> <!-- 現在表示しているページの前後5ページへのリンクを表示 --> {% elif paged_list.number|add:"-5" <= page and paged_list.number|add:"5" >= page %} <a href="?page={{ page }}">{{ page }}</a> {% endif %} {% endfor %} {% if paged_list.has_next %} <a href="?page={{ paged_list.next_page_number }}">次へ</a> {% endif %} </div> <!-- ペジネータここまで -->
こんな感じで表示されます。
Djangoの初回マイグレーション時に relation "auth_user" does not exist というエラーが発生する場合
環境
- Ubuntu 14.04
- PostgreSQL 9.3
- Django 1.8
初回マイグレーション時のエラー
Djangoのプロジェクトをコピーしてきて、$ python manage.py migrate
しようとすると、以下のようなエラーが発生した。
Synchronizing apps without migrations: Creating tables... Creating table app1_model1 Creating table app2_model2 ... Running deferred SQL... Traceback (most recent call last): File "./manage.py", line 10, in <module> execute_from_command_line(sys.argv) ... django.db.utils.ProgrammingError: relation "auth_user" does not exist
どうも、自分で作ったModelの中でdjango.contrib.auth.models.User
へのリレーションを張っているのに、User
のテーブルがまだ作られていないことがマズいらしい。
ということで、先にauth
のマイグレーションを行うことで、この問題を回避できます。
$ python manage.py migrate auth $ python manage.py migrate