ラベル python の投稿を表示しています。 すべての投稿を表示
ラベル python の投稿を表示しています。 すべての投稿を表示

2015年7月27日月曜日

Djangoでajaxを利用する方法

目的:Djangoでajaxを利用したい時にすぐ参照できるように、必要な情報をコンパクトにまとめておく。
対象読者:Djangoユーザー
概要:DjangoにはCSRF対策が入っている関係上、ajaxを利用するためには以下のような作業が必要です。

動作サンプル:https://whiteblack-cat.info/ja-jp/ajax_sample/
ソースコード:https://github.com/ccat/django_ajax_sample



1. 背景(またはどうしてajaxを利用したいのにできないのか)

Djangoには入力フォームの生成と入力データの取得を補助する強力な仕組みが存在するため、 ちょっとしたアプリを作成する分には何の苦労もなくPOSTでデータを送信することができます。 しかし、JavascriptでPOST送信を行おうとすると、途端に403 Forbiddenで悩まされることになります。

これは、DjangoがCSRF対策が デフォルトで有効になっているためです。 CSRF対策をOFFにすることも可能ですが、セキュリティ上の脆弱性が出来てしまうため、リスクを十分に把握した上でなければお勧めできません。 そこで、CSRF対策をONにしたままajaxを利用する方法を記載します。
なお、本内容はhttps://docs.djangoproject.com/en/1.7/ref/contrib/csrf/#ajax の内容を、より具体的に整理したものです。
Django Webアプリケーション一式のサンプルコードは以下からダウンロードできます。
https://github.com/ccat/django_ajax_sample
テンプレートとurl、viewがセットになっており、そのままDjangoプロジェクトに組み込めば動作をテストできます。
また、本サンプルコードは下記で動作しています。
https://whiteblack-cat.info/ja-jp/ajax_sample/

2. 事前作業(ajaxを利用するために1度行えばよい作業)

まず、以下のコードをstaticかどこかに保存してください。
こちらからダウンロードするのが簡単です。

//Cite : https://docs.djangoproject.com/en/1.5/ref/contrib/csrf/#ajax

function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie != '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) == (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
var csrftoken = getCookie('csrftoken');

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
function sameOrigin(url) {
    // test that a given url is a same-origin URL
    // url could be relative or scheme relative or absolute
    var host = document.location.host; // host + port
    var protocol = document.location.protocol;
    var sr_origin = '//' + host;
    var origin = protocol + sr_origin;
    // Allow absolute or scheme relative URLs to same origin
    return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
        (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
        // or any other URL that isn't scheme relative or absolute i.e relative.
        !(/^(\/\/|http:|https:).*/.test(url));
}
$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
            // Send the token to same-origin, relative URLs only.
            // Send the token only if the method warrants CSRF protection
            // Using the CSRFToken value acquired earlier
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});

次に、ajaxを利用するページのテンプレートに、下記コードを埋め込んでください。 なお、1行目で分かる通り、本内容はjqueryを利用することを前提としています。 また、2行目は上記のスクリプトを/static/js/配下のdjangoajax.jsと言う名称で保存したと仮定しています。別の場所、名称で保存した場合は変更してください。

<script type="text/javascript" charset="UTF-8" src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script type="text/javascript" charset="UTF-8" src="/static/js/djangoajax.js"></script>

この2つを行えば事前作業は終了です。

3. ajaxの使い方(ajaxを利用するために毎回必要な作業)

以下のサンプルコードのように、jqueryのpostとコールバックを利用して、普段通りajaxのコードを作成してください。

<script type="text/javascript">
function callback(data, status) {
  $("#echoResult").text("status:"+status+" data:"+data);
}

function sends() {
  $.post('/ajax_sample/input/',{"echo": $("#echo").val()},callback,"html");
}
</script>

<form action="#" method="post">
  {% csrf_token %}
  <input type="text" name="echo" id="echo" />
  <input type="button" value="submit" onclick="sends()" />
</form>

4. 仕組み(または如何にしてajaxを利用できるようにしているのか)

CSRF対策では、ページが表示されるたびにランダムな文字列(CSRF Token)を生成し、それをPOSTに含めることで 「ページを表示した人」と「POSTを送信してきた人」を識別しています。 jqueryデフォルトのpost関数ではCSRF Tokenを送信しないため、そこでDjangoがエラーを出力します。

そこで、djanoajax.jsではCSRF TokenをCookieから読み込み、jqueryのpost関数が呼び出された時に、 CSRF TokenをPOSTのデータに含めるように変更を行っています。

2015年3月10日火曜日

