Skip to content

feat: use extended profile model in account settings#37119

Open
BryanttV wants to merge 5 commits intoopenedx:masterfrom
eduNEXT:bav/use-extended-profile-model-in-account-settings
Open

feat: use extended profile model in account settings#37119
BryanttV wants to merge 5 commits intoopenedx:masterfrom
eduNEXT:bav/use-extended-profile-model-in-account-settings

Conversation

@BryanttV
Copy link
Copy Markdown
Contributor

@BryanttV BryanttV commented Aug 4, 2025

Description

This PR enhances the extended profile fields functionality, enabling the use of custom extended profile models in account settings with improved consistency, atomicity, and error handling.

Currently, when we have extended profile fields associated with the REGISTRATION_EXTENSION_FORM setting, it works in this way:

  1. During Registration: Additional custom fields are stored in both the meta field of the UserProfile model AND in the custom model associated with the form. See the registration code.
  2. In Account Settings (before this PR): Custom fields are only fetched from and stored in the meta field of the UserProfile model, ignoring the custom model entirely.

What This PR Changes

This PR introduces several improvements:

  1. New Setting PROFILE_EXTENSION_FORM: Introduces a new Django setting PROFILE_EXTENSION_FORM that supersedes the deprecated REGISTRATION_EXTENSION_FORM. The new setting enables:

    • More accurate naming (profile extension vs registration extension)
    • Enhanced capabilities for managing extended profile fields
    • Data storage in dedicated model (not just UserProfile.meta)
    • Ability to update extended profile fields via Account Settings API
      Migration Path: Sites currently using REGISTRATION_EXTENSION_FORM (deprecated) will continue working with the old behavior (data stored in UserProfile.meta, no Account Settings updates). To get the new capabilities, migrate to PROFILE_EXTENSION_FORM.
  2. Dual Storage: Extended profile fields are now stored in both the custom model (when configured) and the meta field, maintaining parity with the registration process.

  3. Atomic Updates: Both meta and custom model updates are performed within a single database transaction. If either operation fails, both are rolled back to prevent partial/inconsistent saves.

  4. Consistent Reading: Extended profile data is read from the custom model when PROFILE_EXTENSION_FORM is configured with a model, falling back to meta only when no model is configured.

Important Behavior Notes

  • Migration Consideration: When PROFILE_EXTENSION_FORM is configured with a model, extended profile data is read only from that model. If a user has existing data in meta but no corresponding model record, their extended profile fields will appear empty until their profile is updated (which will then create the model record and populate it from meta).

  • Validation: When a form is configured, all extended profile updates go through that form's validation logic, including any model-level validators.

Related Issues

Testing instructions

  1. Create a Tutor environment.

  2. Create a mount of edx-platform with the changes in this PR.

    tutor mounts add edx-platform
  3. Add the following property to the LMS Django Admin > Site Configurations > local.openedx.io:8000

    {
        "extended_profile_fields": [
            "nickname",
            "interests",
            "wants_newsletter",
            "favorite_language"
        ]
    }
  4. Install this app that includes the custom extra fields mentioned above: https://github.com/bryanttv/custom-extra-fields. You can use this setting in your config.yml

    OPENEDX_EXTRA_PIP_REQUIREMENTS:
    - git+https://github.com/bryanttv/custom-extra-fields.git
  5. Create a tutor inline plugin and enable it with tutor plugins enable custom-settings

    # custom-settings.yml
    name: custom-settings
    version: 1.0.0
    patches:
      openedx-common-settings: |
        PROFILE_EXTENSION_FORM = "custom_extra_fields.forms.CustomExtraFieldsForm"
  6. Run tutor dev launch

  7. Create a user in the platform.

  8. Go to {lms_domain}/api/user/v1/accounts/{username} in your browser. Be sure to use the application/merge-patch+json Media type in the DRF UI.

Test Cases

1. No Form (PROFILE_EXTENSION_FORM = None) and no extended profile fields

The response includes the extended_profile key as an empty list. This list cannot be updated because no fields are defined in the extended_profile_fields setting, and there is no extended form either.

{
    "extended_profile": []
}

2. No Form (PROFILE_EXTENSION_FORM = None) and with extended profile fields

The fields defined in the extended_profile_fields setting are returned and can be updated. However, these fields do not have any validation, meaning any value can be assigned to them. Data is stored in the UserProfile.meta JSON field.

{
    "extended_profile": [
        {
            "field_name": "nickname",
            "field_value": "any_value_allowed"
        },
        {
            "field_name": "interests",
            "field_value": "Programming"
        },
        {
            "field_name": "wants_newsletter",
            "field_value": "true"
        },
        {
            "field_name": "favorite_language",
            "field_value": "spanish"
        }
    ]
}

3. With Form (PROFILE_EXTENSION_FORM = "myapp.forms.ExtendedForm") and no extended profile fields

