偏った言語信者の垂れ流し

2015-08-27

[][]Djangoの管理コマンドを作る際にdjango-extensionsのcreate_commandコマンドを使う

Djangoのmanage.pyスクリプトのサブコマンド(管理コマンド、management command)を追加するには、Djangoアプリケーション内に「management/commands/コマンド名.py」のようにモジュールを作成しますが、頻繁に使うわけでもないので作法をよく忘れてしまってます。

django-extensionsにはcreate_commandという便利なコマンドがあり、ディレクトリの作成とスケルトンの作成をしてくれます。

Current Command Extensions — django-extensions 1.5.5 documentation

django-extensions/create_command.py at master ? django-extensions/django-extensions ? GitHub

Python 3.4、Django 1.8.4、django-extensions 1.5.5の環境で試しました。

django-extensinosを有効にする

django-extensionsはpipでインストールできます。

$ pip install django-extensions

settings.pyのINSTALLED_APPSに「django_extensions」を追加します。

# settings.py抜粋
INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django_extensions',  # この行を追加
    'myapp',
)

これでdjango-extensionsが有効になります。

create_commandを使う

create_commandコマンドでディレクトリスケルトンを作成します。

引数にはコマンドを追加したいアプリケーション名を指定します。--nameオプションで作成するコマンド名を指定することもできます(省略するとsample)

$ python manage.py create_command myapp --name=hello

myappというアプリケーションにhelloコマンドを作成しました。

ディレクトリとファイルのレイアウトは次のようになります。

$ tree myapp/
myapp/
├── __init__.py
├── __pycache__
│   ├── __init__.cpython-34.pyc
│   ├── admin.cpython-34.pyc
│   └── models.cpython-34.pyc
├── admin.py
├── management
│   ├── __init__.py
│   └── commands
│       ├── __init__.py
│       └── hello.py
├── migrations
│   └── __init__.py
├── models.py
├── tests.py
└── views.py

作成したコマンドを見てみる

作成したコマンドは、すぐに実行出来る状態です。

実行するとNotImplementedError(未実装の意味の例外)が発生して終了します。

$ python manage.py hello
Traceback (most recent call last):
  File "manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/home/tokibito/tmp/venv/lib/python3.4/site-packages/django/core/management/__init__.py", line 338, in execute_from_command_line
    utility.execute()
  File "/home/tokibito/tmp/venv/lib/python3.4/site-packages/django/core/management/__init__.py", line 330, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/tokibito/tmp/venv/lib/python3.4/site-packages/django/core/management/base.py", line 393, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/tokibito/tmp/venv/lib/python3.4/site-packages/django/core/management/base.py", line 444, in execute
    output = self.handle(*args, **options)
  File "/home/tokibito/tmp/mysite/myapp/management/commands/hello.py", line 7, in handle
    raise NotImplementedError()
NotImplementedError

コードを見てみます。

myapp/management/commands/hello.py

from django.core.management.base import BaseCommand

class Command(BaseCommand):
    help = "My shiny new management command."

    def handle(self, *args, **options):
        raise NotImplementedError()

handleメソッドにコマンドで実行したい内容を記述できます。

例えば、「Hello, world!」とprintするコマンドであれば、次のようになります。

from django.core.management.base import BaseCommand

class Command(BaseCommand):
    help = "My shiny new management command."

    def handle(self, *args, **options):
        print("Hello, world!")
実行結果
$ python manage.py hello
Hello, world!

このように管理コマンドを追加したいときにちょっとだけ楽をできます。使うと良いかもしれません。

参考

2015-08-07

[]ペアプログラミングをやってみた感想

3日ほど、社内にてペアプログラミングで作業してたので少し感想を書き残しておきます。

ペアプログラミングの方法については、hyuki先生のWikiにあった翻訳記事を参考にしました。

ペアプログラミングのやりかた

ペア相手は、私がリーダーという形で同じチームになり、私よりも社内での開発経験が短い人です。

ペアプログラミングを実施した狙い