django-mptt (0.6.x) README.rst

Djangoでツリー構造を作る時に便利なdjango-mpttのreadme.rstの和訳です。 django-mpttは便利なのですが、日本語の資料が少なそうなので和訳しました。 なお、日本語として読みやすくするために、言い回しや文のつなぎの変更や省略を行っています。気になる方は原文を確認してください。
原文はこちら
他のドキュメントもそのうち和訳するかもしれません。

django-mptt

djangoモデルに修正された先行順走査(MPTT)を実装し、モデルインスタンスのツリーとして動作するユーティリティです。

プロジェクトホーム: http://github.com/django-mptt/django-mptt/
ドキュメント: http://django-mptt.github.io/django-mptt/
ディスカッショングループ: http://groups.google.com/group/django-mptt-dev

修正された先行順走査(MPTT)とは何か?

MPTTとは、階層的なデータをデータベースに保存する手法です。 検索処理を効率化することを目的としています。

効率化のトレードオフとして、要素の追加や移動がより複雑になっており、ツリー構造を保つには追加の作業が必要です。

MPTTについて、どう動作するか詳細を理解できるいくつかの記事を示しておきます。

SQLの中のツリー (Trees in SQL)

データベースに階層構造のデータを保存する (Storing Hierarchical Data in a Database)

MySQLの中の階層的なデータを管理する(Managing Hierarchical Data in MySQL)

django-mpttとは何か?

django-mpttは、簡単にMPTTをあなたのdjangoモデルで使えるようにする、再利用可能なdjangoアプリケーションです。 データベースのテーブルをツリー構造として管理し、モデルインスタンスのツリーを操作するツールを提供します。

必要要件

Python 2.6以上 (実験的なサポート:python 3.2以上)
Django 1.4.2以上

特徴

  • モデルの簡単な登録 - ツリー構造に必要なフィールドを自動的に追加します。
  • モデルインスタンスを作成・削除したり、親を変更したりすると、自動的にツリー構造を更新します。
  • あなたが選択した単一(もしくは複数)のフィールドで、ツリーの各レベルを自動的に整列させます
  • 登録されたモデルに対して、次のような新しいモデルメソッドを追加します。
    • 木の中の位置を変更
    • 祖先、兄弟、子の検索
    • 子孫の数を数える
    • その他の木に関する操作
  • 登録されたモデルに対して TreeManagerが追加され、以下のメソッドを提供します。
    • 木周辺、もしくは別の木へのノードの移動
    • 木のどこかへノードを挿入
    • 木のMPTTフィールドの再生成(djangoの外側でアップデートを行った際に利用)
  • ツリーモデル用のFormフィールド
  • ツリーモデル用のユーティリティ機能
  • ツリーを描画するためのテンプレートタグとフィルター

2014年10月13日月曜日

PythonのWebフレームワークのトレンド

Webアプリを構築する時、Djangoを利用することが多いのですが、ちょこちょこ面倒だと思うポイントがあったりします。 Django以外のWebフレームワークに乗り換えるのもありかと思って調べてみると、このようなページを見つけました。 なるほど、Django以外にも面白そうなフレームワークが色々あるようです。 しかし、手に入る情報の量やフレームワーク自身の寿命を考えると、各フレームワークの人気も気になります。

そこでGoogle Trendで簡単な検索をかけてみました。

なんと言うか、想像以上にDjangoの圧勝ですね。Flaskは少しずつ伸びていますが、Pyramidは一時盛り上がった後、収束してしまっています。 何か特別な理由が無い限り、とりあえずDjangoを選んでおくのが無難、と言うのが現実なようです。

2014年5月29日木曜日

PyPI XML-RPC解説

PyPIはXML-RPCに対応しており、xmlrpclibを用いることで、簡単にPyPIのデータを利用することができます。PyPIに利用可能なメソッドの説明はあるのですが、具体例は少ないので解説記事を作りました。
Updated: 本編をWebサイトからこちらに持ってきました。
基本的にこのページの和訳です。

list_packages()

PyPIに登録されちいるパッケージ一覧をパッケージ名のlistとして戻します。

>>> import xmlrpclib
>>> client = xmlrpclib.ServerProxy('http://pypi.python.org/pypi')
>>> packageList = client.list_packages()
>>> packageList
['0x10c-asm', '1009558_nester', '131228_pytest_1', '17MonIP', '18-e', '1ee', '1pass', '1to001', '2013007_pyh', '2C.py'... ]

>>> len(packageList)
44313

package_releases(package_name, show_hidden=False)

