revision-up-to: | 17812 (1.4) |
---|
Django のシリアライズフレームワークを使うと、 Django オブジェクトを他の形式 に「翻訳」できます。通常、こうした形式はテキストベースで、 Django オブジェ クトをネットワーク越しに伝送するために使われますが、 Django のシリアライザ は任意の形式 (テキストベースもそうでないものも) 扱えます。
See also
もしテーブルに入っているデータをシリアライズされた形式で取り出したいだ
けなら、 dumpdata
コマンドを使うことができます。
高水準では、データのシリアライズは極めて簡単な操作です:
from django.core import serializers
data = serializers.serialize("xml", SomeModel.objects.all())
serialize
関数の引数には、データのシリアライズに使うフォーマット
(シリアライズの形式 参照) と、シリアライズ対象の
QuerySet
(実際には、第二引数は Django オブジェク
トを返す任意のイテレータにできますが、大抵の場合は QuerySet を使うことにな
るでしょう)。
シリアライザオブジェクトを直接使ってもかまいません:
XMLSerializer = serializers.get_serializer("xml")
xml_serializer = XMLSerializer()
xml_serializer.serialize(queryset)
data = xml_serializer.getvalue()
シリアライザオブジェクトを直接使うと、以下のようにファイルライクオブジェク
ト (もちろん HttpResponse
も使えます) に対して直接シ
リアライズできるので便利です:
out = open("file.xml", "w")
xml_serializer.serialize(SomeModel.objects.all(), stream=out)
Note
get_serializer()
に不明な
format を渡すと、
SerializerDoesNotExist
が送出されます。
一部のフィールドだけをシリアライズしたい場合には、シリアライザに fields
引数を指定します:
from django.core import serializers
data = serializers.serialize('xml', SomeModel.objects.all(), fields=('name','size'))
上の例では、 name
と size
だけがシリアライズされます。
Note
モデルによっては、フィールドの一部だけをシリアライズすると、そこからデ シリアライズできない場合があります。シリアライズ後のオブジェクトに、モ デル上で必須のフィールドがひとつでも抜け落ちていると、デシリアライザは デシリアライズ後のインスタンスを保存できないでしょう。
抽象ベースクラス を使って定義したモデルを扱っ ていても、モデルのシリアライズのために特に行うことはありません。単にシリア ライズしたオブジェクトに対してシリアライザを呼び出せば、完全なシリアライズ 済みオブジェクトが出力されます。
ただし、 マルチテーブル継承 を使って定義さ れているモデルを扱う場合、モデルの全てのベースクラスをシリアライズする必要 があります。これは、モデルごとに固有に定義されたフィールドがシリアライズさ れるためです。例えば、以下のようなモデルを考えましょう:
class Place(models.Model):
name = models.CharField(max_length=50)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField()
ここで、 Restaurant
モデルだけをシリアライズしたとします:
data = serializers.serialize('xml', Restaurant.objects.all())
シリアライズ後の出力には、 serves_hot_dogs
という属性しか入りません。
ベースクラスの name
属性は無視されるのです。
上の Restaurant
インスタンスを完全にシリアライズするには、以下のように
して Place
モデルもシリアライズする必要があります:
all_objects = list(Restaurant.objects.all()) + list(Place.objects.all())
data = serializers.serialize('xml', all_objects)
データのデシリアライズもまた、かなり単純な操作です:
for obj in serializers.deserialize("xml", data):
do_something_with(obj)
見ての通り、 deserialize
関数は serialize
関数と同様、文字列または
データストリームを引数にとり、イテレータを返します。
しかしながら、少しだけややこしい部分もあります。 deserialize
イテレータ
の返すオブジェクトは単純な Django オブジェクト ではなく 、
DeserializedObject
という特殊なインスタンスです。このインスタンスは
作成されただけでまだ保存されていないデータであり、リレーションも張られてい
ません。
DeserializedObject.save()
を呼び出すと、データベースにオブジェクトを保
存します。
上のような仕様から、デシリアライズは、たとえシリアライズされていたデータの
表現形式が現在のデータベースの構成と一致していなかったとしても非破壊的な操
作になるよう保証されています。通常、 DeserializedObject
インスタンスの
操作は以下のように行います:
for deserialized_object in serializers.deserialize("xml", data):
if object_should_be_saved(deserialized_object):
deserialized_object.save()
すなわち、デシリアライズしたオブジェクトを保存する場合、前もって保存に適し ているかどうかを調べるのが普通のやり方なのです。もちろん、データソースを信 頼できるのなら、単にデータを保存してもかまいません。
Django オブジェクト自体に対するインスペクションは、
deserialized_object.object
で行えます。
Django は複数のシリアライズ形式に対応しています。そのうちいくつかではサード パーティモジュールのインストールが必要です。
名前 | 情報 |
---|---|
xml |
単純な XML シリアライザです。 |
json |
JSON シリアライザ (Django に付属の simplejson を使ったも の) です。 |
python |
「単純な」Python オブジェクト (リスト、辞書、文字列など) の シリアライザです。単体では取り立てて便利ではありませんが、 他のシリアライザのベースになっています。 |
yaml |
YAML (YAML Ain’t a Markup Language) へのシリアライザです。 このシリアライザは PyYAML がインストールされている場合のみ 利用できます。 |
UTF-8 (や、非 ASCII エンコーディング) でエンコードされたデータを JSON シリ
アライザで扱うには、 serialize()
のパラメタに ensure_ascii=False
を
指定してください。さもないと、出力のエンコードがおかしくなってしまいます。
例:
json_serializer = serializers.get_serializer("json")()
json_serializer.serialize(queryset, ensure_ascii=False, stream=response)
Django のソースコードには simplejson モジュールが付属しています。
ですが、もし (組み込みバージョンのモジュールが付属する) Python 2.6 かそれ以
降のバージョンを使っているなら、 Django は自動的に組み込みの json
モジュ
ールを使います。Cベースの高速化拡張を含むシステムインストール版や、もっと最
近のバージョンを使っているなら Django に付属しているバージョンの代わりにシス
テムバージョンが使われます。
注意してほしいのは、このモジュールを直接使ってシリアライズを実行すると、一 部の Django オブジェクトは何らかの変更が加えられた上で simplejson に渡されて しまうということです。特に、 遅延翻訳オブジェクト をシリアライズする場合は、 特殊なエンコーダ が必要 です。以下のように書くと、うまくいくでしょう:
from django.utils.functional import Promise
from django.utils.encoding import force_unicode
class LazyEncoder(simplejson.JSONEncoder):
def default(self, obj):
if isinstance(obj, Promise):
return force_unicode(obj)
return obj
リリースノートを参照してください
外部キーや多対多のリレーションをシリアライズする時の基本方針は、リレーションオ ブジェクトの主キー値をシリアライズすることです。 この方針はたいていのオブジェクトに対してうまくいきますが、特定の状況では難しい 問題を引き起こします。
ContentType
を参照する外部キーを
持つオブジェクトのリストを考えてください。オブジェクトをシリアライズしようとす
ると、 ContentType を参照する方法がなければいけません。
ContentType
オブジェクトはデータベースへの同期処理で Django が自動的に生成
するので、 ContentType の主キーを予測するのは困難です。それはいつ
syncdb
が実行されたかによって決まるでしょう。このことはオブジェクト
を生成する全てのモデル、特に Permission
、
Group
、
User
において成り立ちます。
Warning
自動生成されたオブジェクトを、フィクスチャや他のシリアライズされたデータに
含めるべきではありません。フィクスチャの中の主キーが偶然データベースの中の
主キーに一致すると、フィクスチャのロードが効果を持たなくなってしまいます。
もっとありそうなのは、主キーが一致せずにロードが
IntegrityError
を出して失敗するということです。
これは利便性の問題とも言えます。整数のIDはオブジェクトを参照するのにいつも一番 良い方法というわけではありません。時にはより分かりやすい自然な参照があるかもし れません。
これらの理由から Django は 自然キー を提供します。自然キーは値の タプルで、主キーを使わずにオブジェクトインスタンスを特定するために使うことがで きます。
次のような2つのモデルを考えてください:
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
birthdate = models.DateField()
class Meta:
unique_together = (('first_name', 'last_name'),)
class Book(models.Model):
name = models.CharField(max_length=100)
author = models.ForeignKey(Person)
通常、 Book
のシリアライズ済みデータは著者を参照するのに整数を使います。
例えば JSON では、 Book はこのようにシリアライズされるかもしれません:
...
{
"pk": 1,
"model": "store.book",
"fields": {
"name": "Mostly Harmless",
"author": 42
}
}
...
これは著者を参照するための特に自然な方法とは言えません。著者を参照する主キーの値 を知っていなければなりませんし、主キーの値は変更がなく、予測可能なものでなければ なりません。
しかし、 Person を扱う自然キーを追加すれば、このフィクスチャはより理解しや
すいものになります。自然キーの扱いを追加するには、 Person のデフォルトマネ
ジャに get_by_natural_key()
メソッドを定義してください。 Person の場合、
ファーストネームとラストネームが良い自然キーかもしれません:
from django.db import models
class PersonManager(models.Manager):
def get_by_natural_key(self, first_name, last_name):
return self.get(first_name=first_name, last_name=last_name)
class Person(models.Model):
objects = PersonManager()
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
birthdate = models.DateField()
class Meta:
unique_together = (('first_name', 'last_name'),)
これで Book は Person
オブジェクトを参照する自然キーを使えます:
...
{
"pk": 1,
"model": "store.book",
"fields": {
"name": "Mostly Harmless",
"author": ["Douglas", "Adams"]
}
}
...
シリアライズ済みデータをロードしようとすると、 ["Douglas", "Adams"]
を実際
の Person
オブジェクトの主キーへと解決するために、 Django は
get_by_natural_key()
を使います。
Note
自然キーに使うフィールドは、オブジェクトを一意に特定することができなけ
ればなりません。これは普通、そのモデルがユニーク句を持っていることを意味しま
す。(単独のフィールドで unique=True であるか、複数のフィールドに対して
unique_together
であるか)しかし、データベースレベルでユニークであること
は強制されません。実際上ユニークだという確証があれば、それらのフィールドをナ
チュラルキーとして使うことができます。
さて、オブジェクトをシリアライズする時に Django に自然キーを発行させるにはど うすれば良いでしょうか。まず、もう一つのメソッドを、今度はモデル自身に追加する必 要があります:
class Person(models.Model):
objects = PersonManager()
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
birthdate = models.DateField()
def natural_key(self):
return (self.first_name, self.last_name)
class Meta:
unique_together = (('first_name', 'last_name'),)
このメソッドは常に自然キーのタプルを返すべきです。この例では
(first name, last name)
です。それから、 serializers.serialize()
を呼ぶ時
に use_natural_keys=True
引数を渡します:
>>> serializers.serialize('json', [book1, book2], indent=2, use_natural_keys=True)
use_natural_keys=True
が指定されると、 Django はこのメソッドを定義している型の
オブジェクト参照をシリアライズするために、 natural_key()
メソッドを使います。
シリアライズデータを生成するために dumpdata
を使う時は、 –natural
コマンドラインフラグを使います。
Note
natural_key()
と get_by_natural_key()
の両方を定義する必要はありませ
ん。シリアライズの時に自然キーを出力させたくなく、しかも自然キー
をロードすることもできるようにするには、 natural_key()
メソッドを実装しな
いということも可能です。
逆に言えば、(何かの変わった理由で)シリアライズ時に自然キーを出力させ
たく、かつこれらのキーを出力できないようにしたければ、
get_by_natural_key()
メソッドを定義するだけにすれば良いのです。
自然キーは参照を解決するためにデータベースのルックアップに依存するので、参 照される前にデータが存在することが大切です。自然キーで forward refernce を作ることはできません。自然キーをデータに含める前に、参照したいデータが存 在していなければならないのです。
この制限に適応させるため、 dumpdata
で --natural
オプシ
ョンを使うと、どんなモデルでも標準的な主キーオブジェクトでのシリアライズより前に
natural_key()
メソッドでシリアライズされます。
しかし、いつもこれで充分とは限りません。外部キーか自然キーによってナチュラ ルキーの一部である別のオブジェクトを参照している時には、シリアライズされたデータ のなかでは依存される側のオブジェクトが先に出現することを保証できなければなりませ ん。
この順序を保証するため、 natural_key()
メソッドに依存関係を定義できます。
natural_key()
メソッド自体に dependencies
属性を設定するのです。
例えば、 上の例で見た Book
モデルに自然キーを与えましょう:
class Book(models.Model):
name = models.CharField(max_length=100)
author = models.ForeignKey(Person)
def natural_key(self):
return (self.name,) + self.author.natural_key()
Book
の自然キーは名前と著者の組み合わせです。つまり Person
は
Book
より先にシリアライズされる必要があります。この依存関係を定義するため、
もう一行加えます:
def natural_key(self):
return (self.name,) + self.author.natural_key()
natural_key.dependencies = ['example_app.person']
この定義により、どんな Person
オブジェクトも Book
オブジェクトより先に
シリアライズされることが保証されます。逆に、 Book
を参照するどんなオブジェ
クトも、 Person
と Book
の両方がシリアライズされた後にシリアライズされ
るでしょう。
Oct 26, 2017