Skip to content

Redesigned form import - #6509

Open
robinmolen wants to merge 19 commits into
feature/6429-form-import/export-export-optionsfrom
feature/6430-redesigned-form-import-with-options
Open

Redesigned form import#6509
robinmolen wants to merge 19 commits into
feature/6429-form-import/export-export-optionsfrom
feature/6430-redesigned-form-import-with-options

Conversation

@robinmolen

@robinmolen robinmolen commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Closes #6430, #6431

Changes

This PR replaces the old form import proces with a completely new implementation. This includes easy ways to handle common import errors (through import options), better (actual working) form definition comparisons and a better developer experience.

Changes to import proces

Most logic of the import proces has been moved to dedicated functions and processes. The object specific actions (setting the form active state, removing stale references, etc.) have been moved to dedicated ImportSerializers. The extra actions done for form version restoring have been moved out of the main import proces, to keep that code focused on one purpose. Finally, the iterative proces has been changed into a flat sequential proces.

The import proces still uses the V2 serializers, as the V3 serializers aren't feature complete just yet. Due to the cleanup the switch to the V3 serializers should be easier in the future.

Comparing form definitions

When comparing form definitions we now only compare values that are stable. The UUID of the form definition itself is ignored as these will most likely be different. We also ignore the component id's, as these change every time the form is saved.

Links to unknown domains

The links used in e-mail templates must be present in the allowlist (in the global configuration). In the current import proces unknown links caused import errors, which the user would have had to solve manually by editing the import files.
The new import proces strips these links away, to prevent annoying import errors. This prevents frustration for form builders and takes away some of the support load.

There are future plans to make this a bit nicer (control over which links are removed and which are accepted), but for now we have to go for the safest option.

Form version restore

The form history / version restore still makes use of the import/export proces. With the changes brought by the export rework, we can now more fully restore the form to its previous configuration.

When creating a form version we try to include all pieces of the configuration, this includes all additional form configuration (product, WMS- and WMTS-tile layers and Yivi attribute groups). When restoring the form we also restore these additional objects. This means that when a form is restored to a previous version, the product used by the restored form is the exact product that was previously used (with the exact same name, price and information).

Checklist

Check off the items that are completed or not relevant.

  • Impact on features

    • Checked copying a form
    • Checked import/export of a form
    • Config checks in the configuration overview admin page
    • Checked new model fields are usable in the admin
    • Problem detection in the admin email digest is handled
  • Dockerfile/scripts

    • Updated the Dockerfile with the necessary scripts from the ./bin folder
  • Commit hygiene

    • Commit messages refer to the relevant Github issue
    • Commit messages explain the "why" of change, not the how
  • Documentation

    • Added documentation which describes the changes

@robinmolen robinmolen linked an issue Aug 1, 2026 that may be closed by this pull request
@robinmolen robinmolen changed the title Feature/6430 redesigned form import with options Redesigned form import Aug 1, 2026
@robinmolen robinmolen mentioned this pull request Aug 1, 2026
9 tasks
@robinmolen robinmolen linked an issue Aug 1, 2026 that may be closed by this pull request
@robinmolen
robinmolen force-pushed the feature/6430-redesigned-form-import-with-options branch from 53766b7 to d59bb8a Compare August 1, 2026 15:33
@codecov

codecov Bot commented Aug 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.42962% with 25 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.21%. Comparing base (346913b) to head (782ea55).

Files with missing lines Patch % Lines
src/openforms/forms/import_export/import_form.py 92.09% 9 Missing and 5 partials ⚠️
src/openforms/forms/import_export/utils.py 76.92% 3 Missing and 3 partials ⚠️
.../openforms/forms/import_export/serializers/form.py 97.43% 0 Missing and 2 partials ⚠️
...orms/forms/import_export/serializers/form_logic.py 91.30% 1 Missing and 1 partial ⚠️
...forms/forms/import_export/serializers/form_step.py 85.71% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@                                Coverage Diff                                 @@
##           feature/6429-form-import/export-export-options    #6509      +/-   ##
==================================================================================
- Coverage                                           97.24%   97.21%   -0.04%     
==================================================================================
  Files                                                 878      879       +1     
  Lines                                               33445    33716     +271     
  Branches                                             3035     3064      +29     
