web-dev-qa-db-ja.com

Djangoでの外部キーオブジェクトのシリアル化

私はDjangoをFlashとAndroidアプリの両方で使用するために)でいくつかのRESTfulサービスの開発に取り組んできました。

サービスインターフェイスの開発は非常に単純ですが、外部キーと多対多の関係を持つオブジェクトのシリアル化で問題が発生しています。

私はこのようなモデルを持っています:

_class Artifact( models.Model ):
    name                = models.CharField( max_length = 255 )
    year_of_Origin      = models.IntegerField( max_length = 4, blank = True, null = True )
    object_type         = models.ForeignKey( ObjectType, blank = True, null = True )
    individual          = models.ForeignKey( Individual, blank = True, null = True )
    notes               = models.TextField( blank = True, null = True )
_

次に、select_related()を使用して、このモデルに対してこのようなクエリを実行し、外部キー関係が確実に実行されるようにします。

_artifact = Artifact.objects.select_related().get(pk=pk)
_

オブジェクトを取得したら、それをシリアル化し、ビューに返します。

_serializers.serialize( "json", [ artifact ] )
_

これは私が返すものです。外部キー(object_typeと個人)は、関連するオブジェクトの単なるidであることに注意してください。

_[
      {
            pk: 1
            model: "artifacts.artifact"
            fields: {
                year_of_Origin: 2010
                name: "Dummy Title"
                notes: ""
                object_type: 1
                individual: 1
            }
      }
]
_

これは素晴らしいことですが、select_related()を使用するときに期待していたことは、オブジェクトのIDだけでなく、外部キーフィールドに関連オブジェクトが自動的に入力されることでした。

私は最近Djangoに変換していますが、CakePHPでの開発にかなりの時間を費やしています。

Cake ORMで私が本当に気に入っているのは、クエリを呼び出すときにリレーションシップをバインド解除する機能を備え、リレーションシップに従い、ネストされたオブジェクトをデフォルトで作成することでした。

これにより、ケースバイケースで介入を必要としない方法でサービスを抽象化することが非常に簡単になりました。

Djangoはデフォルトではこれを行わないようですが、オブジェクトとそのすべての関連オブジェクトを自動的にシリアル化する方法はありますか?ヒントまたは参考資料をいただければ幸いです。

40
Kieran Lynn

RESTfulな目的ではありませんが、同様の要件がありました。私の場合、「完全な」シリアル化モジュールを使用することで、必要なことを実現できました Django Full Serializers 。これは wadofstuff の一部であり、新しいBSDライセンスの下で配布されます。

Wadofstuffはこれを非常に簡単にします。たとえばあなたの場合、あなたは次のことをする必要があります:

まず、wadofstuffをインストールします。

次に、settings.pyファイルに次の設定を追加します。

SERIALIZATION_MODULES = {
    'json': 'wadofstuff.Django.serializers.json'
}

3番目に、シリアル化に使用するコードに少し変更を加えます。

artifact = Artifact.objects.select_related().get(pk=pk)
serializers.serialize( "json", [ artifact ], indent = 4, 
    relations = ('object_type', 'individual',))

重要な変更は、relationsキーワードパラメータです。唯一の(マイナーな)問題は、関連モデルの名前ではなく、リレーションを形成するフィールドの名前を使用することです。

警告

ドキュメント から:

Wad of Stuffシリアライザーは、モデルのシリアル化時にDjangoシリアライザーと100%互換性があります。データストリームを逆シリアル化するとき、Deserializerクラスのみ標準のDjango serializersによって返されるシリアル化されたデータで動作します。

(エンファシスを追加)

お役に立てれば。

25
Manoj Govindan

更新:実際にManojのソリューションは少し時代遅れであり、Wad of Stuffのシリアライザはしばらくの間未更新のままにされており、私がそれを試したところ、Django 1.6をサポートしなくなったようです。

ただし、 Djangoの公式ドキュメントはこちら をご覧ください。組み込みの自然キーを使用する方法がいくつかあります。 Djangoの組み込みシリアライザーには、自然キーの一部としてImageFieldを使用することをサポートする少し問題があるようです。しかし、それはあなた自身で簡単に修正できます。

12
Shen Haocheng

このチケットの詳細は次のとおりです。

関係をたどる深さを指定して、詳細なシリアル化を許可 https://code.djangoproject.com/ticket/4656

1
guettli

私はこのトピックが古くなっていることを認識していますが、答えを探している人々のために私の解決策を共有しています(検索中、私はここで終わりました)。

JSONに変換できるモデル/クエリセット内にネストされた(外部キー)オブジェクト/辞書(ネストされた(外部キー)オブジェクト/辞書も含むことができる)を与える単純な関数を探していたことに注意してください。

