feat: use extended profile model in account settings#37119
feat: use extended profile model in account settings#37119BryanttV wants to merge 5 commits intoopenedx:masterfrom
Conversation
|
Thanks for the pull request, @BryanttV! This repository is currently maintained by 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 approvalIf you haven't already, check this list to see if your contribution needs to go through the product review process.
🔘 Provide contextTo 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:
🔘 Get a green buildIf one or more checks are failing, continue working on your changes until this is no longer the case and your build turns green. DetailsWhere 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:
💡 As a result it may take up to several weeks or months to complete a review and merge your PR. |
|
Thanks for the pull request, @BryanttV! This repository is currently maintained by 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 approvalIf you haven't already, check this list to see if your contribution needs to go through the product review process.
🔘 Provide contextTo 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:
🔘 Get a green buildIf 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 PRYour 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:
💡 As a result it may take up to several weeks or months to complete a review and merge your PR. |
12561c1 to
dc1cee2
Compare
dc1cee2 to
04cdfb7
Compare
dae3705 to
065faee
Compare
efortish
left a comment
There was a problem hiding this comment.
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?
065faee to
06c8680
Compare
|
Thanks for the review, @efortish! I will be working on the unit tests for this functionality. |
| 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 {} |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
06c8680 to
4e25200
Compare
f234f96 to
b6b08c9
Compare
f15cab3 to
750e00a
Compare
|
Hi @feanil, thanks for your additional comments! I updated the implementation to include the new I also marked the |
|
@BryanttV sorry for the slow response here, I'm hoping to have time tomorrow to review this PR. |
d81e92f to
3a806cf
Compare
|
Hii @feanil, I've made the latest changes based on your suggestions. Could you check again, please? Thank you very much! |
feanil
left a comment
There was a problem hiding this comment.
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.
1cd80c1 to
84e3918
Compare
|
Hi @feanil, bug fixed. Thanks! |
feanil
left a comment
There was a problem hiding this comment.
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)] |
There was a problem hiding this comment.
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.
|
@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:
|
bb177a9 to
c059910
Compare
- 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
c059910 to
5811763
Compare
|
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. |
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_FORMsetting, it works in this way:metafield of theUserProfilemodel AND in the custom model associated with the form. See the registration code.metafield of theUserProfilemodel, ignoring the custom model entirely.What This PR Changes
This PR introduces several improvements:
New Setting
PROFILE_EXTENSION_FORM: Introduces a new Django settingPROFILE_EXTENSION_FORMthat supersedes the deprecatedREGISTRATION_EXTENSION_FORM. The new setting enables: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 toPROFILE_EXTENSION_FORM.Dual Storage: Extended profile fields are now stored in both the custom model (when configured) and the
metafield, maintaining parity with the registration process.Atomic Updates: Both
metaand custom model updates are performed within a single database transaction. If either operation fails, both are rolled back to prevent partial/inconsistent saves.Consistent Reading: Extended profile data is read from the custom model when
PROFILE_EXTENSION_FORMis configured with a model, falling back tometaonly when no model is configured.Important Behavior Notes
Migration Consideration: When
PROFILE_EXTENSION_FORMis configured with a model, extended profile data is read only from that model. If a user has existing data inmetabut 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 frommeta).Validation: When a form is configured, all extended profile updates go through that form's validation logic, including any model-level validators.
Related Issues
REGISTRATION_EXTENSION_FORMdjango setting #38409Testing instructions
Create a Tutor environment.
Create a mount of edx-platform with the changes in this PR.
Add the following property to the LMS Django Admin > Site Configurations >
local.openedx.io:8000{ "extended_profile_fields": [ "nickname", "interests", "wants_newsletter", "favorite_language" ] }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.ymlCreate a tutor inline plugin and enable it with
tutor plugins enable custom-settingsRun
tutor dev launchCreate a user in the platform.
Go to
{lms_domain}/api/user/v1/accounts/{username}in your browser. Be sure to use theapplication/merge-patch+jsonMedia type in the DRF UI.Test Cases
1. No Form (
PROFILE_EXTENSION_FORM = None) and no extended profile fieldsThe response includes the
extended_profilekey as an empty list. This list cannot be updated because no fields are defined in theextended_profile_fieldssetting, and there is no extended form either.{ "extended_profile": [] }2. No Form (
PROFILE_EXTENSION_FORM = None) and with extended profile fieldsThe fields defined in the
extended_profile_fieldssetting 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 theUserProfile.metaJSON 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 fieldsFields defined in the form will be displayed in the legacy registration form if they are marked as required. However, if the
extended_profile_fieldsproperty 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
metaand the custom model (if configured) are updated atomically, or both fail together.{ "extended_profile": [] }4. With Form (
PROFILE_EXTENSION_FORM = "myapp.forms.ExtendedForm") and with extended profile fieldsWhen both settings are defined, the fields listed in
extended_profile_fieldsare returned, each with its corresponding validation as defined in the form.meta).{ "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:
AccountUpdateErroris raisedmetafield nor the custom model was updated (rollback worked)Deadline
None
Demo
extended-profile-fields-in-account-settings.mp4