ペアプログラミングを実施した狙いとしては、以下の通りです。

  • 案件ソースコードに対して、どこから手を付けたら良いのかを把握してもらう
  • チケット駆動開発やコード設計、テストコードをどのように書いていくかなど、開発の仕方に慣れてもらう
  • 作業の効率を上げるためのツールの使い方など、経験のある人がどのように作業を進めているのかを知ってもらう

気をつけた点

実施に際し、気をつけたのは次の通りです。

  • 期限までに余裕のある作業を対象とすること(最悪、私が期限までに何とか出来るもの)
  • 作業内容(入力、処理内容、出力)の説明とプログラムの詳細設計(処理をどのように分割し、結合するか)は、文章でチケットに記載、作業開始前に共有すること
  • 品質を落とさないこと(教育目的だからという理由で品質を落とすことはできない。どのぐらいの品質で、作ればいいのかを知ってもらう意味もあるため。)

重要としなかったこと

また、以下の点についてはコストとして最初から認識し、今回の作業では重要としないことにしました。

  • 作業効率(私一人で作業したほうが早いですが、今回は教育目的としたため、作業効率は求めませんでした。時間的余裕があったのも理由です)

感想

だいたい想定通り作業を進められたのでよかったように思います。事前に設計をきっちりやっていたため、試行錯誤する時間はほとんどありませんでした。

狙いがうまくいったかどうかは、まだわかりません。

特に周囲から指摘はされなかったけれど、ペアプログラミングだと会話が多くなるので、声量を落とさないと結構騒がしくなるのではと思いました。弊社の場合はオフィスが基本的に静かなので、余計に声が通ってしまうんですよね。

今回は作業効率を重視しませんでしたが、作業効率の向上を目的としたペアプログラミングもいつかやってみたいと思いました。

2015-07-21

[][]DjangoテンプレートのstaticタグとSTATIC_URLコンテキスト変数の使い分け

Djangoテンプレートで静的ファイルのパス指定時にdjango.contrib.staticfilesのstaticタグとSTATIC_URLのどちらを使うべきか、という質問があったので、少し調べました。この記事はDjango 1.8のドキュメントとコードを参考にしています。

結論としてはstaticタグを使ったほうが、テンプレートの再利用性は高いです。

STATIC_URLコンテキスト変数

STATIC_URLコンテキスト変数は、Djangoの設定ファイル(settings)に指定したSTATIC_URLがそのまま格納されています。

django/template/context_processors.py#L74

また、コンテキスト変数の参照を避けるのであれば、get_static_prefixタグを使ってSTATIC_URLの値を取得することもできます。

staticタグ

staticタグは、デフォルトテンプレートタグではないので、使用する際にはloadタグによる指定が必要です。

{% load staticfiles %}

staticタグには、コンテキスト変数文字列を指定できます。

また、staticタグの場合、STATIC_URLと直接結合するのではなく、ストレージクラスurlメソッドを呼び出して、その結果が出力として使われます。

ストレージ側にURLの生成を任せる場合は、staticタグを使う必要があります。

django/contrib/staticfiles/templatetags/staticfiles.py#L9

settings.STATICFILES_STORAGEでストレージを変更することを想定するなら、STATIC_URLよりもstaticタグを使ったほうが良いようです。

参考

2015-07-05

[][]PostgreSQLでウィンドウ関数rankを試す

SQLのウィンドウ関数を使うと、グループ化しつつ、グループ内でランキングを作成するのが簡単と聞いていたので、PostgreSQLで試していた。

PostgreSQLのバージョンは、9.4.4

対象のデータ

ウィンドウ関数を試すにあたって、商品、カテゴリ、売上という3つのテーブルを作成し、データを投入した。

ER

A5:SQL Mk-2を使って作成。

f:id:nullpobug:20150705151518p:image

DDL

ER図から出力したDDLを使う。

-- 売上
drop table if exists sales cascade;