==================================================================================
+ Hits                                                32525    32777     +252     
- Misses                                                608      617       +9     
- Partials                                              312      322      +10     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@robinmolen
robinmolen force-pushed the feature/6430-redesigned-form-import-with-options branch from d59bb8a to c507a57 Compare August 1, 2026 16:38
@robinmolen
robinmolen force-pushed the feature/6430-redesigned-form-import-with-options branch from c507a57 to 4d24e3b Compare August 2, 2026 13:54
Comment thread src/openforms/forms/import_export/matchers/form_definition.py Outdated
Comment thread src/openforms/forms/import_export/matchers/form_definition.py Outdated
Comment thread src/openforms/forms/import_export/utils.py Outdated
Comment thread src/openforms/forms/import_export/import_form.py
Comment thread src/openforms/forms/import_export/serializers/form.py
@robinmolen
robinmolen force-pushed the feature/6430-redesigned-form-import-with-options branch from 4d24e3b to 13065d8 Compare August 7, 2026 07:55
Passing the form import options from the admin view back to the import_form_data method.
…ring import

When importing, we automatically remove all links with domains that are not present in the allowlist.

The current implemention of the import flow, where we don't know which form is being uploaded and what unknown domains are present, we have to take a direct approach. When we can provide the user an overview about which unknown domains are present, then an informed "accept this domain" import option would be nice. For now, as we cannot give any indication about the form content, we cannot safely accept any domain.

To prevent future load for the technical support, we automatically remove unknown links from the email content.
… module

The serializer-specific import logic has been moved to the respective import serializers. Those now deal with things like applying backwards compatibility code, creating additional data after persisting data, updating uuid's, etc.
When importing additional form configuration (product, WMS- and WMTS-tile layers and Yivi attribute groups) we perform shallow and deep comparisons.

The shallow comparisons are done for the quick "importing a form into the environment where the importdata was made" situations. This shallow comparison looks of the existing object using the UUID from the importdata.

The deep comparison is for cross-environment situations, using the object data when searching for an already existing variant. This is used when importing a form that was made in another Open Forms environment.

When an existing instance is found, the UUID's in the form data are updated to match with the UUID of the existing instance. The existing instance will remain as it is, the import data won't update the existing configuration.

In case neither comparison returns an existing instance, we create a new object for the imported data.

When restoring a previous form version, we skip the identifier comparison. This could mean that we create a new object with an identifier that is already in use (for example, when a product price has been changed over time. In our form version data the product has the same UUID as the existing product but different configuration, so the deep comparison doesn't find a match. We will then create a new product object with the old configuration). Using resource specific `generate_identifier` methods we generate new identifiers, to ensure that the new object has a unique identifier.
…estore functionality

When restoring a previous form version, we should try to restore as much as possible back to its previous state. When restoring additional form configuration, we force a deep comparison.
To pass the theme and category uuid's to the import options, we need to extract them from the old form configuration before importing the form. This ensures we can keep the logic within the import/export module simplistic and straight-forward.
…nality

The form version restore now save the resources used at the time of creating the form version. This ensures that when restoring a form version, the result is the exact form with the exact resources as before.

Added tests to validate complete saving and restoring, and how form resources will be re-used
To prevent creating duplicate form definitions when importing forms, we now perform deep configuration comparisons.
The FormDefinitionMatcher initiates a map of formDefinition hashes and the represented form definition instance. By comparing the imported form definitions against this map we can easily find form definitions with similar configuration, which can be re-used.

The FormDefinitionMatcher only handles reusable form definitions, and creates hashes using the `configuration`. When creating the hash the `configuration` is normalized by removing the component `id` attributes (these are different after each form save, so will never match completely)
…port_export module

Moved the compatibility handling code from the form serializers to their respective ImportSerializers.

There are a couple more compatibility handling actions in the registration module. These have been left there, as the import_export module will just lessen the context needed to understand those situations.
@sergei-maertens
sergei-maertens force-pushed the feature/6430-redesigned-form-import-with-options branch from 13065d8 to 782ea55 Compare August 7, 2026 08:36
@sergei-maertens

Copy link
Copy Markdown
Member

Here too - just rebased it for reviewing purposes

@robinmolen
robinmolen marked this pull request as ready for review August 7, 2026 10:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fully restoring previous form versions Import options

2 participants