指定されたパッケージのリリース一覧をlistとして戻します。show_hiddenがFalse(Default)の場合、隠されたリリースは含まれません。

>>> import xmlrpclib
>>> client = xmlrpclib.ServerProxy('http://pypi.python.org/pypi')
>>> client.package_releases('Django')
['1.6.5', '1.6.4', '1.6.3', '1.6.2', '1.6.1', '1.6', '1.5.8', '1.5.7', '1.5.6', '1.5.5', '1.5.4', '1.5.3', '1.5.2', '1.5.1', '1.5', '1.4.13', '1.4.12', '1.4.11', '1.4.10', 
'1.4.9', '1.4.8', '1.4.7', '1.4.6', '1.4.5', '1.4.4', '1.4.3', '1.3.7', '1.3.6', '1.3.5', '1.2.7', '1.1.4', '1.0.4']
なお、show_hiddenを設定する際、「show_hidden=True」と指定するとエラーとなるため、「True」のみを設定してください。
>>> client.package_releases('Django',show_hidden=True)
Traceback (most recent call last):
  File "", line 1, in 
TypeError: __call__() got an unexpected keyword argument 'show_hidden'

>>> client.package_releases('Django',True)
['1.6.5', '1.6.4', '1.6.3', '1.6.2', '1.6.1', '1.6', '1.5.8', '1.5.7', '1.5.6', '1.5.5', '1.5.4', '1.5.3', '1.5.2', '1.5.1', '1.5', '1.4.13', '1.4.12', '1.4.11', '1.4.10', 
'1.4.9', '1.4.8', '1.4.7', '1.4.6', '1.4.5', '1.4.4', '1.4.3', '1.4.2', '1.4.1', '1.4', '1.3.7', '1.3.6', '1.3.5', '1.3.4', '1.3.3', '1.3.2', '1.3.1', '1.3', '1.2.7', 
'1.2.6', '1.2.5', '1.2.4', '1.2.3', '1.2.2', '1.2.1', '1.2', '1.1.4', '1.1.3', '1.1.2', '1.1.1', '1.1', '1.0.4', '1.0.3', '1.0.2', '1.0.1']
また、大文字小文字が区別され、存在しないパッケージ名を指定すると空のlistが戻されます。
>>> client.package_releases('django')
[]

package_roles(package_name)

指定されたパッケージのOwnerとMaintainer一覧をlistとして取得します。

>>> import xmlrpclib
>>> client = xmlrpclib.ServerProxy('http://pypi.python.org/pypi')
>>> client.package_roles('Django')
[['Owner', 'ubernostrum'], ['Owner', 'jacobian'], ['Maintainer', 'apollo13'], ['Maintainer', 'jezdez']]

user_packages(user)

指定されたユーザーのロール、パッケージ名のlistを取得します。 Package rolesがパッケージに対するロール、ユーザー一覧なのに対し、こちらはユーザーに対するロール、パッケージ一覧です。

>>> import xmlrpclib
>>> client = xmlrpclib.ServerProxy('http://pypi.python.org/pypi')
>>> client.user_packages('ubernostrum')
[['Owner', 'webcolors'], ['Owner', 'pownce-api'], ['Owner', 'django-registration'], ['Owner', 'django-profiles'], ['Owner', 'django-flashpolicies'], ['Owner', 'Django'], 
['Owner', 'django-funserver'], ['Owner', 'django-contact-form'], ['Owner', 'mdn-sphinx-theme']]

release_downloads(package_name, version)

指定されたパッケージ、バージョンのダウンロードファイルとダウンロード数のlistを取得します。

>>> import xmlrpclib
>>> client = xmlrpclib.ServerProxy('http://pypi.python.org/pypi')
>>> client.release_downloads('Django','1.6.5')
[['Django-1.6.5.tar.gz', 85849], ['Django-1.6.5-py2.py3-none-any.whl', 63062]]

release_urls(package_name, version)

指定されたパッケージ、バージョンのダウンロード用urlに関する情報を、dictionaryのlist形式で戻します。 dictionaryとして戻されるのは以下の項目です。

  • has_sig
  • upload_time
  • comment_text
  • python_version
  • url
  • md5_digest
  • downloads
  • filename
  • packagetype
  • size