create table sales (
  id serial not null
  , item_id integer not null
  , category_id integer not null
  , price integer not null
  , constraint sales_PKC primary key (id)
) ;

-- 商品
drop table if exists item cascade;

create table item (
  id serial not null
  , name character varying(50) not null
  , price integer not null
  , category_id integer not null
  , constraint item_PKC primary key (id)
) ;

-- カテゴリ
drop table if exists category cascade;

create table category (
  id serial
  , name character varying(50) not null
  , constraint category_PKC primary key (id)
) ;

データの投入

各テーブルに、集計できる最低限ぐらいのデータを入れておいた。

insert into category(id,name) values (1,'野菜');
insert into category(id,name) values (2,'果物');
insert into item(id,name,price,category_id) values (1,'じゃがいも',30,1);
insert into item(id,name,price,category_id) values (2,'にんじん',80,1);
insert into item(id,name,price,category_id) values (3,'たまねぎ',50,1);
insert into item(id,name,price,category_id) values (4,'りんご',70,2);
insert into item(id,name,price,category_id) values (5,'みかん',30,2);
insert into sales(id,item_id,category_id,price) values (1,1,1,30);
insert into sales(id,item_id,category_id,price) values (2,1,1,30);
insert into sales(id,item_id,category_id,price) values (3,1,1,30);
insert into sales(id,item_id,category_id,price) values (4,1,1,30);
insert into sales(id,item_id,category_id,price) values (5,2,1,80);
insert into sales(id,item_id,category_id,price) values (6,2,1,80);
insert into sales(id,item_id,category_id,price) values (7,3,1,50);
insert into sales(id,item_id,category_id,price) values (8,4,2,70);
insert into sales(id,item_id,category_id,price) values (9,4,2,70);
insert into sales(id,item_id,category_id,price) values (10,5,2,30);
insert into sales(id,item_id,category_id,price) values (11,5,2,30);
insert into sales(id,item_id,category_id,price) values (12,5,2,30);

ウィンドウ関数のrankを使ったクエリ

集計結果にランクをつけるクエリを試した。

with sales_report as (
  select
    I.name
    , I.category_id
    , count(I.id) cnt
    , sum(I.price) price 
  from
    sales S 
  inner join item I 
    on S.item_id = I.id 
  inner join category C 
    on S.category_id = C.id 
  group by
    I.id
),
item_rank as ( 
  select
    sales_report.*
    , rank() over (partition by sales_report.category_id order by cnt) count_rank
    , rank() over (partition by sales_report.category_id order by price) price_rank 
  from
    sales_report
) 
select
  * 
from
  item_rank 
where
  count_rank < 3;

1つ目のsales_reportは、売上テーブルのデータを商品IDでグループ化して、売上の合計金額と個数を出す。

2つ目のitem_rankは、rank関数を使って個数と合計金額をカテゴリID毎にランク付けする。

3つ目は、個数のランクが3未満のものだけに絞り込む。

実行結果

f:id:nullpobug:20150705151803p:image

参考

2015-06-26

[]MikroTik RB750GLを購入

タイトルの通り、MikroTikのルーター (RouterBOARD)、RB750GLを購入しました。

RouterBoard.com : RB750GL

社内で希望者を集めて共同でAmazon.comから購入。海外発送で送料込み、1台あたり7400円ぐらいでした。

電源アダプタのプラグがCタイプなんですが、日本のコンセントに合うAタイプに変換するプラグも付属していました。

f:id:nullpobug:20150626230258p:image

f:id:nullpobug:20150626225726p:image

初期設定では、ポート1にインターネット側のLANケーブル、ポート2〜5に利用するコンピュータ側のLANケーブルをつなぐと、DHCPでアドレスが割り当てられてすぐに使える状態でした。

パッケージの背面に説明があります。

f:id:nullpobug:20150626230333p:image

デフォルトIPアドレスは 192.168.88.1 となっていました。

家のルーターハングアップすることが増えてたので、取り替えてしばらく使ってみようと思います。

参考

RouterBOARD はし?めの一歩