django-private-mediaでMEDIA_ROOTをプライベート化する

django-private-mediaを使用し、DjangoのMEDIA_ROOTを認証アクセス(非パプリック化)させる際のメモです。

システム構成

フロントエンドにHTTPリバースプロキシとしてnginxを配備し、後段にDjangoのアプリケーションサーバとしてGunicornを利用する構成前提。

手順

まずはpipを使い、django-private-mediaのインストール

$ pip install django-private-media

インストールが完了後、Djangoのsettings.pyへdjango-private-media用の設定を追加。

# settings.py
PRIVATE_MEDIA_URL = '/privates/'
PRIVATE_MEDIA_ROOT = '/opt/django/private'
PRIVATE_MEDIA_SERVER = 'app.permissions.NginxXAccelRedirectServer'
PRIVATE_MEDIA_PERMISSIONS = 'app.permissions.LoginRequireMedia'

その後、プライベートメディアを紐付けるモデルへPrivateMediaStorageをセットします。

# models.py

from django.db import models
from private_media.storages import PrivateMediaStorage

class Car(models.Model):
    photo = models.ImageField(storage=PrivateMediaStorage())

今回は、PRIVATE_MEDIA_URLで指定されたURLへアクセスされた際に、PRIVATE_MEDIA_SERVERで定義したクラス(NginxXAccelRedirectServer)にて、NginxのX-Accel-Redirect機能を利用し、DjangoではなくNginx経由で直接メディアファイルを配信します。

また、PRIVATE_MEDIA_PERMISSIONSで定義したクラス(LoginRequireMedia)では、アクセス時の認証処理を実装します。


# app.permissions.py

# -*- coding:utf-8 -*-
import mimetypes
from user.models import UserProfile
from private_media.servers import ApacheXSendfileServer
from django.http import Http404
from django.http import HttpResponse


class LoginRequireMedia(object):
    def has_read_permission(self, request, path):
        u = request.user

        if not u.is_authenticated():
            return False
        elif u.is_superuser:
            return True
        elif UserProfile.objects.get(user=u).expire_check():
            # ログイン済みで、有効期限内のユーザをチェックしている
            return True
        else:
            raise Http404('File not found')


class NginxXAccelRedirectServer(ApacheXSendfileServer):
    def serve(self, request, path):
        # NginxのX-Accel-Redirect用locationをセット
        fullpath = '/private/' + str(path)

        response = HttpResponse()
        response['X-Accel-Redirect'] = fullpath

        # From django-filer (https://github.com/stefanfoulis/django-filer/):
        # This is needed for lighttpd, hopefully this will
        # not be needed after this is fixed:
        # http://redmine.lighttpd.net/issues/2076
        stream = 'application/octet-stream'
        response['Content-Type'] = mimetypes.guess_type(path)[0] or stream

        return response

あとは、Nginx側のコンフィグにて、X-Accel-Redirect用のlocationをセットすればOKです。

# nginx.conf
location /private/ {
  internal;
  root   /opt/django/;
}


以上で、PRIVATE_MEDIA_ROOTで指定されたディレクトリへのアクセスは認証が必要となり、メディアファイルの保護が実現可能となります。

Bootstrap3へのjPlayer導入

Bootstrap3で構成されているサイトへjPlayerを導入したいけれど、jPlayerのデフォルトデザインや公式スキンはレスポンシブ対応されていないため、Bootstrapに組み込んでレスポンシブなデザインにフィットするように実装してみました。

使えそうなjPlayerスキンを調査した結果jplayer-bootstrap-skinが良さそうでしたが、デザインがファンキー過ぎてそのままでは利用できなかった為、カスタマイズして利用すること。

利用したモジュールは主に下記3つ。

assetsというディレクトリを作成し、その配下にJavaScriptやCSSを全て配置し、対象モジュールをHTMLから読み込む前提。

HTML

CSS

HTMLの67行目と101行目でBootstrapのグリッドデザインを指定し、曲名リストの出力位置を画面サイズによって変更しています。

今思うと、Bootstrap-playerなどを使ってHTML5をベースに作ったほうがすんなり出来たような気がしますが、jPlayer側のコールバックとプレイリスト機能を利用する必要があったので今回はこれでOK。

CSS3 スタンダード・デザインガイド【改訂第2版】を読んでみた

自分のスキルセットがサーバサイド(いわゆるバックエンド)側に偏りすぎているので、フロントエンドのスキルを底上げしたくて、とりあえずCSS3を基礎から学ぼうと購入。

本書は2013年4月時点でのCSS3の機能や使い方に関して、各プロパティ毎に豊富なサンプルが記載されています。

全編カラーなので非常に見やすく、またプロパティに対するスマートフォン含めた対応ブラウザも一覧化されているので読み物としては勿論、リファレンス的な使い方でも十分価値がある一冊です。(むしろリファレンス的な位置付け?)

本書の構成としては、基本レイアウト、コンテンツのデザインから特殊効果まで全6章で構成されており、CSS3については、ほぼ網羅されていてかなりボリュームがあるので、一回サラッと通読した後に、実業務で詰まった箇所を掘り下げる形で理解度を深めていこうかなと思っています。

