如何将Django表单类变量传递给嵌套类Meta?

1 投票
1 回答
1141 浏览
提问于 2025-04-17 08:34

你好!我现在遇到了一点小麻烦。下面的代码运行得很好,完全符合预期。简单来说,这是一种模型表单,它会根据从网址中收到的字符串,或者根据提供的实例的类,动态地确定它的模型。

我想问的是,是否可以把这个功能抽象到另一个模块,也就是 forms.py,通过传递 model_name 变量来实现。我可以顺利地把 model_name 传递给表单类,但我不知道怎么把它传递给 Meta。有没有简单的方法可以做到这一点?如果没有也没关系,不过这样做会让我的视图代码看起来整洁很多。

@user_passes_test(lambda u: u.is_staff, login_url="%slogin/" % NINJA_ADMIN_URL_PREFIX)
def content_form(request, model_name=None, edit=False, call_name=''):
    if edit:
        content = Content.objects.get(call_name=call_name)
        model_name = content.fields.__class__.__name__

    class ContentForm(forms.ModelForm):
        parent = ModelTextField(queryset=Content.objects.all(), widget=JQueryAutocomplete(
                        source_url='%sjson/call_names.json' % NINJA_ADMIN_URL_PREFIX, jquery_opts = {'minLength': 2},
                        override_label='item.fields.call_name', override_value='item.fields.call_name'),
                        required=False)

        class Meta():
            model = get_ninja_type(model_name)
            widgets = {
                    'ninja_type': forms.widgets.HiddenInput(),
                }

        def clean_parent(self):
            call_name = self.cleaned_data['parent']
            if call_name:
                try:
                    parent = Content.objects.get(call_name=call_name)
                except Content.DoesNotExist:
                    raise forms.ValidationError("The call name '%s' doesn't exist. Choose another parent." % call_name)
                return parent
            else:
                return None

    if request.method == 'POST':
        if edit:
            form = ContentForm(request.POST, instance=content.fields)
        else:
            form = ContentForm(request.POST)
        if form.is_valid():
            content = form.save()
            messages.success(request, "Your new content has been saved.")
            return HttpResponseRedirect('%scontent/' % NINJA_ADMIN_URL_PREFIX)

    else:
        if edit:
            form = ContentForm(instance=content.fields)
        else:
            form = ContentForm(initial={'ninja_type': model_name.lower(),
                                    'author': request.user})
    if edit:
        page_title = 'Edit %s' % model_name
    else:
        page_title = 'Create New %s' % model_name

    return render(request, 'ninja/admin/content_form.html', {
                  'form': form,
                  'ninja_type': model_name,
                  'page_title': page_title,
                  'edit': edit,
                  'meta_field_names': NINJA_META_FIELD_NAMES,
                  })

1 个回答

1

当我仔细想想,这个答案其实很明显。一个函数返回一个类是完全没问题的。这就是在views.py开头的内容。我觉得这可能是实现这个功能的最佳方式,或者至少是最简洁的方式。

@user_passes_test(lambda u: u.is_staff, login_url="%slogin/" % NINJA_ADMIN_URL_PREFIX)
def content_form(request, model_name=None, edit=False, call_name=''):
    if edit:
        content = Content.objects.get(call_name=call_name)
        model_name = content.fields.__class__.__name__

    ContentForm = get_content_form(model_name)

    if request.method == 'POST':
        if edit:
            form = ContentForm(request.POST, instance=content.fields)
        else:

这是forms.py中的内容。

def get_content_form(model_name):
    class DynamicContentForm(forms.ModelForm):
        parent = ModelTextField(queryset=Content.objects.all(), widget=JQueryAutocomplete(
                        source_url='%sjson/call_names.json' % NINJA_ADMIN_URL_PREFIX, jquery_opts = {'minLength': 2},
                        override_label='item.fields.call_name', override_value='item.fields.call_name'),
                        required=False)

        class Meta():
            model = get_ninja_type(model_name)
            widgets = {
                    'ninja_type': forms.widgets.HiddenInput(),
                }

        def clean_parent(self):
            call_name = self.cleaned_data['parent']
            if call_name:
                try:
                    parent = Content.objects.get(call_name=call_name)
                except Content.DoesNotExist:
                    raise forms.ValidationError("The call name '%s' doesn't exist. Choose another parent." % call_name)
                return parent
            else:
                return None
    return DynamicContentForm

撰写回答