0

tl;dr : モデルのフォームを特定のモデルに関連付ける簡単な方法を探しています。モデルだけを知っていれば、適切な作成/編集フォームをレンダリングできます。対応する の名前を Model クラスの文字列属性として保存することで解決策を開発しましたが、ModelFormdjango を初めて使用するので、これが望ましい解決策であるかどうか興味があります。


私は、多数の製品グループのタスクを保存する django タスク/プロジェクト管理サイトに取り組んでいます。mysite/<ProductGroup>/create_task.htmlに移動すると、その製品グループ内でタスク/プロジェクトを作成するためのフォームにユーザーを誘導する必要があります。デフォルトではTask、単純な を使用して基本モデル インスタンスを編集できますModelFormTaskただし、特定の製品グループについては、モデルをサブクラス化し (例) 、そのサブクラスに固有のものをSalesTask表示するオプションが必要です (例)。 ModeFormSalesTaskForm

私の現在の解決策は、 task-object-type を contenttype としてProductGroupモデルに保存することです。

class ProductGroup(models.Model):
    task_type = models.ForeignKey(ContentType)
    ...
    <define other fields here>

ModelForm次に、レンダリング時に対応するものを使用する特別な文字列属性を持つベース タスク モデルを定義します。

<models.py>
class Task(models.Model):
    product_group = models.ForeignKey(ProductGroup)
    ...
    <define task fields common to all Task subclasses>
    ...
    # Associate model with a form (regular python class attribute, 
    # not a django field)
    form = 'TaskForm'

<forms.py>
class TaskForm(ModelForm):
    class Meta:
        model = Task

*Note that it would be slightly more convenient if I could set Task.form equal 
to the actual TaskForm(ModelForm) class rather than a string, but I couldn't 
get around the circular imports when trying this route (models.py `Task` 
would need to import `Taskorm` from forms.py, which itself needs to import 
`Task`).*

この設定により、特定の製品グループの Task モデルを簡単に拡張できます。これは、 と をサブクラスTask化し、モデル サブクラス定義TaskFormの属性 (例: ) をオーバーライドしてから、 の sales インスタンスに task_type 外部キーを設定するだけです。Task.formSalesTask.form = 'SalesTaskForm'ProductGroup

結果のcreate_taskビュー関数は、特定の製品グループに適したフォームをインテリジェントにレンダリングできます。

<views.py>
...
import mysite.forms as taskforms
...
def create_task(request, name):
    try:
        product_group = ProductGroup.object.get(product_group_iexact=name)
    except ProductGroup.DoesNotExist:
        raise Http404
if request.method == 'POST':
    task_model = product_group.item_type.model_class()
    try:
        form = taskforms.__getattribute__(task_model.form)
    except AttributeError:
        raise Http404

    if form.is_valid():
        # Process form
    ...

これは機能しているようで、解決策に不満はありませんが、フォームを特定のモデルに関連付ける必要があるのは一般的なようで、django には比較的新しいので、組み込みまたはこれに対処するより雄弁な方法は?

前もって感謝します。

4

1 に答える 1

0

sergzach のコメントに基づいて、現在のソリューションを放棄し、代わりにクラスベースのジェネリック ビューに切り替える必要があることに気付きました。組み込みのクラス ベースのビューに関するドキュメントは (単純な例を超えて) あまり見つかりませんでしたTemplateViewが、のソースを掘り下げると、非常に雄弁なソリューションを提供するビュー クラス がdjango.views.gereic.edit明らかになります。CreateView

url.conf でtemplate_nameandを引数として指定したい場合は、直接インポートして使用できます。私の場合、url の正規表現から product_group を取得し、指定された製品グループのフィールドを使用して適切なモデルを取得したいと考えていました。したがって、私のクラス定義は上記と同じままで、url.py は次のようになります。modelCreateViewContentTypeProductGroup

from mysite.views import CreateTask
urlpatterns = patterns('',
    ...
    url(r'^product_groups/(?P<product_group>[\w-]+)/new_task$', 
        CreateTask.as_view(),
        name='create_task'),
    ...

次に、views.py で、取得した製品グループから適切なモデルを取得するメソッドを単純にサブクラス化CreateViewしてオーバーライドします。get_form_class

class CreateTask(CreateView):
    template_name = "item_form.html"
    def get_form_class(self):
        """
        Returns the form class to use in this view
        """
        if self.form_class:
        # If we pass form_class as an arg, use that
                return self.form_class
        if self.kwargs['product_group']:
        # Otherwise, get the product_group from the url regex, get its associated
        # task model subclass and have the form_facory generate a form_class
            try:
                product_group = ProductGroup.objects.get(product_group__iexact=
                        self.kwargs['product_group'])
            except ProductGroup.DoesNotExist:
                raise Http404
            model = product_group.task_type.model_class()
        # The remainder is straight from CreateView's definition:
        else:
                if self.model is not None:
                    # If a model has been explicitly provided, use it
                    model = self.model
                elif hasattr(self, 'object') and self.object is not None:
                    # If this view is operating on a single object, use
                    # the class of that object
                    model = self.object.__class__
                else:
                    # Try to get a queryset and extract the model class
                    # from that
                    model = self.get_queryset().model
        return model_forms.modelform_factory(model)

CreateViewこのクラス (または から継承する他のクラス)の非常に洗練された機能は、ModelFormMixin対応するモデルModelFormが作成されていないか、引数として提供されていない場合、 がを使用しModelFormMixinてジェネリックを自動的に生成することです。そのため、カスタマイズが不要な場合は、対応するモデル フォームを作成する必要さえありません。したがって、Sales グループが追加のフィールドを含める必要がある場合、基本モデルを としてサブクラス化し、このサブクラスにフィールドを追加してから、外部キーがではなくを指すように設定するだけです。とても滑らか!ModelFormmodel_form_factorybudgetTaskSalesTaskbudgettask_typeSalesTaskTask

于 2012-07-27T17:26:11.440 に答える