Dynamic form

This is my code

def generate_dynamic_formset(app_label, head_model_name, model_configs, instance=None, hide_fields=None, readonly_fields=None):
    """
    Generate dynamic forms and formsets based on the provided model configurations.
    """

    hide_fields = hide_fields or []
    readonly_fields = readonly_fields or []

    HeadModel = apps.get_model(app_label=app_label, model_name=head_model_name)

    # Dynamically create a form class for the head model with specified fields and Crispy Forms integration
    class DynamicHeadForm(modelform_factory(HeadModel, fields=model_configs['head_fields'])):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.helper = FormHelper()
            self.helper.form_method = 'post'
            self.helper.form_tag = False  # Let the template handle the form tag

            if 'password' in self.fields:
                self.fields['password'].widget = PasswordInput()

            # Apply read-only fields if in edit mode
            if instance:
                for field in readonly_fields:
                    if field in self.fields:
                        self.fields[field].widget.attrs['readonly'] = True

                # Hide or clear fields as specified
                for field in hide_fields:
                    if field in self.fields:
                        self.fields[field].initial = None
                        self.fields[field].required = False  # Make the field optional
            
                    
        def clean(self):
            cleaned_data = super().clean()
            # Remove hidden fields from cleaned_data
            for field in hide_fields:
                if field in cleaned_data and cleaned_data[field] == '':
                    # Ensure the field is not saved if it has an empty value
                    cleaned_data.pop(field, None)
            return cleaned_data

    head_form = DynamicHeadForm(instance=instance)

    formsets = []
    for config in model_configs['body_models']:
        BodyModel = apps.get_model(app_label=app_label, model_name=config['model_name'])
        fk_field = config['fk_field']

        DynamicFormset = inlineformset_factory(
            HeadModel, 
            BodyModel, 
            fields=config['fields'], 
            extra=3
        )
        
        # Dynamically create a formset with Crispy Forms integration
        class DynamicBodyFormset(DynamicFormset):
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
                for form in self.forms:
                    form.helper = FormHelper()
                    form.helper.form_method = 'post'
                    form.helper.form_tag = False  # Let the template handle the form tag

                    # Apply read-only fields to body model forms
                    if instance:
                        for field in readonly_fields:
                            if field in form.fields:
                                form.fields[field].widget.attrs['readonly'] = True

                        # Hide or clear fields as specified for body model forms
                        for field in hide_fields:
                            if field in form.fields:
                                form.fields[field].initial = None  # Clear the field value
                                form.fields[field].required = False  # Make the field optional

            def clean(self):
                cleaned_data = super().clean()
                for form in self.forms:
                    form_cleaned_data = form.clean()
                    # Remove hidden fields from cleaned_data
                    for field in hide_fields:
                        if field in form_cleaned_data and form_cleaned_data[field] == '':
                            # Ensure the field is not saved if it has an empty value
                            cleaned_data.pop(field, None)
                return cleaned_data                    

        formset = DynamicBodyFormset(instance=instance)
        formsets.append({'formset': formset, 'fk_field': fk_field})

    return head_form, formsets

It does not return 3 extra form. It returns only one form. Please help to fix this.

Welcome @mshs013 !

Side note: When posting code here, enclose the code between lines of three backtick - ` characters. This means you’ll have a line of ```, then your code, then another line of ```. This forces the forum software to keep your code properly formatted. (I’ve taken the liberty of fixing your original post for you.)

Please show the view that is using this code.

Thank you I’m new here.

def dynamic_multiform_view(request, app_label, head_model_name, pk=None, model_configs=None, hide_fields=None, readonly_fields=None, is_form_row=True, extra_form=1):
   """
   A dynamic view for creating and updating models with related inline formsets using Crispy Forms.
   """
   instance = None
   if pk:
       HeadModel = get_model(app_label, head_model_name)
       instance = get_object_or_404(HeadModel, id=pk)

   head_form, body_formsets = generate_dynamic_formset(
       app_label,
       head_model_name,
       model_configs,
       instance=instance,
       hide_fields=hide_fields or [],
       readonly_fields=readonly_fields or [],
   )
   
   has_file_field = any(
       field.widget.input_type == 'file' for formset in body_formsets for field in formset['formset'].forms[0].fields.values()
   )

   # Add this before checking for 'Profile'
   #print("Model Configurations:", model_configs)

   # Check if 'Profile' is present
   profile_model_in_body_models = any(
       body_model['model_name'] == 'Profile'
       for body_model in model_configs['body_models']
   )
   #print("Profile Model in Body Models:", profile_model_in_body_models)

   if request.method == 'POST':
       head_form = head_form.__class__(request.POST, request.FILES, instance=instance)
       body_formsets_instances = [
           formset['formset'].__class__(request.POST, request.FILES, instance=instance) for formset in body_formsets
       ]


       if head_form.is_valid() and all([formset.is_valid() for formset in body_formsets_instances]):
           if profile_model_in_body_models:
               post_save.disconnect(create_or_update_user_profile, sender=User)

           saved_head = head_form.save(commit=False)

           # Handle password updates
           if 'password' in head_form.cleaned_data and head_form.cleaned_data['password']:
               saved_head.set_password(head_form.cleaned_data['password'])

           saved_head.save()

           if profile_model_in_body_models:
               post_save.connect(create_or_update_user_profile, sender=User)

           # Save body formsets
           for body_formset in body_formsets_instances:
               instances = body_formset.save(commit=False)
               for obj in instances:
                   setattr(obj, body_formsets[0]['fk_field'], saved_head)  # Set the foreign key to the head instance
                   obj.save()
               body_formset.save_m2m()

           # Add a specific flash message based on whether the instance is new or being updated
           if pk:
               messages.success(request, f'{head_model_name} was updated successfully.')
           else:
               messages.success(request, f'{head_model_name} was created successfully.')

           return redirect(reverse(f'view_{head_model_name.lower()}'))  # Adjust with your success URL

   context = {
       'head_form': head_form,
       'body_formsets': body_formsets,
       'has_file_field': has_file_field,
       'is_form_row':is_form_row,
       'base_model_name': head_model_name,
       'view_url': f'view_{head_model_name.lower()}',
       'pk': pk,
   }
   return render(request, 'core/dynamic_multiform.html', context)

this is my utils function and

def userForm(request, pk=None):

   model_configs = {
       'head_fields': ['first_name', 'last_name', 'email', 'password', 'is_active'],
       'body_models': [
           {
               'model_name': 'Profile',
               'fields': ['official_id', 'contact_no', 'user_img', 'user_sign'],
               'fk_field': 'user',
           }
       ]
   }
   
   hide_fields = ['password']  # Hide password field in edit form
   readonly_fields = ['email']  # Make email read-only in edit mode

   return dynamic_multiform_view(
       request,
       app_label='core',
       head_model_name='User',
       pk=pk,
       model_configs=model_configs,
       hide_fields=hide_fields,
       readonly_fields=readonly_fields,
       is_form_row=False,
       extra_form=3,
   )

this is my view function @KenWhitesell