0

序文

Django フレームワーク (現在、安定版 1.4.3) に基づく「コレクション管理システム」を実装しています。
ORM の優れたリレーショナルな性質を工夫して、いくつかの厄介な「オブジェクト指向の側面」を強制します。その使用法について、最小限の例を抽出しようとしました。(免責事項: 完全な機能説明はまた長くて退屈な読み物になるため、設計上の議論に発展しない限り作成しません)。

抜粋した例

Django モデルを使用して OO を大まかにエミュレートするとします。さまざまな属性(= クラスのデータ メンバー) で構成できるリリースモデル (= クラス) があります。その後、異なるインスタンス(= オブジェクト) を同じReleaseに対してインスタンス化できます。各インスタンスは、関連するリリースに存在する属性のすべて/一部/なしの値を格納できます。 これにより、次のモデルが得られます。

#An attribute is a name, that can be given a value at instance level.
class Attribute(models.Model):
    name = models.CharField(max_length=60)

#A release can be compared to a class in OO :
# it's a data structure description (the attributes' list).
class Release(models.Model):
    name = models.CharField(max_length=60)
    #A release is caracterized by a list of attributes
    # the same attribute can be present in several releases
    attributes = models.ManyToManyField(Attribute, blank=True, null=True)

#An instance entry can be compared to an object in OO :
# it instantiates a release
# it can hold its own value for each of the attributes in this release.
#Nb : Attributes are all optional.
class Instance(models.Model):
    #the instantiated release
    release = models.ForeignKey(Release)

#Store the actual attribute-value pairs for the different instances.
class InstanceAttribute(models.Model):
    instance = models.ForeignKey(Instance)
    attribute = models.ForeignKey(Attribute)
    value = models.CharField(max_length=60)

問題

そのアプローチで強力な Django admin を使用することは素晴らしいことです。

  • Releasesを追加し、それらをAttributesで構成するために、すべてがすぐに使用できます。

  • インスタンスの追加ビューはますます複雑になっています。(関連するリリースID は、次の形式の URL で GET によって転送できます: instance/add/?release=x )。このビューが読み込まれると、次のInstanceAttributeの InlineFormset を提案する必要があります。

    1. 関連するリリースを構成する属性の数と一致するフォームの数
    2. これらのフォームのそれぞれについて、AttributeフィールドはReleaseのAttributeに初期化する必要があり、クエリセットはこのAttributeのみを表示するように制限されています。

をオーバーライドして、追加のパラメーターを目的の数値に設定してModelAdmin.get_formsets()を返すことで、#1 を解決できます。add_view のソース をよく見てみると、#2 を実装する良い方法が見つかりません...inlineformset_factory

4

1 に答える 1

0

私は自分自身の質問に、機能している解決策で答えていますが、控えめに言っても満足のいくものではありません。私が達成しようとしていることを正確に示すことで、読者が上記の問題を理解するのにも役立つと思います。

グローバルな状態ベースのソリューション

したがって、(悪い) アイデアは、ビューで使用されるInstanceAttributeフォームをサブクラス化することです。InstanceAttributeForm静的データ メンバー__init__でグローバル状態を導入しながら、フォームをオーバーライドできます。

  • このグローバルな状態は、現在構築されている形式のカウンター( Attributesのリストをインデックス化するために使用される) である限り、提示する必要があるAttributesのリストなどのデータを受け取る必要があります。
  • オーバーライドさ__init__れたものは、これらのグローバル変数を使用して、フォームセットによって構築された各フォームに属性をバインドします。

コード

class InstanceAttributeForm(forms.ModelForm):
    #form_id count the initializations (to know which form is currently constructed by the formset)
    form_id = 0
    total = 0
    #the list of attributes in the instantiated release
    #it should be populated before the formset start the constructor calls
    attributes = []

    def __init__(self, *args, **kwargs):
        super(InstanceAttributeForm, self).__init__(*args, **kwargs)
        #"deferred template rendering' is trying to construct 3 additional forms
        #bonus point if you can comment on the reason for this behavior  ; )
        if InstanceAttributeForm.form_id==InstanceAttributeForm.total:
            return

        attribute = InstanceAttributeForm.attributes[InstanceAttributeForm.form_id]
        self.initial = {'attribute' : attribute}
        self.fields['attribute'].queryset = Attribute.objects.filter(id=attribute.id)
        InstanceAttributeForm.form_id += 1

実際にフォームのコンストラクターに入る前に、これらのグローバル変数を初期化し、InstanceAttributeFormをフォームセット ファクトリにフックする必要があります。これを実現するために、 Instance ModelAdminをカスタマイズできます:

class InstanceAdmin(admin.ModelAdmin):  
    #Responsible for re-initializing the global state each time before calling the super add_view
    def add_view(self, request, form_url='', extra_context=None):
        initialize_globalstate(request)
        return super(InstanceAdmin, self).add_view(request, form_url, extra_context)

    #Return a formset factory specifying the exact number of required forms
    # and hooking our specialised ModelForm class.
    def get_formsets(self, request, obj=None):
        yield inlineformset_factory(
            Instance,
            InstanceAttribute,
            form=InstanceAttributeForm,
            can_delete=False,
            extra=InstanceAttributeForm.total)

完全を期すために、グローバル状態の初期化手順 (add_view()上記のオーバーライドで呼び出されます):

def initialize_globalstate(request):
    instantiated_release = Release.objects.get(id=request.GET['release'])

    InstanceAttributeForm.form_id = 0
    InstanceAttributeForm.attributes = instantiated_release.attributes.all()
    InstanceAttributeForm.total = len(InstanceAttributeForm.attributes)
于 2013-03-04T23:05:39.530 に答える