>>> import xmlrpclib
>>> client = xmlrpclib.ServerProxy('http://pypi.python.org/pypi')
>>> client.release_urls('Django','1.6.5')
[{'has_sig': False, 'upload_time': , 'comment_text': '', 'python_version': 'any', 'url': 'https://pypi.python.org/packages/any/D/Django/Django-1.6.5-py2.py3-none-any.whl', 
'md5_digest': '2bcdb4729f9f358b0925b532eef0a8ff', 'downloads': 63062, 'filename': 'Django-1.6.5-py2.py3-none-any.whl', 'packagetype': 'bdist_wheel', 'size': 6683598}, 
{'has_sig': False, 'upload_time': , 'comment_text': '', 'python_version': 'source', 'url': 'https://pypi.python.org/packages/source/D/Django/Django-1.6.5.tar.gz', 
'md5_digest': 'e4c5b2d35ecb3807317713afa70a0c77', 'downloads': 85849, 'filename': 'Django-1.6.5.tar.gz', 'packagetype': 'sdist', 'size': 6633768}]

release_data(package_name, version)

指定されたパッケージ、バージョンに関する基本情報(URLや説明、先月のダウンロード数等)を取得します。

>>> import xmlrpclib
>>> client = xmlrpclib.ServerProxy('http://pypi.python.org/pypi')
>>> client.release_data('Django','1.6.5')
{'maintainer': None, 'docs_url': '', 'requires_python': None,...}

search(spec[, operator])

dictionary形式でSpecに指定された条件で検索を行い、条件にあったパッケージのdictionaryのlistを戻します。

  • 'name': パッケージ名
  • 'version': バージョン
  • 'summary': サマリー
