2013年9月2日月曜日

[Django 1.5.1] TypeError: can't compare offset-naive and offset-aware datetimes

TimeZoneは後から真面目に考えようと、とりあえず何も考えずに実装していたら、日付を比較する行でエラーが発生!
class Permit(models.Model):
    ...
    expires = models.DateTimeField()
    created = models.DateTimeField(auto_now_add=True)
    ...
    permit = Permit.object.get(id=xxx)
    if permit.expires < datetime.now():  # ERROR
        ...
naiveとawareの意味が判らないので、まずは、そこから調査。
Pythonの日付処理とTimeZoneによると、TimeZone情報を持ったdatetimeオブジェクトがaware, 持たない場合がnaive。awareとnaiveで比較するとエラーになることが判明。

次にDjangoだが、settings.pyにTimeZoneの有効/無効の設定があり、デフォルトは有効になっている
...
# If you set this to False, Django will not use timezone-aware datetimes.
USE_TZ = True
...

では、実際に、それぞれのFieldにどんな値が設定されているのか確認すると expiresは日本時間がUTCとして、createdは正しい現在時刻がUTCとして設定されていた。
そこで、以下の2ヶ所を修正をして、期待通りの動作になった。

まず、expiresに値を設定するところでTimeZoneを指定するようにした。
class Permit(models.Model):
    ...
    def save(self, *args, **kwargs):
        dt = datetime.strptime(xxx)
        self.expires = dt.replace(tzinfo=pytz.timezone('Asia/Tokyo'))
        ...

次に時間を比較するところで、UTCを使用するように変更
import pytz
...
    permit = Permit.objects.get(id=xxx)
    if permit.expires < datetime.utcnow().replace(tzinfo=pytz.UTC):
        ...

もっと、よく調べたら、Djangoに便利なユーティリティーが存在することが判明
from django.utils import timezone
...
    permit = Permit.objects.get(id=xxx)
    if permit.expires < timezone.now():
        ...

2013年8月21日水曜日

[jQuery] 無限スクロールを試す

JSONレスポンスが使える無限スクロールPluginを探していたら、 14 Best Free jQuery Infinite Scrolling Plugins Examples and Tutorialsというサイトが見つかった。 ここで紹介されているPluginの中で、 Autobrowse jQuery を試して見たところ、期待どおりJSONレスポンスを使った無限スクロールが実現。

2013年8月11日日曜日

[Django 1.5.1] CreateViewのtemplate_name_suffix

ファイルアップロード時の動作を改善しようとdjango-jquery-file-uploadのコードを読んでいたところ、なぜか、使用するテンプレートの指定がどこにも書かれていない。 調べたところ、CreateViewはテンプレートの指定がない場合、モデル名に'_form'を付けたファイルを使うようになっている。
class CreateView(SingleObjectTemplateResponseMixin, BaseCreateView):
    """
    View for creating a new object instance,
    with a response rendered by template.
    """
    template_name_suffix = '_form'
...
例えば下記のような場合、picture_form.htmlを探します。
class PictureCreateView(CreateView):
    model = Picture

2013年7月28日日曜日

MySQLを使用するDjangoプロジェクトのセットアップ

Djangoプロジェクトmysiteを作成。

$ django-admin.py startproject mysite

このプロジェクト用のデータベースmysite_dbと、
このデータベースにアクセスするユーザmysite_userを作成。
$ mysql -u root -p
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
...
mysql> create database mysite_db;
Query OK, 1 row affected (0.00 sec)

mysql> grant CREATE,ALTER,DROP,INDEX,SELECT,UPDATE,INSERT,DELETE
    -> on mysite_db.*
    -> to mysite_user@localhost
    -> identified by 'mysite_pass';
Query OK, 0 rows affected (0.03 sec)

mysql> quit
Bye
$

プロジェクトの設定ファイルsettings.pyに作成したデータベースの情報を書き込む。
$ cd mysite/
$ vi mysite/settings.py
...
ENGINE = django.db.backends.mysql
NAME   = mysite_db
USER   = mysite_user
PASWORD= mysite_pass
...
$

syncdbコマンドでデータベースを初期化。
$ ./manage.py syncdb
Creating tables ...
Creating table auth_permission
Creating table auth_group_permissions
Creating table auth_group
Creating table auth_user_groups
Creating table auth_user_user_permissions
Creating table auth_user
Creating table django_content_type
Creating table django_session
Creating table django_site

