Repository navigation
fix: portal setting - #7796
fix: portal setting#7796
Conversation
| return self.one() | ||
|
|
||
|
|
||
| class PortalLoginSerializer(serializers.Serializer): |
There was a problem hiding this comment.
Here is the code review focusing on irregularities, potential issues, and optimization suggestions:
1. Potential Logic Issue: setattr for File Fields in auth_config Merge
Issue: The merging logic for auth_config uses {**(portal.auth_config or {}), **value}. If portal.auth_config contains keys that are file paths or objects (e.g., if auth_config stores file references), a shallow merge dictionary update might not deep-merge nested structures correctly. However, this is minor compared to the next point.
2. Irregularity: Unused/Redundant Logic & Readability
Suggestion:
In the loop:
if field == "auth_config":
if not license_valid or not isinstance(value, dict):
continue
value = {**(portal.auth_config or {}), **value}
setattr(portal, field, value)If field == "auth_config" but license_valid is False or value is not a dict, the continue skips the setattr. This is correct.
However, if field != "auth_config", it executes setattr(portal, field, value) immediately after the if block.
This structure is slightly hard to follow because setattr is conditional on not entering the auth_config block OR successfully passing the checks inside auth_config.
A clearer structure:
for field, value in instance.items():
if not hasattr(portal, field) or field in file_fields:
continue
if field == "auth_config":
if not license_valid or not isinstance(value, dict):
continue
value = {**(portal.auth_config or {}), **value}
setattr(portal, field, value)Verdict: Your current logic is functionally equivalent but slightly less readable than the above. It works, but separating the setattr makes the flow of control clearer.
3. Optimization: Set Initialization
Suggestion:
Change file_fields = ["logo", "id", "create_time", "update_time"] (which was changed to a set) is good.
Ensure file_fields is indeed a set. If it’s a tuple/list, membership testing is O(n). A set is O(1).
In the diff: file_fields = {"logo", "id", "create_time", "update_time"} is a set literal. Good optimization.
4. Potential Bug: instance.items() and File Uploads
Critical Check:
How are file uploads handled in instance?
Typically, Django ModelForms or Serializers handle files via request.FILES.
If instance is a dictionary representing request.DATA or a JSON payload, instance.get("logo") will likely return a string path, URL, or None, not a file object.
Your _handle_file_field(portal, field_name, instance.get(field_name)) expects a file-like object.
If instance is from a JSON API, you won't be able to upload a new logo unless your custom serializer/view correctly extracts the file from the multipart request and puts it into instance as an UploadedFile object before calling edit.
Recommendation: Verify that self._handle_file_field receives a valid file object. If this endpoint is JSON-only, logo updates will fail silently or raise an error when trying to save a string to the file field.
5. Optimization: Early Return / Guard Clauses
The current if portal is None: raise... is fine.
6. Consistency: Return Value
You replaced return PortalSerializer.Model(portal).data with return self.one().
- Pro: Reusing
self.one()ensures consistency with other methods (likeretrieve) to avoid duplicate serialization logic. - Con: Ensure
self.one()fetches the fresh instance from the DB (it should, since it's called afterportal.save()). Ifself.one()calls a separate query, it is safe. If it relies on cached state, it might be stale. Usually,.one()in such contexts re-fetches. Likely Good.
7. Security/Maintenance: License Check Caching
self._is_license_valid() is called once and stored in license_valid. Good practice. Avoid redundant license logic calls.
Refactored Suggestion (Improving Readability)
def edit(self, instance, with_valid=True):
portal = Portal.objects.first()
if portal is None:
raise AppApiException(500, _("Portal configuration does not exist"))
# Cache license validation result
license_valid = self._is_license_valid()
# Fields to ignore during standard attribute assignment
ignored_fields = {"logo", "id", "create_time", "update_time"}
for field, value in instance.items():
# Skip unknown fields or special file/ID/time fields
if not hasattr(portal, field) or field in ignored_fields:
continue
# Special handling for auth_config
if field == "auth_config":
# Only allow updating auth_config if license is valid and input is a dict
if not license_valid or not isinstance(value, dict):
continue
# Deep merge strategy: existing config updated by new data
value = {**(portal.auth_config or {}), **value}
setattr(portal, field, value)
# Handle Logo upload separately (FileField logic)
if "logo" in instance:
if license_valid:
# Ensure _handle_file_field receives a proper file object
file_obj = instance.get("logo")
self._handle_file_field(portal, "logo", file_obj)
# else: Ignore logo update if license invalid
# Optional: Log that logo change was ignored due to invalid license
portal.save()
return self.one()Summary of Findings
- Logic: Mostly sound. The control flow for
auth_configis correct but slightly dense; refactoring improves readability. - Performance: Using a
setforignored_fieldsis a good optimization. - Risk: Crucial: Verify how file uploads reach
instance["logo"]. If this is a JSON API,instance.get("logo")is likely invalid for_handle_file_field. Ensure your middleware/serializer maps multipart form data to the dictionary correctly. - Consistency: Returning
self.one()is better for DRY principles than manual serialization.
fix: portal setting