>>> import xmlrpclib
>>> client = xmlrpclib.ServerProxy('http://pypi.python.org/pypi')
>>> client.search({'name':['django']})
[{'_pypi_ordering': 0, 'version': '0.3.0', 'name': 'alpaca-django',...] 

browse(classifiers)

指定された分類(classifier)に含まれるパッケージとバージョンの一覧を戻します。 classifier一覧
https://pypi.python.org/pypi?%3Aaction=list_classifiers

>>> import xmlrpclib
>>> client = xmlrpclib.ServerProxy('http://pypi.python.org/pypi')
>>> client.browse(['Programming Language :: Python'])
[['pythonweb', '0.6.0'], ['mycloud', '0.51'], ['django-ship', '0.2.1']...]

changelog(since, with_ids=False)

sinceで指定されたtimestampから最新までの変更ログのlistを戻します。1つ1つのログは(name, version, timestamp, action)です。with_ids=Trueの場合、最後にserialが追加されます。また、timestampは全てUTCベースで、sinceは整数のみです。 以下は、今から1000秒前を起点として変更ログを戻しています。

>>> import xmlrpclib
>>> import time
>>> client = xmlrpclib.ServerProxy('http://pypi.python.org/pypi')
>>> client.changelog(int(time.time()-1000))
[['arvados-python-client', '0.1.1401370328.82b4650', 1401370508,...] 

changelog_last_serial()

最新のchangelogのserialを戻します。

>>> import xmlrpclib
>>> client = xmlrpclib.ServerProxy('http://pypi.python.org/pypi')
>>> client.changelog_last_serial()
1107680

changelog_since_serial(since_serial)

指定されたserial以降の変更ログのlistを戻します。1つ1つのログは(name, version, timestamp, action, serial)です。

>>> import xmlrpclib
>>> client = xmlrpclib.ServerProxy('http://pypi.python.org/pypi')
>>> client.changelog_since_serial(1107670)
[['infi.recipe.buildout_logging', '0.1.1', 1401370958,...] 

2014年5月5日月曜日

DjangoでFormに初期値を渡す

Web frameworkであるDjangoにおいて、Viewを関数で実装する(classを利用しない)時に、Formに初期値を入れる方法のメモです。

Django 1.5系列で試していますが、それ以降でもおそらく変わらないはず。

方法:
Formを生成する際に初期値をDictionary形式で渡す。
form = forms.ProfileForm({"email":request.user.email,"language":profile.language})

以下、全体像です。
forms.pyでFormのclassを定義し、view.pyで初期値を入れたformを返しています。
#forms.py

from django import forms

from django.conf import settings

class ProfileForm(forms.Form):
    email = forms.EmailField(
        label=_('mail address'),
        required=True,
    )

    language = forms.ChoiceField(
        label=_('language'),
        required=True,
        choices=settings.LANGUAGES,
    )


#views.py

@login_required
def formProfile(request):
    profile = Profile(user=request.user,language=request.LANGUAGE_CODE)
    form = None

    if request.method == 'POST':
        form = forms.ProfileForm(request.POST)
        if form.is_valid():
            profile.user.email=form.email
            profile.user.save()
            profile.language=form.language
            profilef.save()
    else:
        form = forms.ProfileForm({"email":request.user.email,"language":profile.language})

    return render(request, 'profile.html', {
        'form': form,
    })

2014年4月12日土曜日

Django CMS 3.0が正式リリースされたのだけれど

Django CMS 3.0が正式リリースされたのだけれど、なぜかページ編集画面が正常に動作しない。 編集関連の機能が一切効かないし、CMSのナビゲーションバーも反応なし。 環境依存の問題なのだろうか。

2014年4月5日土曜日

django-cms 3.0rc2をUbuntu 12.04にインストール

django-cmsをubuntu 12.04にインストールするメモです。
インストール中に質問される「django CMS version」で「rc」を選択しないとインストールに失敗します。
$sudo apt-get update
$sudo apt-get install python-virtualenv libpq-dev python-dev libjpeg-dev libpng12-dev
$virtualenv pyenv
$source ./pyenv/bin/activate
$pip install djangocms-installer
$mkdir project_name
$cd project_name
$djangocms -p . project_name

Database configuration (in URL format) [default sqlite://locahost/project.db]:[Enter]
django CMS version (choices: 2.4, stable, rc, develop) [default stable]: rc
Django version (choices: 1.4, 1.5, 1.6, stable) [default 1.5]: 1.6
Activate Django I18N / L10N setting (choices: yes, no) [default yes]:[Enter]
Install and configure reversion support (choices: yes, no) [default yes]:[Enter]
Languages to enable. Option can be provided multiple times, or as a comma separated list: en,ja
Optional default time zone [default America/Chicago]: UTC
Activate Django timezone support (choices: yes, no) [default yes]:[Enter]
Activate CMS permission management (choices: yes, no) [default yes]:[Enter]
Use Twitter Bootstrap Theme (choices: yes, no) [default no]:[Enter]
Load a starting page with examples after installation (choices: yes, no) [default no]:[Enter]


参考;
https://www.digitalocean.com/community/articles/how-to-set-up-and-install-django-cms-on-a-debian-7-or-ubuntu-13-vps

https://github.com/divio/django-cms-tutorial/blob/master/Step%201%20-%20Initial%20Setup.md

2014年1月12日日曜日

Pythonで作成したスクリプトをdaemon化する Ubuntu 12.04 - 14.04

Ubuntu 12.04以降(2015年2月時点で14.04までOK)でPythonで作成したスクリプトをdaemon化する場合、upstartに任せてしまうのが便利です。
サンプルはここに置きました。

参考:http://heartbeats.jp/hbblog/2013/02/upstart-daemon.html

なお、upstartを利用しているディストリビューションでも、設定ファイルをインストールするフォルダが異なることがあるようなので、Ubuntu以外を利用している人は設定ファイルの保存先を変更してください。

upstart

Ubuntuではサービスの起動にupstartを利用できるようになっています。
upstartでサービスの起動を行う場合、以下のようなファイルを/etc/init以下に保存し、「/sbin/initctl reload-configuration」で設定を再読み込みします。

#/etc/init/python_daemon_example.conf

description "python daemon example"
author  "ccat"

start on runlevel [2345]
stop on runlevel [016]

chdir /usr/local/python_daemon_example
exec python daemon.py
respawn
cp ./python_daemon_example.conf /etc/init/python_daemon_example.conf
/sbin/initctl reload-configuration

2013年12月16日月曜日

PIL/PillowをUbuntu 12.04 64bitのvirtualenvへインストールする

はじめに

先日、django-wikiを使ってみようと思ったところ、依存関係のあるPIL/Pillowのインストールに少し手間取りました。そこで、PIL/PillowをUbuntu 12.04 64bit版のvirtualenv環境へインストールする方法を記録しておきます。

インストール方法

virtualenv自身のインストールも含め、下記に記載しました。なお、virtualenvで./envディレクトリにpython環境を構築することを前提としています。
sudo apt-get install python-virtualenv #virtualenvパッケージをインストール
virtualenv ./env #python環境を./envに作成
sudo apt-get install libjpeg8 libjpeg8-dev libpng12-0 libpng12-dev python-dev #PIL/Pillowに必要なパッケージをインストール
source ./env/bin/activate # virtualenvの環境を有効化
pip install pillow # or "pip install PIL".  pillowかPILをインストール

以上でインストール終了です。簡単ですね。

なお、libpngやlibjpegの後ろについている数字は比較的よく変更されるため、すぐ上記ではインストールできなくなる可能性があります。libpngやlibjpegのインストールでエラーが発生した場合、"apt-cache search libpng"や "apt-cache search libjpeg"でパッケージの後ろの数字を確認しましょう。