Fields defined in the form will be displayed in the legacy registration form if they are marked as required. However, if the extended_profile_fields property in the Site Configuration is not defined, no fields will be returned from the endpoint.

Still, the defined fields can be updated and will be validated according to the logic in the form. Updates are transactional: both meta and the custom model (if configured) are updated atomically, or both fail together.

  • If the form is associated with a model, additional validations from the model will be executed, and its fields will be updated as well. Data for the extended profile fields will also be extracted from that model.
{
    "extended_profile": []
}

4. With Form (PROFILE_EXTENSION_FORM = "myapp.forms.ExtendedForm") and with extended profile fields

When both settings are defined, the fields listed in extended_profile_fields are returned, each with its corresponding validation as defined in the form.

  • If the form is associated with a model, additional validations from that model will be applied, and its fields will be updated accordingly. Data for the extended profile fields will be sourced from the model (not from meta).
  • Updates are atomic: if validation fails or the save operation errors, no partial data is persisted.
{
    "extended_profile": [
        {
            "field_name": "nickname",
            "field_value": "codewanderer"
        },
        {
            "field_name": "interests",
            "field_value": "Programming"
        },
        {
            "field_name": "wants_newsletter",
            "field_value": "true"
        },
        {
            "field_name": "favorite_language",
            "field_value": "python"
        }
    ]
}

5. Testing Atomic Rollback (Error Handling)

To verify that failed updates don't leave partial data:

  1. Configure a custom form with validation that can fail (e.g., a field with choices)
  2. Attempt to update extended profile fields with invalid data
  3. Verify that an AccountUpdateError is raised
  4. Verify that neither the meta field nor the custom model was updated (rollback worked)

Deadline

None

Demo

extended-profile-fields-in-account-settings.mp4

@openedx-webhooks openedx-webhooks added the open-source-contribution PR author is not from Axim or 2U label Aug 4, 2025
@openedx-webhooks
Copy link
Copy Markdown

openedx-webhooks commented Aug 4, 2025

Thanks for the pull request, @BryanttV!

This repository is currently maintained by @openedx/wg-maintenance-openedx-platform.

Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review.

🔘 Get product approval