You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no): yes
Username (leave blank to use 'xxx'): root
Email address: hoge@example.com
Password:
Password (again):
Superuser created successfully.
Installing custom SQL ...
Installing indexes ...
Installed 0 object(s) from 0 fixture(s)
$

初期化されたデータベースの中身を確認。
$ mysql -u root -p
Enter password:
...

mysql> use mysite_db;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+----------------------------+
| Tables_in_mysite_db        |
+----------------------------+
| auth_group                 |
| auth_group_permissions     |
| auth_permission            |
| auth_user                  |
| auth_user_groups           |
| auth_user_user_permissions |
| django_content_type        |
| django_session             |
| django_site                |
+----------------------------+
9 rows in set (0.00 sec)

mysql> desc auth_user;
+--------------+--------------+------+-----+---------+----------------+
| Field        | Type         | Null | Key | Default | Extra          |
+--------------+--------------+------+-----+---------+----------------+
| id           | int(11)      | NO   | PRI | NULL    | auto_increment |
| password     | varchar(128) | NO   |     | NULL    |                |
| last_login   | datetime     | NO   |     | NULL    |                |
| is_superuser | tinyint(1)   | NO   |     | NULL    |                |
| username     | varchar(30)  | NO   | UNI | NULL    |                |
| first_name   | varchar(30)  | NO   |     | NULL    |                |
| last_name    | varchar(30)  | NO   |     | NULL    |                |
| email        | varchar(75)  | NO   |     | NULL    |                |
| is_staff     | tinyint(1)   | NO   |     | NULL    |                |
| is_active    | tinyint(1)   | NO   |     | NULL    |                |
| date_joined  | datetime     | NO   |     | NULL    |                |
+--------------+--------------+------+-----+---------+----------------+
11 rows in set (0.00 sec)

mysql> select username,email from auth_user;
+----------+------------------+
| username | email            |
+----------+------------------+
| root     | hoge@example.com |
+----------+------------------+
1 row in set (0.00 sec)

mysql>

Ubuntu 12.04 LTSにDjango 1.5.1をインストール

How to install Django を参考にインストール

ApacheとMySQLの組み合わせで動かしたいので、下記ソフトをインストール。
  • Apache 2.4.4
  • mod_wsgi 3.4
  • MySQL 5.6.12
  • MySQL-python 1.2.3

次に、Djangoのインストールだが、すごく簡単。
Installing an official release manually の手順通り。
$ wget -O Django-1.5.1.tar.gz https://www.djangoproject.com/download/1.5.1/tarball/
$ tar xvzf Django-1.5.1.tar.gz
$ cd Django-1.5.1
$ sudo python setup.py install

正しくインストールできたかどうかは、下記のコマンドで確認。
$ python -c "import django; print(django.get_version())"
1.5.1
$

2013年7月24日水曜日

Ubuntu 12.04 LTSにMySQL for Python 1.2.3をインストール

PythonからMySQLにアクセスするためのライブラリ。

任意のフォルダにソースコードをダウンロードして展開。
$ wget http://sourceforge.net/projects/mysql-python/files/mysql-python/1.2.3/MySQL-python-1.2.3.tar.gz
$ tar xvzf MySQL-python-1.2.3.tar.gz
$ cd MySQL-python-1.2.3

ビルドする前に、READMEを参考に、
必要なライブラリのインストールと、site.cfgの編集を行う。
$ sudo apt-get install python-setuptools
$
$ # mysql_configのパスを
$ # mysql_config
$ vi site.cfg
...
# The path to mysql_config.
# Only use this if mysql_config is not on your PATH, or you have some weird
# setup that requires it.
mysql_config = /opt/mysql/server-5.6/bin/mysql_config
...
$

準備ができたらビルドしてインストール。
$ python setup.py build
$ sudo python setup.py install
...
Installed /usr/local/lib/python2.7/dist-packages/MySQL_python-1.2.3-py2.7-linux-x86_64.egg
Processing dependencies for MySQL-python==1.2.3
Finished processing dependencies for MySQL-python==1.2.3
$

最後に、ここ を参考にコードを書いて、MySQLにアクセスできることを確認。
$ mysql -u root -p
Enter password:
...
mysql> create database test_python;
Query OK, 1 row affected (0.00 sec)

