データベース初期化
startproject:
(v17)hdknr@wzy:~/ve/v17/src/sample$ mkdir -p web; django-admin.py startproject app web ;cd web
migrate & createsuperuser :
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, contenttypes, auth, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying sessions.0001_initial... OK
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ python manage.py createsuperuser
Username (leave blank to use 'hdknr'): admin
Email address: admin@admin.admin
Password:
Password (again):
Superuser created successfully.
syncdbも一応使える:
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ rm db.sqlite3 ; python manage.py syncdb
Operations to perform:
Apply all migrations: admin, contenttypes, auth, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying sessions.0001_initial... OK
You have installed Django's auth system, and don't have any superusers defined.
Would you like to create one now? (yes/no): yes
Username (leave blank to use 'hdknr'): admin
Email address: admin@admin.admin
Password:
Password (again):
Superuser created successfully.
モデルマイグレーション
makemigrations:
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ python manage.py makemigrations
No changes detected
アプリ追加
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ python manage.py startapp todos
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ tree todos/
todos/
├── __init__.py
├── admin.py
├── migrations
│ └── __init__.py
├── models.py
├── tests.py
└── views.py
1 directory, 6 files
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ echo "INSTALLED_APPS += ('todos',)" >> app/settings.py
モデルクラス無いので追加:
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ python manage.py makemigrations
No changes detected
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ cat todos/models.py
from django.db import models
from django.contrib.auth.models import User
class Todo(models.Model):
user = models.ForeignKey(User)
title = models.CharField(max_length=50)
description = models.TextField()
due_at = models.DateTimeField(
null=True, blank=True, default=None)
created_at = models.DateTimeField(auto_now_add=True, )
updated_at = models.DateTimeField(auto_now=True, )
makemigrations:
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ python manage.py makemigrations todos
Migrations for 'todos':
0001_initial.py:
- Create model Todo
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ tree todos/
todos/
├── __init__.py
├── __init__.pyc
├── admin.py
├── admin.pyc
├── migrations
│ ├── 0001_initial.py
│ ├── __init__.py
│ └── __init__.pyc
├── models.py
├── models.pyc
├── tests.py
└── views.py
1 directory, 11 files
migrate:
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ python manage.py migrate todos
Operations to perform:
Apply all migrations: todos
Running migrations:
Applying todos.0001_initial... OK
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ echo ".schema todos_todo" | python manage.py dbshell
CREATE TABLE "todos_todo" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"title" varchar(50) NOT NULL,
"description" text NOT NULL,
"due_at" datetime NULL,
"created_at" datetime NOT NULL,
"updated_at" datetime NOT NULL,
"user_id" integer NOT NULL REFERENCES "auth_user" ("id"));
CREATE INDEX todos_todo_e8701ad4 ON "todos_todo" ("user_id");
django_migrations
マイグレーションテーブル:
$ echo ".schema django_migrations" | python manage.py dbshell
CREATE TABLE "django_migrations" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"app" varchar(255) NOT NULL,
"name" varchar(255) NOT NULL,
"applied" datetime NOT NULL);
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ echo "select * from django_migrations;" | python manage.py dbshell
1|contenttypes|0001_initial|2014-09-03 02:10:35.695671
2|auth|0001_initial|2014-09-03 02:10:35.744375
3|admin|0001_initial|2014-09-03 02:10:35.788881
4|sessions|0001_initial|2014-09-03 02:10:35.808466
5|todos|0001_initial|2014-09-03 02:10:35.855269
6|todos|0002_todo_closed|2014-09-03 02:11:12.687727
モジュール:
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ tree $VIRTUAL_ENV/lib/python2.7/site-packages/django/db/migrations/ -I "*.pyc"
/home/hdknr/ve/v17/lib/python2.7/site-packages/django/db/migrations/
├── __init__.py
├── autodetector.py
├── executor.py
├── graph.py
├── loader.py
├── migration.py
├── operations
│ ├── __init__.py
│ ├── base.py
│ ├── fields.py
│ ├── models.py
│ └── special.py
├── optimizer.py
├── questioner.py
├── recorder.py
├── state.py
└── writer.py
1 directory, 16 files
shellから:
>>> from django.db.migrations.recorder import *
>>> for i in MigrationRecorder.Migration.objects.values():
... print i
...
{'applied': datetime.datetime(2014, 9, 3, 2, 10, 35, 695671, tzinfo=<UTC>), 'app': u'contenttypes', u'id': 1, 'name': u'0001_initial'}
{'applied': datetime.datetime(2014, 9, 3, 2, 10, 35, 744375, tzinfo=<UTC>), 'app': u'auth', u'id': 2, 'name': u'0001_initial'}
{'applied': datetime.datetime(2014, 9, 3, 2, 10, 35, 788881, tzinfo=<UTC>), 'app': u'admin', u'id': 3, 'name': u'0001_initial'}
{'applied': datetime.datetime(2014, 9, 3, 2, 10, 35, 808466, tzinfo=<UTC>), 'app': u'sessions', u'id': 4, 'name': u'0001_initial'}
{'applied': datetime.datetime(2014, 9, 3, 2, 10, 35, 855269, tzinfo=<UTC>), 'app': u'todos', u'id': 5, 'name': u'0001_initial'}
{'applied': datetime.datetime(2014, 9, 3, 2, 11, 12, 687727, tzinfo=<UTC>), 'app': u'todos', u'id': 6, 'name': u'0002_todo_closed'}
pre_migrate/ post_migrate
todos/models.py 修正:
from django.dispatch import receiver
from django.db.models.signals import (
pre_migrate, post_migrate,
)
def trace(msg, sender, app_config, *args, **kwargs):
assert sender == app_config
assert args == ()
if app_config and app_config.name == "todos":
print msg
for k, v in kwargs.items():
print "***", k, type(v), v
@receiver(pre_migrate)
def pre_migrate_handler(
sender=None, app_config=None, *args, **kwargs
):
trace(
">>> pre_migrate signal",
sender, app_config, *args, **kwargs)
@receiver(post_migrate)
def post_migrate_handler(
sender=None, app_config=None, *args, **kwargs
):
trace(
">>> post_migrate signal",
sender, app_config, *args, **kwargs)
class Todo(models.Model):
closed = models.BooleanField(default=False)
....
makemigrations & migrate:
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ python manage.py makemigrations todos
Migrations for 'todos':
0002_todo_closed.py:
- Add field closed to todo
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ python manage.py migrate todos
Operations to perform:
Apply all migrations: todos
Running migrations:
Applying todos.0002_todo_closed... OK
>>> post_migrate signal
*** using <type 'str'> default
*** verbosity <type 'int'> 1
*** signal <class 'django.dispatch.dispatcher.Signal'> <django.dispatch.dispatcher.Signal object at 0x25ba110>
*** interactive <type 'bool'> True
Database router の allow_migrateメソッド
allow_syncdb -> allow_migrateの変更。
既存のXOOPSのデータベースを読むようにrouteを設定:
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ more app/database.py
_EXCLUSIVE = lambda obj: \
{"xoops": "xoops"}.get(obj._meta.app_label, 'default')
class SitesRouter(object):
def db_for_read(self, model, **hints):
return _EXCLUSIVE(model)
def db_for_write(self, model, **hints):
return _EXCLUSIVE(model)
def allow_relation(self, obj1, obj2, **hints):
d1, d2 = _EXCLUSIVE(obj1), _EXCLUSIVE(obj2)
return None if d1 != d2 else d1
def allow_migrate(self, db, model):
''' allow_syncdb -> allow_migrate '''
return _EXCLUSIVE(model)
app/settings.py:
INSTALLED_APPS += ('todos', 'xoops',)
#
DATABASES['xoops'] = {
'ENGINE': 'django.db.backends.mysql',
'NAME': os.environ['XOOPS_DBNAME'],
'USER': os.environ['XOOPS_DBUSER'],
'PASSWORD': os.environ['XOOPS_DBPASSWORD'],
'TEST_CHARSET': 'utf8',
'TEST_DATABASE_COLLATION': 'utf8_general_ci',
}
DATABASE_ROUTERS = ['app.database.SitesRouter']
migrateしてみる:
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ python manage.py migrate
Operations to perform:
Synchronize unmigrated apps: xoops
Apply all migrations: admin, contenttypes, todos, auth, sessions
Synchronizing apps without migrations:
>>> pre_migrate signal
*** using <type 'str'> default
*** verbosity <type 'int'> 1
*** signal <class 'django.dispatch.dispatcher.Signal'> <django.dispatch.dispatcher.Signal object at 0x27a4190>
*** interactive <type 'bool'> True
Creating tables...
Installing custom SQL...
Installing indexes...
Running migrations:
No migrations to apply.
>>> post_migrate signal
*** using <type 'str'> default
*** verbosity <type 'int'> 1
*** signal <class 'django.dispatch.dispatcher.Signal'> <django.dispatch.dispatcher.Signal object at 0x27a41d0>
*** interactive <type 'bool'> True
initial_data.json
リリースノート:
initial_data fixtures are no longer loaded for apps with migrations;
if you want to load initial data for an app, we suggest you do it in a migration.
initial_data.json:
[{"fields": {"description": "task1", "title": "task1",
"due_at": null, "created_at": "2014-09-03T03:10:37.059Z",
"updated_at": "2014-09-03T03:10:37.059Z", "user": 1, "closed": false},
"model": "todos.todo", "pk": 1}]
削除:
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ echo "delete from todos_todo;" | python manage.py dbshell
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ echo "select count(*) from todos_todo;" | python manage.py dbshell
0
todosをmigrate:
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ python manage.py migrate todos
Operations to perform:
Apply all migrations: todos
Running migrations:
No migrations to apply.
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ echo "select count(*) from todos_todo;" | python manage.py dbshell
0
全体:
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ python manage.py migrate
Operations to perform:
Synchronize unmigrated apps: xoops
Apply all migrations: admin, contenttypes, todos, auth, sessions
Synchronizing apps without migrations:
Creating tables...
Installing custom SQL...
Installing indexes...
Installed 1 object(s) from 1 fixture(s)
Running migrations:
No migrations to apply.
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ echo "select count(*) from todos_todo;" | python manage.py dbshell
1
todos/fixtures/initial_data.json:
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ python manage.py dumpdata > todos/fixtures/initial_data.json
migrateで読まない:
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, contenttypes, todos, auth, sessions
Running migrations:
No migrations to apply.
testsだと読まれるが、テーブルが無いのでエラー:
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ python manage.py test todos
....
django.db.utils.OperationalError:
Problem installing fixture '/home/hdknr/ve/v17/src/sample/web/todos/fixtures/initial_data.json':
Could not load contenttypes.ContentType(pk=1): no such table: django_content_type
プロジェクトルートにあってもエラー:
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ mv todos/fixtures/initial_data.json .
(v17)hdknr@wzy:~/ve/v17/src/sample/web$ python manage.py test todos
....
django.db.utils.OperationalError:
Problem installing fixture '/home/hdknr/ve/v17/src/sample/web/initial_data.json':
Could not load contenttypes.ContentType(pk=1): no such table: django_content_type
よって, initial_data.json は使わないでloadata で明示的にロードしたほうがいいのだろうか。
TransactionTestCase
TODO:
TransactionTestCaseをつかったことないのないのであとで調べる
リリースノート:
Test rollback behavior is different for apps with migrations; in particular,
Django will no longer emulate rollbacks on non-transactional databases
or inside TransactionTestCase unless specifically requested.
Any initial data loaded in migrations will only be available in TestCase tests
and not in TransactionTestCase tests,
and additionally only on backends
where transactions are supported (the most important exception being MyISAM).
Django can reload that data for you on a per-testcase basis
by setting the serialized_rollback option to True
in the body of the TestCase or TransactionTestCase,
but note that this will slow down that test suite by approximately 3x.
Third-party apps or those developing against MyISAM will need to set this;
in general, however,
you should be developing your own projects against a transactional database
and be using TestCase for most tests, and thus not need this setting.
The initial serialization is usually very quick,
but if you wish to exclude some apps from this process
(and speed up test runs slightly),
you may add those apps to TEST_NON_SERIALIZED_APPS.
Apps without migrations are not affected;
initial_data fixtures are reloaded as usual.
依存
アプリがマイグレーションがあるアプリケーション依存するならば、
かならずマイグレーションを持つようにする事。
It is not advised to have apps
without migrations depend on
(have a ForeignKey or ManyToManyField to) apps with migrations.
Read the dependencies documentation for more.
South
southからのマイグレーション。