If you haven't already, check this list to see if your contribution needs to go through the product review process.

  • If it does, you'll need to submit a product proposal for your contribution, and have it reviewed by the Product Working Group.
    • This process (including the steps you'll need to take) is documented here.
  • If it doesn't, simply proceed with the next step.
🔘 Provide context

To help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:

  • Dependencies

    This PR must be merged before / after / at the same time as ...

  • Blockers

    This PR is waiting for OEP-1234 to be accepted.

  • Timeline information

    This PR must be merged by XX date because ...

  • Partner information

    This is for a course on edx.org.

  • Supporting documentation
  • Relevant Open edX discussion forum threads
🔘 Get a green build

If one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green.

Details
Where can I find more information?

If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources:

When can I expect my changes to be merged?

Our goal is to get community contributions seen and reviewed as efficiently as possible.

However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:

  • The size and impact of the changes that it introduces
  • The need for product review
  • Maintenance status of the parent repository

💡 As a result it may take up to several weeks or months to complete a review and merge your PR.

@openedx-webhooks
Copy link
Copy Markdown

Thanks for the pull request, @BryanttV!

This repository is currently maintained by @openedx/wg-maintenance-edx-platform.

Once you've gone through the following steps feel free to tag them in a comment and let them know that your changes are ready for engineering review.

🔘 Get product approval

If you haven't already, check this list to see if your contribution needs to go through the product review process.

  • If it does, you'll need to submit a product proposal for your contribution, and have it reviewed by the Product Working Group.
    • This process (including the steps you'll need to take) is documented here.
  • If it doesn't, simply proceed with the next step.
🔘 Provide context

To help your reviewers and other members of the community understand the purpose and larger context of your changes, feel free to add as much of the following information to the PR description as you can:

  • Dependencies

    This PR must be merged before / after / at the same time as ...

  • Blockers

    This PR is waiting for OEP-1234 to be accepted.

  • Timeline information

    This PR must be merged by XX date because ...

  • Partner information

    This is for a course on edx.org.

  • Supporting documentation
  • Relevant Open edX discussion forum threads
🔘 Get a green build

If one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green.

🔘 Update the status of your PR

Your PR is currently marked as a draft. After completing the steps above, update its status by clicking "Ready for Review", or removing "WIP" from the title, as appropriate.


Where can I find more information?

If you'd like to get more details on all aspects of the review process for open source pull requests (OSPRs), check out the following resources:

When can I expect my changes to be merged?

Our goal is to get community contributions seen and reviewed as efficiently as possible.

However, the amount of time that it takes to review and merge a PR can vary significantly based on factors such as:

  • The size and impact of the changes that it introduces
  • The need for product review
  • Maintenance status of the parent repository

💡 As a result it may take up to several weeks or months to complete a review and merge your PR.

@github-project-automation github-project-automation Bot moved this to Needs Triage in Contributions Aug 4, 2025
@BryanttV BryanttV force-pushed the bav/use-extended-profile-model-in-account-settings branch 2 times, most recently from 12561c1 to dc1cee2 Compare August 5, 2025 17:35
@BryanttV BryanttV marked this pull request as ready for review August 5, 2025 18:10
@BryanttV BryanttV requested review from a team as code owners August 5, 2025 18:10
@BryanttV BryanttV requested a review from mariajgrimaldi August 5, 2025 18:11
@mphilbrick211 mphilbrick211 moved this from Needs Triage to Ready for Review in Contributions Aug 5, 2025
@BryanttV BryanttV force-pushed the bav/use-extended-profile-model-in-account-settings branch from dc1cee2 to 04cdfb7 Compare August 6, 2025 22:53
@BryanttV BryanttV force-pushed the bav/use-extended-profile-model-in-account-settings branch 2 times, most recently from dae3705 to 065faee Compare August 11, 2025 17:48
@efortish efortish self-requested a review September 5, 2025 18:38
Copy link
Copy Markdown
Contributor

@efortish efortish left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you so much for the contribution @BryanttV
Everything looks good, I followed your instructions step by step and it went great!

Can we add some edge-case tests suit for the new features in api.py and registration_form.py?

@mphilbrick211 mphilbrick211 moved this from Ready for Review to In Eng Review in Contributions Sep 16, 2025
@BryanttV BryanttV force-pushed the bav/use-extended-profile-model-in-account-settings branch from 065faee to 06c8680 Compare September 30, 2025 01:17
@openedx-webhooks openedx-webhooks added the core contributor PR author is a Core Contributor (who may or may not have write access to this repo). label Sep 30, 2025
@BryanttV
Copy link
Copy Markdown
Contributor Author

Thanks for the review, @efortish! I will be working on the unit tests for this functionality.

Comment thread openedx/core/djangoapps/user_api/accounts/api.py Outdated
Comment thread openedx/core/djangoapps/user_api/accounts/api.py Outdated
Comment thread openedx/core/djangoapps/user_api/accounts/api.py Outdated
Comment thread openedx/core/djangoapps/user_api/accounts/api.py Outdated
Comment on lines +602 to +615
def get_extended_profile_data():
extended_profile_model = get_extended_profile_model()

# pick the keys from the site configuration
extended_profile_field_names = configuration_helpers.get_value('extended_profile_fields', [])
if extended_profile_model:
try:
profile_obj = extended_profile_model.objects.get(user=user_profile.user)
return model_to_dict(profile_obj)
except (AttributeError, extended_profile_model.DoesNotExist):
return {}

try:
extended_profile_fields_data = json.loads(user_profile.meta)
except ValueError:
extended_profile_fields_data = {}
try:
return json.loads(user_profile.meta or "{}")
except (ValueError, TypeError, AttributeError):
return {}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This section made me question something: is the profile meta and the extended profile model out of date at some point? Should they be in sync?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every time we update the extended profile fields, they will be updated in both the model and the meta, so they should always be synchronized.

Comment thread openedx/core/djangoapps/user_api/accounts/api.py Outdated
@BryanttV BryanttV force-pushed the bav/use-extended-profile-model-in-account-settings branch from 06c8680 to 4e25200 Compare November 11, 2025 23:10
@BryanttV BryanttV force-pushed the bav/use-extended-profile-model-in-account-settings branch 2 times, most recently from f234f96 to b6b08c9 Compare November 21, 2025 17:43
@BryanttV BryanttV marked this pull request as draft November 21, 2025 20:26
@BryanttV BryanttV force-pushed the bav/use-extended-profile-model-in-account-settings branch from f15cab3 to 750e00a Compare November 24, 2025 18:46
@BryanttV
Copy link
Copy Markdown
Contributor Author

BryanttV commented Mar 4, 2026

Hi @feanil, thanks for your additional comments!

I updated the implementation to include the new PROFILE_EXTENSION_FORM setting, and I addressed your latest comments. Could you take another look? Thanks!

I also marked the REGISTRATION_EXTENSION_FORM setting as deprecated. Do you think we should go ahead and open the DEPR ticket now?

@BryanttV BryanttV requested a review from feanil March 4, 2026 22:05
@feanil
Copy link
Copy Markdown
Contributor

feanil commented Mar 12, 2026

@BryanttV sorry for the slow response here, I'm hoping to have time tomorrow to review this PR.

Comment thread openedx/core/djangoapps/user_api/accounts/api.py Outdated
Comment thread openedx/core/djangoapps/user_api/accounts/api.py Outdated
Comment thread openedx/core/djangoapps/user_api/accounts/forms.py
Comment thread openedx/core/djangoapps/user_api/accounts/tests/test_forms.py
Comment thread openedx/core/djangoapps/user_authn/views/registration_form.py Outdated
@BryanttV BryanttV force-pushed the bav/use-extended-profile-model-in-account-settings branch 3 times, most recently from d81e92f to 3a806cf Compare March 30, 2026 22:18
@BryanttV
Copy link
Copy Markdown
Contributor Author

Hii @feanil, I've made the latest changes based on your suggestions. Could you check again, please? Thank you very much!

@BryanttV BryanttV requested a review from feanil March 30, 2026 23:01
Copy link
Copy Markdown
Contributor

@feanil feanil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more bug but hopefully this is the last thing and then we can land this. Can you add a test case to validate this bug and then fix it.

Comment thread openedx/core/djangoapps/user_api/accounts/views.py Outdated
@BryanttV BryanttV force-pushed the bav/use-extended-profile-model-in-account-settings branch 4 times, most recently from 1cd80c1 to 84e3918 Compare April 6, 2026 19:14
@BryanttV
Copy link
Copy Markdown
Contributor Author

BryanttV commented Apr 6, 2026

Hi @feanil, bug fixed. Thanks!

@BryanttV BryanttV requested a review from feanil April 6, 2026 19:38
Copy link
Copy Markdown
Contributor

@feanil feanil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found one more bug but I think once that's fixed this is ready to merge! Please make sure to link to the DEPR ticket in the PR description as well, I didn't see it in there. We'll want references in both directions from that DEPR so that we can drop the old config once we cut Verawood.

Also you should add a note with a link to this PR and the DEPR to the Verawood release notes. Since Verawood will be the last release where the old config will work. (Please come back and delete the old setting and related code once Verawood is cut.)

_("The extended profile information could not be saved due to a system error."),
),
}
dev_msg, user_msg = error_messages[type(exc)]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This lookup will fail if the type of the exc is a subtype of one of the named exceptions above which can happen especially with DatabaseError so instead just separate this into 3 different except clauses. The except will match with subclasses and so you don't need to do this lookup yourself.

