Dynamically modify help_text on an Inline form

I have a Rule model with a foreign key on Alternative.
I am trying to modify the help text on a field for Alternative based on the relevant instance.

ie. if I have 3 Alternatives attached to the Rule for each Alternative form the help text on that field needs to be dynamically computed for each of the 3 instances separately.

I tried modifying the model itself but this just resulted in the help text being overridden and all 3 instances would then have the same modified help text.

I also tried to modify the AlternativeInline instance but if I fetch the formset, it does not yet contain the specific model instance.

Any hint?

Note for a non inline (ie. top level Admin class) the following worked:

    def get_form(self, request, obj=None, change=False, **kwargs):
        form = super().get_form(request, obj=obj, change=change, **kwargs)

        if obj:
            form.base_fields["foo"].help_text = collect_help_text(obj)

If I had to try and do this in the admin, I’d probably be looking at a custom InlineModelAdmin class with an overridden/customized formset using a custom get_form_kwargs method to customize the individual instances.

Thank you for the feedback.

I have tried this but at this stage I think the specific model instance has not yet been assigned to the form, ie. all I will then modify is the generic formset of the inline.

Ahh yes, I see that now. The get_form_kwargs method is used to create keyword parameters for the call to _construct_form.

Since that’s the case, you’re likely going to want to either override the ModelForm being used to create the formset to modify its __init__ method (since that is definitely receiving the model instance upon being created), or overriding the get_formset method in the InlineModelAdmin class, or possibly even overriding _create_formsets in the ModelAdmin class.

In any event, I suggest that you might want to read the source for these classes in django.contrib.admin.options to gain an understanding of what’s happening where.

Unfortunately ModelForm init does not receive the model instance. The signature is def __init__(self, *args, **kwargs).

As for get_formset, it does receive a model instance but it is not the instance for the inline form (ie. Alternative), but rather the model instance that includes the inline (ie. Rule). This is the signature def get_formset(self, request, obj=None, **kwargs)

I will poke around further in the class as you suggested. Thx!

The signature of a method alone is not sufficient information to determine whether or not something is passed into a method. The **kwargs is used extensively to pass data through these various methods. Additionally, ModelForm is one of those classes that is heavily dependent upon its metaclass for its construction.

So yes, you will find that the __init__ method of a ModelForm does receive the instance for which it is being built. I suggest you read the _construct_form method in django.forms.models.BaseModelFormSet.

kwargs[“instance”] = self.get_queryset()[i]

So I think I figured it out using __init__ afterall.

With the below code I do get the instance of the inline model (ie. Alternative). However, the changes I make to the help_text, do not end up getting rendered.

class AlternativeForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(AlternativeForm, self).__init__(*args, **kwargs)
        instance = getattr(self, "instance", None)
        if instance and isinstance(instance, Alternative):
            self.base_fields["foo"].help_text = collect_help_text(instance.language)

You want to modify the fields attribute, not base_fields.

ah yes right … copied the wrong version.

thanks so much for your help!