@factory.post_generation
の工場を呼び出すだけで解決しました。厳密に言えば、これは具体的な問題の解決策ではありませんが、なぜこれがより良いアーキテクチャーになったのかを以下に詳しく説明します。 @ rhunwickのソリューションはSubFactory(LazyAttribute(''))
を真にRelatedFactory
に渡しますが、これは私の状況では正しくないという制限が残っていました。
我々はObjectWithSubObjectsFactory
にProblemFactory
からsub_object
とobject
の作成を動かす(およびexclude
句を削除)、およびProblemFactory
の末尾に次のコードを追加します。
@factory.post_generation
def post(self, create, extracted, **kwargs):
if not create:
return # No IDs, so wouldn't work anyway
object = ObjectWithSubObjectsFactory()
sub_object_ids_by_code = dict((sbj.name, sbj.id) for sbj in object.subobject_set.all())
# self is the `Foo` Django object just created by the `ProblemFactory` that contains this code.
for another_obj in self.anotherobject_set.all():
if another_obj.name == 'age_in':
another_obj.attribute_id = sub_object_ids_by_code['Age']
another_obj.save()
elif another_obj.name == 'income_in':
another_obj.attribute_id = sub_object_ids_by_code['Income']
another_obj.save()
だから、RelatedFactory
コールがPostGeneration
呼び出し前に実行されているようです。 this questionで
命名は理解しやすいですので、ここではそのサンプル問題の同じ溶液のコードは次のとおりです。
dataset
、column_1
とcolumn_2
の創出新工場DatasetAnd2ColumnsFactory
に移動し、以下のコードがあるさFunctionToParameterSettingsFactory
の末尾に追加されます。
@factory.post_generation
def post(self, create, extracted, **kwargs):
if not create:
return
dataset = DatasetAnd2ColumnsFactory()
column_ids_by_name =
dict((column.name, column.id) for column in dataset.column_set.all())
# self is the `FunctionInstantiation` Django object just created by the `FunctionToParameterSettingsFactory` that contains this code.
for parameter_setting in self.parametersetting_set.all():
if parameter_setting.name == 'age_in':
parameter_setting.column_id = column_ids_by_name['Age']
parameter_setting.save()
elif parameter_setting.name == 'income_in':
parameter_setting.column_id = column_ids_by_name['Income']
parameter_setting.save()
私は、このように、工場を設定するオプションを渡し、このアプローチを拡張:
whatever = WhateverFactory(options__an_option=True, options__another_option=True)
次に、このファクトリコードオプションを検出し、必要なテストデータを生成した(方法をに変更され注意パラメータ名のプレフィックスと一致するように、options
):
@factory.post_generation
def options(self, create, not_used, **kwargs):
# The standard code as above
if kwargs.get('an_option', None):
# code for custom option 'an_option'
if kwargs.get('another_option', None):
# code for custom option 'another_option'
私はその後、さらにこれを拡張しました。私の望むモデルは自己結合を含んでいたので、私の工場は再帰的です。だから、のようなコールのために:私が持っている@factory.post_generation
以内
whatever = WhateverFactory(options__an_option='xyz',
options__an_option_for_a_nested_whatever='abc')
:あなたは、私は、このオプションではなく、私にrhunwicksの適切な解決策@行った理由を読む必要はありません
class Meta:
model = Whatever
# self is the top level object being generated
@factory.post_generation
def options(self, create, not_used, **kwargs):
# This generates the nested object
nested_object = WhateverFactory(
options__an_option=kwargs.get('an_option_for_a_nested_whatever', None))
# then join nested_object to self via the self join
self.nested_whatever_id = nested_object.id
いくつかの注意上記の質問。 2つの理由があった。
私がそれを実験するのを止めたのは、RelatedFactoryと世代後の順番が信頼できないということでした。明らかに無関係な要素がそれに影響します。おそらく遅延評価の結果です。明白な理由がなく工場が突然稼働を停止するというエラーがありました。一度、私はRelatedFactoryに割り当てられた変数の名前を変更したからです。これはばかげて聞こえるが、私はそれを死にかけてテストした(そしてhereを掲示した)が、変数の名前を変更することにより、RelatedFactoryとpost genの実行順序が確実に変更されたことは間違いない。私はまだそれが何か他の理由(私は診断することが決してなかった)のために再び起こるまで、これが私のためにいくつかの見落としであったと考えました。
第2に、宣言型コードが混乱し、柔軟性がなく、再因子付けするのが難しいことがわかりました。インスタンス化中に異なる設定を渡すのは簡単ではないので、テストデータのさまざまなバリエーションに対して同じファクトリを使用できるので、コードを繰り返す必要があります。object
は工場に追加する必要がありますMeta.exclude
リスト - データを生成するコードの信頼性の高いエラーでした。開発者は、コントロールフローを理解するために、いくつかの工場を何回か渡す必要があります。これらのトリックを使い果たすまで、宣言的なボディーの間にジェネレーションコードが広がり、残りのコードはポストジェネレーションに入り込むか、非常に複雑になります。共通の例は、相互依存オブジェクトの別の三つ組(例えば、モデル、パラメータ値など)の外部キーとして、相互依存モデル(例えば、親子カテゴリ構造またはデータセット/属性/エンティティ)の三つ組である他のモデルのパラメータ値へ)。これらのタイプの構造のうちのいくつかは、特に入れ子になっていると、すぐに処理できなくなります。
私はそれが実際にfactory_boyの精神ではないことを認識していますが、すべてをこれらの問題を解決したポストジェネレーションに入れています。パラメータを渡すことができるので、同じ単一のファクトリがすべての複合モデルテストデータ要件を満たし、コードは繰り返されません。混乱する継承と上書きの連鎖に依存せず、いくつかのバグの影響を受けるのではなく、すぐにわかりやすく完全に信頼できる作成シーケンスを簡単に確認できます。インタラクションは明白であるため、機能を追加するためにすべてを消化する必要はなく、機能のさまざまな領域は、世代後のif節でグループ化されます。作業変数を除外する必要はなく、工場コードの期間中は参照することができます。ファンクションの記述はファクトリクラス名ではなくパラメータ名になるため、単体テストコードは簡略化されているため、WhateverCreateXYZCreateABC..()
ではなくWhateverFactory(options__create_xyz=True, options__create_abc=True..
のような呼び出しでデータを作成します。これは、コードのきれいな責任の分裂をきれいにします。
私は同じpkを指定すると問題を解決できると思います。 –
'sub_object_id = sub_object_id'を意味していますか?または 'LazyAttribute(lambda obj:obj.factory_parent.sub_object.id)'? – Chris
[この質問](http://stackoverflow.com/questions/32995158/getting-id-of-associated-child-records-in-factory-boy/33043428#33043428)への回答は、別の方法を示すようですが、しかし、私はそれをこの要件に適合させることはできません。 (この質問は、これらの回答の1つにバグを修正することです)。 – Chris