個人的には@mediaの指定をネストして記述する手法など、単純なプロパティの紹介以外に即実践で役立ちそうなTips的な手法が非常に参考になりました。

フロントエンド・エンジニアは勿論の事、デザイナーからデザインテンプレートを受け取って、業務をこなすサーバサイドエンジニアも読んで損は無い良書だと思います。

AngularJS公式チュートリアルメモ - その①

AngularJS公式チュートリアルのメモ。

実施したのは2013年12月29日付けで最新版のv1.2.7。

実行環境はMac OS前提。

チュートリアル実行準備

まずGithubからチュートリアル用ソース一式をClone。

git clone https://github.com/angular/angular-phonecat.git

実行環境としてNode.jsが必要となるため、Macにインストールされていない場合は公式サイトからインストーラーを使ってインストールする。

また、チュートリアルではユニットテストも併せて実施することを推奨しており、テスト実行環境であるKarmaも併せてインストールしておく。

sudo npm install -g karma
npm install

これでチュートリアルを実施する準備が完了。

Step0. Bootstraping

チュートリアルは各ステップ毎にワークディレクトリをリセットしすすめる。

cd ./angular-phonecat
git checkout -f step-0

リセット後に、nodeを起動して確認用のテストWebサーバを立ち上げる。

./scripts/web-server.js

実行後、http://localhost:8000/app/index.htmlへアクセスしてページが表示されることを確認。

app/index.htmlの確認

<!doctype html>
<html lang="en" ng-app>
<head>
<meta charset="utf-8">
<title>My HTML File</title>
<link rel="stylesheet" href="css/app.css">
<link rel="stylesheet" href="css/bootstrap.css">
<script src="lib/angular/angular.js"></script>
</head>
<body>

<p>Nothing here {{'yet' + '!'}}</p>

</body>
</html>
<html ng-app>

このHTMLがAngularJSのアプリケーションであることを宣言。

<script src="lib/angular/angular.js">