私のmodels.pyには、(モデルクラス内ではなく)カスタム関数があります。

Models.py

def Django_sub_dict(obj):
    allowed_fields = obj.allowed_fields() # pick the list containing the requested fields
    sub_dict = {}
    for field in obj._meta.fields: # go through all the fields of the model (obj)
        if field.name in allowed_fields: # be sure to only pick fields requested
            if field.is_relation: # will result in true if it's a foreign key
                sub_dict[field.name] = Django_sub_dict(
                    getattr(obj, field.name)) # call this function, with a new object, the model which is being referred to by the foreign key.
            else: # not a foreign key? Just include the value (e.g., float, integer, string)
                sub_dict[field.name] = getattr(obj, field.name)
    return sub_dict # returns the dict generated

この関数は、models.Modelが提供されている場合、models.Modelオブジェクトのすべてのフィールドをループします。モデル内の関数を次のように呼び出します(完全を期すために、モデル全体を含めます)。

同じModels.py

class sheet_categories(models.Model):
    id = models.AutoField(primary_key=True, unique=True)
    create_date = models.DateField(auto_now_add=True)
    last_change = models.DateField(auto_now=True)
    name = models.CharField(max_length=128)
    sheet_type = models.ForeignKey(
        sheet_types, models.SET_NULL, blank=False, null=True)
    balance_sheet_sort = models.IntegerField(unique=True)

    def allowed_fields(self):
        return [
                'name',
                'sheet_type',
                'balance_sheet_sort',
                ]

    def natural_key(self):
        return Django_sub_dict(self) # call the custom function (which is included in this models.py)

注:ネストされたJSONオブジェクトには、モデルのallowed_fieldsに含まれるフィールドのみが含まれます。したがって、機密情報は含まれません。

最終的にJSONを生成するために、私のviews.pyに次のビューがあります。

views.py

class BalanceSheetData(ListView): # I believe this doesn't have to **be** a ListView.
    model = models.sheet_categories

    def get_queryset(self):
        return super().get_queryset().filter() # the filter is for future purposes. For now, not relevant

    def get(self, request, *args, **kwargs):
        context = {
            'queryset': serializers.serialize("json",
                                          self.get_queryset(),
                                          use_natural_foreign_keys=True, # this or the one below makes Django include the natural_key() within a model. Not sure.
                                          use_natural_primary_keys=True, # this or the one above makes Django include the natural_key() within a model. Not sure.
                                          ),
        }
        return JsonResponse(context)

これにより、JSON応答で必要なすべてのネストされた詳細が最終的に提供されました。私はJSON応答を共有しませんが、これはほとんど読めないので。

コメントしてください。

1

この古い質問に新しい回答を追加します。モデル、マネージャー、およびクエリセットをシリアル化する簡単に拡張できる方法として、作成し、最近公開した Django-serializable-model です。モデルがSerializableModelを拡張すると、すべての関係のサポートが組み込まれているオーバーライド可能な.serializeメソッドを受け取ります。

あなたの例を使用して、一度関連するすべてのモデル拡張SerializableModel

joins = ['object_type', 'individual']
artifact = Artifact.objects.select_related(*joins).get(pk=pk)
artifact.serialize(*joins)

関係を引数として.serializeを呼び出すと、関連オブジェクトに対してライブラリが再帰的に実行され、それらに対して.serializeも呼び出されます。これは次のような辞書を返します:

{
  'id': 1,
  'year_of_Origin': 2010,
  'name': 'Dummy Title',
  'notes': '',
  'object_type_id': 1,
  'individual_id': 1,
  'object_type': { ... nested object here ... },
  'individual': { ... nested object here ... }
}

次に、この辞書でjson.dumpsを呼び出して、JSONに変換できます。

デフォルトでは、SerializableModelを拡張すると、モデルのマネージャーもSerializableManagerを使用するSerializableQuerySetに設定されます(カスタムマネージャーを使用している場合は、自分で拡張できます)。つまり、マネージャまたはクエリセットで.serializeを呼び出すこともできます。

artifacts = Artifact.objects.select_related(*joins).all()
artifacts.serialize(*joins)

これはクエリセットの各モデルオブジェクトで.serializeを呼び出すだけで、上記と同じ形式で辞書のリストを返します。

Django-serializable-model を使用すると、モデルごとにデフォルトの動作を簡単にオーバーライドでき、各モデルの.serializeに適用されるホワイトリストまたはブラックリストを追加できます。常に特定の結合をシリアル化します(そのため、常にそれらを引数として追加する必要はありません)。

0
agilgur5