Django:动态更新 ModelForm 的字段小部件

Django: Dynamically update ModelForm's Field widgets

我想根据 Exercise class 中的 exercise_type 字段动态生成 ModelForm 的 Meta Class 小部件。 如何获取值?

class ExerciseAdminForm(ModelForm):
        
    class Meta:
        model = Exercise
        fields = '__all__'
        widgets = {
            'starter_code': _make_language_mode(exercise_type=Exercise.exercise_type)
        }

我不知道如何获取excercise_object。


基本上我需要覆盖管理界面中的默认表单,以使用 CodeMirror and Skulpt 对输入字段的支持。

class ExerciseAdminForm(ModelForm):
    
    def __init__(self, *args, **kwargs):
        super(ExerciseAdminForm, self).__init__(*args, **kwargs)
        print("DEBUG: len(kwargs)=",len(kwargs))
        initial_dict = kwargs['initial']
        print("DEBUG: len(initial_dict)=",len(initial_dict))
        self.fields['starter_code'].widget = _make_language_mode(exercise_type=excercise_object.exercise_type)


    class Meta:
        model = Exercise
        fields = '__all__'
        widgets = {
            'preamble': _make_markdown_mode()
        }

我的调试消息显示:

('DEBUG: len(kwargs)=', 1)

('DEBUG: len(initial_dict)=', 0)

我的函数从 Python 2.7 库站点包中调用:

/root/.virtualenvs/codebench/local/lib/python2.7/site-packages/django/contrib/admin/options.py

第 1481 行:

form = ModelForm(initial=initial) 

在changeform_view函数中:

@csrf_protect_m
@transaction.atomic
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):

    to_field = request.POST.get(TO_FIELD_VAR, request.GET.get(TO_FIELD_VAR))
    if to_field and not self.to_field_allowed(request, to_field):
        raise DisallowedModelAdminToField("The field %s cannot be referenced." % to_field)

    model = self.model
    opts = model._meta
    add = object_id is None

    if add:
        if not self.has_add_permission(request):
            raise PermissionDenied
        obj = None

    else:
        obj = self.get_object(request, unquote(object_id), to_field)

        if not self.has_change_permission(request, obj):
            raise PermissionDenied

        if obj is None:
            raise Http404(_('%(name)s object with primary key %(key)r does not exist.') % {
                'name': force_text(opts.verbose_name), 'key': escape(object_id)})

        if request.method == 'POST' and "_saveasnew" in request.POST:
            return self.add_view(request, form_url=reverse('admin:%s_%s_add' % (
                opts.app_label, opts.model_name),
                current_app=self.admin_site.name))

    ModelForm = self.get_form(request, obj)
    if request.method == 'POST':
        form = ModelForm(request.POST, request.FILES, instance=obj)
        if form.is_valid():
            form_validated = True
            new_object = self.save_form(request, form, change=not add)
        else:
            form_validated = False
            new_object = form.instance
        formsets, inline_instances = self._create_formsets(request, new_object, change=not add)
        if all_valid(formsets) and form_validated:
            self.save_model(request, new_object, form, not add)
            self.save_related(request, form, formsets, not add)
            if add:
                self.log_addition(request, new_object)
                return self.response_add(request, new_object)
            else:
                change_message = self.construct_change_message(request, form, formsets)
                self.log_change(request, new_object, change_message)
                return self.response_change(request, new_object)
    else:
        if add:
            initial = self.get_changeform_initial_data(request)
            form = ModelForm(initial=initial)
            formsets, inline_instances = self._create_formsets(request, self.model(), change=False)
        else:
            form = ModelForm(instance=obj)
            formsets, inline_instances = self._create_formsets(request, obj, change=True)

    adminForm = helpers.AdminForm(
        form,
        list(self.get_fieldsets(request, obj)),
        self.get_prepopulated_fields(request, obj),
        self.get_readonly_fields(request, obj),
        model_admin=self)
    media = self.media + adminForm.media

    inline_formsets = self.get_inline_formsets(request, formsets, inline_instances, obj)
    for inline_formset in inline_formsets:
        media = media + inline_formset.media

    context = dict(self.admin_site.each_context(request),
        title=(_('Add %s') if add else _('Change %s')) % force_text(opts.verbose_name),
        adminform=adminForm,
        object_id=object_id,
        original=obj,
        is_popup=(IS_POPUP_VAR in request.POST or
                  IS_POPUP_VAR in request.GET),
        to_field=to_field,
        media=media,
        inline_admin_formsets=inline_formsets,
        errors=helpers.AdminErrorList(form, formsets),
        preserved_filters=self.get_preserved_filters(request),
    )

    context.update(extra_context or {})

    return self.render_change_form(request, context, add=add, change=not add, obj=obj, form_url=form_url)

也许您可以覆盖 __init__ 函数来执行此操作?将练习实例作为关键字参数传递给表单。假设您通过表单和 _make_language_mode returns 小部件传递练习对象。

def __init__(self, *args, **kwargs):
    excercise_object = kwargs["excercise"]
    self.fields['starter_code'].widget = _make_language_mode(exercise_type=excercise_object.exercise_type)

这是您想要覆盖或修改时的示例 fields.widget

class CommentForm(forms.ModelForm):
    class Meta:
        model = Comment
        fields = ['name', 'comment']
        widgets = {
            'name': forms.TextInput(attrs={'class': 'form-control'}),
            'comment': forms.TextInput(attrs={'class': 'form-control'}),
        }

    def __init__(self, *args, change=None, **kwargs):
        super().__init__(*args, **kwargs)
        if change: 
            self.fields['name'].widget.attrs.update({'class': 'special'})
            self.fields['comment'].widget.attrs.update(size='40')

https://docs.djangoproject.com/en/stable/ref/forms/widgets/