AngularJS本体をロードすることで、{{の中括弧で囲まれている部分がAngluarJSで解釈されるコードとなり、ここでは単純に文字列「yet」と「!」を+で結合して表示させている。

<p>1 + 2 = {{ 1 + 2 }}</p>

新たに上記Pタグを適当な位置に挿入すると{{の中括弧で囲まれている部分が計算式としてAngularJS側で評価され「3」が表示されるのが確認できる。

Step1. Static Template

ワークディレクトリをステップ1用にリセット

cd ./angular-phonecat
git checkout -f step-1

ステップ0同様にnodeを起動して、http://localhost:8000/app/index.htmlが正常に表示されることを確認。

app/index.htmlへ新たにPタグを追加して、静的コンテンツとしてページが正常に更新されることを確認する。

<p>Total number of phones: 2</p>

Step2. Angular Template

ワークディレクトリをステップ2用にリセット

cd ./angular-phonecat
git checkout -f step-2

ステップ0同様にnodeを起動して、http://localhost:8000/app/index.htmlが正常に表示されることを確認。

app/index.htmlの確認

<html ng-app="phonecatApp">
<head>
...
<script src="lib/angular/angular.js"></script>
<script src="js/controllers.js"></script>
</head>
<body ng-controller="PhoneListCtrl">

<ul>
<li ng-repeat="phone in phones">
{{phone.name}}
<p>{{phone.snippet}}</p>
</li>
</ul>

</body>
</html>

AngularJS本体に加え、新たにコントローラー用のJSが読み込まれている。

<script src="js/controllers.js"></script>

bodyタグでng-controllerを指定することで、先程読み込んだcontrollers.jsに定義されているファンクションPhoneListCrlrを呼び出す。

<body ng-controller="PhoneListCtrl">

次にng-repeatを使用し、コントローラー内で定義されているスコープphonesモデルに対してループ処理を実装し、name属性とsnippet属性を表示している。

<li ng-repeat="phone in phones">

app/js/controllers.jsの確認

var phonecatApp = angular.module('phonecatApp', []);

phonecatApp.controller('PhoneListCtrl', function ($scope) {
  $scope.phones = [
    {'name': 'Nexus S',
      'snippet': 'Fast just got faster with Nexus S.'},
    {'name': 'Motorola XOOM™ with Wi-Fi',
      'snippet': 'The Next, Next Generation tablet.'},
    {'name': 'MOTOROLA XOOM™',
      'snippet': 'The Next, Next Generation tablet.'}
  ];
});

まず、AngularJSモジュールとしてphonecatAppを定義。

phonecatAppのコントローラ「PhoneListCrrl」をファンクションとして定義し、先程index.htmlで表示させたphonesがモデルとして登録されているのが確認できる。

テストについて

チュートリアルでは各ステップで実装された、コードに対してKarma + Jasmineを使ったテスト例が紹介されている。

test/unit/controllersSpec.jsの確認

/* jasmine specs for controllers go here */

describe('PhoneCat controllers', function() {
  describe('PhoneListCtrl', function(){
    beforeEach(module('phonecatApp'));
    it('should create "phones" model with 3 phones', inject(function($controller) {

      var scope = {},

        ctrl = $controller('PhoneListCtrl', {$scope:scope});
      expect(scope.phones.length).toBe(3);
    }));
  });

});

テスト実行前にphonecatAppモジュールを読み込み、ファンクションとして定義されているPhoneListCtrlコントローラーのphonesモデルが3件登録されていることを確認している内容。

テストの実行

ワークディレクトリから./scripts/test.shを実行することで、Karma サーバが起動されて自動的にユニットテストが走る。

./scripts/test.sh
Starting Karma Server (http://karma-runner.github.io)
-------------------------------------------------------------------
WARN [karma]: Port 9876 in use
INFO [karma]: Karma v0.10.8 server started at http://localhost:9877/
INFO [launcher]: Starting browser Chrome
INFO [Chrome 31.0.1650 (Mac OS X 10.9.1)]: Connected on socket 65y3F2iECMUSNlxmu4t8
Chrome 31.0.1650 (Mac OS X 10.9.1): Executed 1 of 1 SUCCESS (1.048 secs / 0.015 secs)

「Executed 1 of 1 SUCCESS 」とテストが1件実行され、正常に通っていることが確認できる。

試しにtest/unit/controllersSpec.js内のtoBe(3)の箇所をtoBe(4)などに変更してみると、テストがコケる。

なお、Karmaサーバに関するコンフィグはワークディレクトリの ./config/karma.conf.jsに配置されている。

“私は“カップルなんて、お金も財産もなにもないところからスタートするべきだ”と思っているので、お金のことなど関係なく、誠実でやさしい男性であれば、誰と結婚してもいいと娘には言っています。お金は、はたらけば手に入ります。商才があるとか、ないとか、いろんな要素がお金儲けにはありますが、要するにはたらけば、どうにかなるのです。でも、誠実さとかやさしさというものは、お金でどうにかなるものではないのです。絶対に”

海外赴任を経験した人がよく言うのですが、父親がコミュニティ活動にあまり参加しないのは先進国では日本くらいです。自営業や商店街の店主は地域の会合やPTAに参加しても、サラリーマンはほとんど参加しない。海外であれば、企業人として成功した人がNGOやNPOのボードメンバーとして活躍し、過去の体験をビジネス以外の分野で活かそうとすることも珍しくはありません。企業のトップが地元交響楽団の理事になる、ということも多い。

しかし、日本の企業人はいつまでも相談役などにとどまって、会社にしがみつきます。その結果、会社の中で学んだことがちっとも社会に還元されず、コミュニティの衰退が進んでいく。企業活動を根底で支えているのは社会資本の厚さですから、これは長い目で見て、企業にとっても決していいことではありません。

みんなには内緒だが、うちの会社は週休三日制を敷いている。

土日プラス平日にひとつ休みを取る。

この制度の主目的は、「社会的にエース級の戦力だけど、激務に潰されて心を壊してしまったひと」や、「一騎当千だが、結婚出産などで会社との折り合いにストレスを感じている人」の居場所を作れたらいいなという自分含めたラッセル怠惰への讃歌読書会メンバーで構成された経営陣によるものだ。

うちみたいなベンチャーは、そういうところを工夫しないと人材の囲い込み戦略で負けてしまうし。みたいなささやかな裏目的もある。こんな工夫だけで意外とやばい人材が集まる。

という話を、こっそり知り合いにすることがある。すると決まってこう言われる。

「週三日休みって生産性はどうなるのさ?」とか「平日休みと抜けられない打ち合わせが被ったらどうするの?」とかそういうものだ。

だいたいは、そこが問題なんですよねと軽く切り返した後に早くもストーブを出した話とか、ヒトカラで黙々と精密採点DXをやる楽しい休日の話にスライドするのだが、実は回答はある。

・週休三日と生産性の両立問題

通常二十営業日/月でこなす仕事を十六営業日でクリア出来ない人材はそもそも採用していないので、問題はない。ほとんどのメンバーは十営業日未満でクリアしている。

・打ち合わせや、電話対応と平日休みの被り問題

まず、自分しか仕事内容が分からない状態が発生した時点で、チームワークの著しい欠如と見做して評価が下がる方針がある。どんな職務内容でも、仕事のメールであれば自分以外の誰ひとりにも見られたくないメールなど有り得ない。

うちの会社に限らず、自分が急死しても仕事が止まらない体制を志向していない人材は論外だ。そこから派生して、関連性の薄いプロジェクトのCCメールを鬱陶しいと言ってしまう人材も方針上NGだ。彼らはチームワークの本質を理解していない。細やかな相互マネジメントで管理コストの削減は全メンバーの必須業務だ。

そういった方針に従えば、「休日取引先から個人携帯に連絡が来る」「打ち合わせに自分が行くしかない」状態がそもそも発生しないことになる。問題ではない。

33 ヒップアタック(茸)2013/09/24(火) 22:56:21.36 ID:hdXMTMFu0
仕様を決めてから納期を決めろ
仕様を少しでも変更したら納期のばせ
仕様の再考が発生したら納期のばせ


これは徹底しろ
これは客のためでもある