mysql> use test_python;
Database changed
mysql> create table test(
    -> id int,
    -> value text,
    -> primary key(id));
Query OK, 0 rows affected (0.04 sec)

mysql> insert into test values(1, "sample text");
Query OK, 1 row affected (0.01 sec)

mysql>

>>> import MySQLdb
>>> conn = MySQLdb.connect(db="test_python", host="127.0.0.1", port=3306, user="root", passwd="xxx")
>>> cur = conn.cursor()
>>> cur.execute("insert into test values (2, 'python text')")
1L
>>> cur.execute("select * from test")
2L
>>> rows = cur.fetchall()
>>> for row in rows:
        print row


(1L, 'sample text')
(2L, 'python text')
>>> cur.close()
>>> conn.commit()
>>> conn.close()
>>>

Ubuntu 12.04 LTSにMySQL 5.6をインストール

まず始めに、/etc/mysqlフォルダの有無を確認。
Ubuntu 12.04 LTSをインストールすると、自動的にmysql-clientとmysql-commonが組み込まれるようなので、これらをuninstallするか、とりあえず、フォルダ名を変更する。このフォルダがあると、参考にしたサイトの手順どおりにインストールできない。

確認が終わったら、 ここから 任意のフォルダにDebian用バイナリー配布パッケージをダウンロード。

次に、 2.5.2. Installing MySQL on Linux Using Debian Packages を参考に、MySQLとlibaioライブラリをインストール。
$ sudo dpkg -i mysql-5.6.12-debian6.0-x86_64.deb
$ sudo apt-get install libaio1

続いて、 2.2. Installing MySQL on Unix/Linux Using Generic Binaries2.10.1. Unix Postinstallation Procedures を参考に、MySQLデータを初期化し、システムテーブルを作成。
$ sudo groupadd mysql
$ sudo useradd -r -g mysql mysql
$ cd /opt/mysql
$ sudo chown -R mysql .
$ sudo chgrp -R mysql .
$ cd server-5.6
$ sudo install -o mysql -g mysql -d /var/opt/mysql
$ sudo scripts/mysql_install_db --user=mysql --datadir=/var/opt/mysql

/opt/mysql/server-5.6に作成された設定ファイルmy.cnfを/etcに移動して、コメントになっているbasedir、datadirを書き換える。
$ sudo mv my.cnf /etc
$ sudo vi /etc/my.cnf
...
basedir = /opt/mysql/server-5.6
datadir = /var/opt/mysql
...
$

これで、起動に必要な準備は完了。
早速、起動してみる。
$ sudo bin/mysqld_safe --user=mysql &

アクセスできるか確認するためにバージョンを表示。
正しく表示されたら起動OKなので、rootのパスワードを設定。
$ bin/mysqladmin version
...
Server version  5.6.12
Protocol version 10
Connection  Localhost via UNIX socket
UNIX socket  /tmp/mysql.sock
Uptime:   1 min 55 sec
...
$ bin/mysqladmin -u root password 'xxx'  # xxxがパスワード

続いて、パスワードが正しく設定できたか確認。
$ bin/mysql -u root -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
...
mysql> quit
Bye

ブートの設定をするために、一旦、shutdown。
$ bin/mysqladmin -u root -p shutdown

ブート時に自動的に起動するように設定。
$ sudo cp support-files/mysql.server /etc/init.d/mysql
$ sudo update-rc.d mysql defaults
 Adding system startup for /etc/init.d/mysql ...
   /etc/rc0.d/K20mysql -> ../init.d/mysql
   /etc/rc1.d/K20mysql -> ../init.d/mysql
   /etc/rc6.d/K20mysql -> ../init.d/mysql
   /etc/rc2.d/S20mysql -> ../init.d/mysql
   /etc/rc3.d/S20mysql -> ../init.d/mysql
   /etc/rc4.d/S20mysql -> ../init.d/mysql
   /etc/rc5.d/S20mysql -> ../init.d/mysql
$

最後に、コマンドとライブラリのパスを追加して作業終了。
PATH=/opt/mysql/server-5.6/bin:${PATH}

$ sudo vi /etc/ld.so.conf.d/mysql.conf  # 新規作成
/opt/mysql/server-5.6/lib
$ sudo ldconfig