1

modelフィクスチャのモデルクラスを示すプロパティをフィクスチャに設定する、djangoフィクスチャシステムに少し似たものを実装したいと思います。こんな感じ

my_app.models.my_model

私の質問は、この「パス」が指すクラスのインスタンスを作成するために、このような文字列を処理する標準的な方法は何ですか。私はそれが次のように見えるべきだと思います:

  1. モジュール名とクラス名の部分に分割します
  2. モジュールをロードします(ロードされていない場合)
  3. モジュールからその名前でクラスを取得します
  4. それをインスタンス化する

どのくらい正確にそれを行う必要がありますか?

編集:私は汚い解決策を思いついた:

def _resolve_class(self, class_path):
    tokens = class_path.split('.')
    class_name = tokens[-1]
    module_name = '.'.join(tokens[:-1])
    exec "from %s import %s" % (module_name, class_name)
    class_obj = locals()[class_name]
    return class_obj

それは仕事ですが、execの使用と、フィクスチャの悪意のある準備によって実行を操作する可能性があるため、汚いです。それはどのように適切に行われるべきですか?

4

4 に答える 4

2

これを正確に行うためにフレームワークの周りで使用されるため、私はdjangoのimport_module関数をよく使用します。

from django.utils.importlib import import_module

path = 'foo.bar.classname'
module_path, class_name = path.rsplit('.', 1)
module = import_module(module_path)
cls = getattr(module, class_name)()  # instantiated!

ソリューションの場合は、モジュールでexec使用してクラスを取得することで簡単に削除できます。__import__

于 2012-10-22T20:56:30.117 に答える
1

loaddata編集:なぜコマンドが 行うことを本質的に行わないのですか?https://github.com/django/django/blob/master/django/core/management/commands/loaddata.py#L184 djangodeserialize関数の使用: https ://docs.djangoproject.com/en/dev/topics/シリアル化/#deserializing-data

于 2012-10-22T15:46:24.747 に答える
1

関数でexecを使用することの危険性は、攻撃者が偽の値を提供することを可能にし、攻撃者が望むコードを「誤って」実行することになることに注意してください。ここでは、それを正確に可能にする関数を直接記述しています。execを使用しても、それほど悪化することはありません。唯一の違いは、execがないと、Pythonのインポートパス上のファイルにコードを取り込む方法を理解する必要があることです。

それはあなたがそれをすべきではないという意味ではありません。自分がしていることに注意してください。プラグインフレームワークには本質的にこの問題があります。実行時にフレームワークを拡張可能にすることの全体的なポイントは、プラグインを構成できる人なら誰でも、プログラム内で好きなコードを実行できるようにすることです。エンドユーザーがプラグインを構成しているのと同じ人ではない環境でプログラムを使用する場合は_resolve_class、同じように扱うようにしてくださいexec。直接渡す文字列をユーザーが入力できないようにしてください_resolve_class

さて、それはさておき、あなたはexec非常に簡単に使用を避けることができます。Pythonには__import__、インポートメカニズムの基盤となる実装を取得するための組み込み関数があります。これを使用して動的インポートを行うことができます(help(__import__)この回答を書くためにどのように機能するかを理解するのに十分でした。もう少し詳細が必要な場合はドキュメントもあります)。これを使用すると、関数は次のようになります。

def _resolve_class(self, class_path):
    modulepath, classname = class_path.rsplit('.', 1)
    module = __import__(modulepath, fromlist=[classname])
    return getattr(module, classname)

rsplit(モジュールパスを分割して再度結合する必要がないように、最大​​数の分割も使用していることに注意してください)

于 2012-10-22T23:15:48.540 に答える
0

Djangoシリアライザーで使用されている形式をコピーして、jsonオブジェクト作成コードを記述します。dictのリストを定義します。各dictには、アプリ名とモデル名を含むいくつかのメタデータフィールドmodelと、モデルフィールドデータを含むネストされたdictが含まれています。

[
    {
        "pk": 1,
        "model": "app_name.model_name",
        "fields": {
            "default_value": "",
            "category": null,
            "position": 10,
            ...
        }
    },
    {
        "pk": 2,
        "model": "app_name.model_name",
        "fields": {
            ...
        }
    }
]

したがって、を使用json.loads()してデータを取得し、ディクテーションを繰り返してデータを引き出します

for obj_dict in json.loads(data):
    app_name, model_name = obj_dict.split('.')
    # some magic here to import your model
    my_model = magic_import_function(app_name, model_name)
    foo = my_model(*obj_dict["fields"])
    foo.save()
于 2012-10-22T17:53:43.003 に答える