Something like

except ValidationError as exc:
          raise AccountUpdateError(
              developer_message=f"Extended profile validation failed: {str(exc)}",
              user_message=_("The extended profile information could not be saved due to validation
  errors."),
          ) from exc
      except IntegrityError as exc:
          raise AccountUpdateError(
              developer_message=f"Extended profile integrity error: {str(exc)}",
              user_message=_("The extended profile information could not be saved. Please check for 
  duplicate values."),                                                                                  
          ) from exc
      except DatabaseError as exc:                                                                      
          raise AccountUpdateError(                                                                   
              developer_message=f"Database error saving extended profile: {str(exc)}",
              user_message=_("The extended profile information could not be saved due to a system       
  error."),
          ) from exc   

This may feel less DRY but it is less error prone and less complex while not being much longer in terms of code length.

@feanil
Copy link
Copy Markdown
Contributor

feanil commented Apr 17, 2026

@BryanttV checking back in on this, no rush but just ping me when you're ready for me to take another look/merge.

I think other than the one code change, don't forget to:

  1. Link to the DEPR ticket in the PR description (with cross-references in both directions from the DEPR)
  2. Note in the Verawood release notes linking to this PR and the DEPR
  3. And if we can land it before next Thursday, don't forget to put up the delete REGISTRATION_EXTENSION_FORM and related code PR once Verawood is cut

@BryanttV BryanttV force-pushed the bav/use-extended-profile-model-in-account-settings branch 3 times, most recently from bb177a9 to c059910 Compare April 21, 2026 21:44
- Integrate extended profile model into account settings flow
- Improve validation and error handling
- Refactor form handling and API separation
- Ensure atomic updates for profile changes
- Support PROFILE_EXTENSION_FORM setting
- Add and update comprehensive unit tests
- Improve documentation and code clarity
@BryanttV BryanttV force-pushed the bav/use-extended-profile-model-in-account-settings branch from c059910 to 5811763 Compare April 22, 2026 13:05
@BryanttV
Copy link
Copy Markdown
Contributor Author

Hi @feanil! Thanks a lot for following up.

I’ve made the last change you requested and created the DEPR ticket. The links are now referenced both in this PR and in the DEPR ticket.

I’ve never created release notes before, could you let me know where I should add them and if there’s a specific format I should follow? Thanks again!

I’ll also be working on the PR to remove the setting and will notify you when it’s ready.

@BryanttV BryanttV requested a review from feanil April 22, 2026 13:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core contributor PR author is a Core Contributor (who may or may not have write access to this repo). open-source-contribution PR author is not from Axim or 2U

Projects

Status: In Eng Review

Development

Successfully merging this pull request may close these issues.

7 participants