-
Notifications
You must be signed in to change notification settings - Fork 86
feat(profile):added editable profile with projects,experience $ bio #2561
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds editable profile functionality by introducing three new fields to the User model: bio, projects, and experience. These fields allow users to add biographical information and structured data about their projects and work experience.
Key Changes
- Added
bio,projects, andexperiencefields to the User model with JSONField types for structured data - Created a new
UserProfileViewAPI endpoint with GET and PATCH methods to retrieve and update profile information - Added URL routing for the new profile endpoint
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 10 comments.
| File | Description |
|---|---|
| db/user.py | Adds three new optional fields to User model: bio (CharField), projects (JSONField), and experience (JSONField) |
| api/dashboard/urls.py | Imports UserProfileView and adds new profile endpoint route |
| api/dashboard/profile_views.py | New API view implementing GET and PATCH endpoints for user profile with bio, projects, and experience fields |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| path("coupon/", include("api.dashboard.coupon.urls")), | ||
| path("projects/", include("api.dashboard.projects.urls")), | ||
| path("achievement/", include("api.dashboard.achievement.urls")), | ||
| path('profile/user-profile/',UserProfileView.as_view()), |
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing space after comma in the path definition. Should be path('profile/user-profile/', UserProfileView.as_view()) for consistency with Python style conventions.
| path('profile/user-profile/',UserProfileView.as_view()), | |
| path('profile/user-profile/', UserProfileView.as_view()), |
| from db.user import User | ||
|
|
||
| class UserProfileView(APIView): | ||
| permission_classes = [IsAuthenticated] |
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This view uses permission_classes = [IsAuthenticated] which is inconsistent with the rest of the codebase. All other dashboard views use authentication_classes = [CustomizePermission] for JWT-based authentication (see examples in api/dashboard/profile/profile_view.py, api/dashboard/projects/projects_view.py, etc.). The IsAuthenticated permission class expects Django's default authentication, which doesn't align with the JWT authentication pattern used throughout this project. Change this to use authentication_classes = [CustomizePermission] instead, and use JWTUtils.fetch_user_id(request) to get the user.
| def patch(self, request): | ||
| user = request.user | ||
| data = request.data | ||
|
|
||
| if 'bio' in data: | ||
| user.bio = data['bio'] | ||
| if 'projects' in data: | ||
| user.projects = data['projects'] | ||
| if 'experience' in data: | ||
| user.experience = data['experience'] |
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PATCH endpoint lacks input validation and error handling. The code accepts any data from request.data without validating the structure or content of bio, projects, and experience fields. This could allow:
- Bio strings exceeding the 2000 character limit (which would cause a database error)
- Invalid JSON structures for projects/experience that don't match expected schemas
- Injection of malicious data
Following the pattern in api/dashboard/profile/profile_view.py:UserProfileEditView (lines 66-81), you should use a serializer for validation. Create a serializer that validates max_length for bio and validates the structure of projects/experience JSONFields before saving.
| if 'experience' in data: | ||
| user.experience = data['experience'] | ||
|
|
||
| user.save() |
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing error handling for the user.save() operation. If the save fails (e.g., database connection issues, constraint violations, or bio exceeding max_length), the endpoint will return a 500 error instead of a proper error response. The codebase pattern (see api/dashboard/projects/projects_view.py lines 42-53) uses try-except blocks or serializer validation to handle such errors gracefully and return CustomResponse(...).get_failure_response() with appropriate error messages.
| projects = models.JSONField(default=list, blank=True, null=True) | ||
| experience = models.JSONField(default=list, blank=True, null=True) |
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using a mutable default value (list) directly in JSONField can lead to unexpected behavior where all instances share the same list object. While Django's JSONField handles this differently than regular Python defaults, it's safer and more explicit to use a callable. Consider changing default=list to default=list (which Django serializes properly), or use a lambda function default=lambda: [] to be more explicit about creating a new list for each instance.
| projects = models.JSONField(default=list, blank=True, null=True) | |
| experience = models.JSONField(default=list, blank=True, null=True) | |
| projects = models.JSONField(default=lambda: [], blank=True, null=True) | |
| experience = models.JSONField(default=lambda: [], blank=True, null=True) |
| @@ -1,5 +1,5 @@ | |||
| from django.urls import path, include | |||
|
|
|||
| from.profile_views import UserProfileView | |||
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing space after 'from' keyword. The import should be from .profile_views import UserProfileView instead of from.profile_views import UserProfileView.
| from.profile_views import UserProfileView | |
| from .profile_views import UserProfileView |
|
|
||
| class UserProfileView(APIView): | ||
| permission_classes = [IsAuthenticated] | ||
|
|
||
| def get(self, request): | ||
| user = request.user | ||
| return Response({ | ||
| "bio": user.bio, | ||
| "projects": user.projects, | ||
| "experience": user.experience | ||
| }) |
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The GET endpoint doesn't follow the codebase's standard response pattern. Other views in this codebase (e.g., api/dashboard/profile/profile_view.py:UserProfileEditView, api/dashboard/projects/projects_view.py) use CustomResponse for consistent API responses with proper error handling structure. Consider refactoring to return CustomResponse(response={...}).get_success_response() instead of a plain Response object.
| class UserProfileView(APIView): | |
| permission_classes = [IsAuthenticated] | |
| def get(self, request): | |
| user = request.user | |
| return Response({ | |
| "bio": user.bio, | |
| "projects": user.projects, | |
| "experience": user.experience | |
| }) | |
| from utils.custom_response import CustomResponse | |
| class UserProfileView(APIView): | |
| permission_classes = [IsAuthenticated] | |
| def get(self, request): | |
| user = request.user | |
| response_data = { | |
| "bio": user.bio, | |
| "projects": user.projects, | |
| "experience": user.experience, | |
| } | |
| return CustomResponse(response=response_data).get_success_response() |
|
|
||
| class UserProfileView(APIView): | ||
| permission_classes = [IsAuthenticated] | ||
|
|
||
| def get(self, request): | ||
| user = request.user | ||
| return Response({ | ||
| "bio": user.bio, | ||
| "projects": user.projects, | ||
| "experience": user.experience | ||
| }) | ||
|
|
||
| def patch(self, request): | ||
| user = request.user | ||
| data = request.data | ||
|
|
||
| if 'bio' in data: | ||
| user.bio = data['bio'] | ||
| if 'projects' in data: | ||
| user.projects = data['projects'] | ||
| if 'experience' in data: | ||
| user.experience = data['experience'] | ||
|
|
||
| user.save() | ||
| return Response({"message": "Profile updated successfully"}) No newline at end of file |
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The response doesn't follow the codebase's standard pattern. Similar to the GET endpoint, this should use CustomResponse for consistency. The success response should return the updated data (like api/dashboard/profile/profile_view.py:UserProfileEditView line 79 does with CustomResponse(response=serializer.data).get_success_response()), not just a message.
| class UserProfileView(APIView): | |
| permission_classes = [IsAuthenticated] | |
| def get(self, request): | |
| user = request.user | |
| return Response({ | |
| "bio": user.bio, | |
| "projects": user.projects, | |
| "experience": user.experience | |
| }) | |
| def patch(self, request): | |
| user = request.user | |
| data = request.data | |
| if 'bio' in data: | |
| user.bio = data['bio'] | |
| if 'projects' in data: | |
| user.projects = data['projects'] | |
| if 'experience' in data: | |
| user.experience = data['experience'] | |
| user.save() | |
| return Response({"message": "Profile updated successfully"}) | |
| from utils.custom_response import CustomResponse | |
| class UserProfileView(APIView): | |
| permission_classes = [IsAuthenticated] | |
| def get(self, request): | |
| user = request.user | |
| profile_data = { | |
| "bio": user.bio, | |
| "projects": user.projects, | |
| "experience": user.experience, | |
| } | |
| return CustomResponse(response=profile_data).get_success_response() | |
| def patch(self, request): | |
| user = request.user | |
| data = request.data | |
| if 'bio' in data: | |
| user.bio = data['bio'] | |
| if 'projects' in data: | |
| user.projects = data['projects'] | |
| if 'experience' in data: | |
| user.experience = data['experience'] | |
| user.save() | |
| updated_profile_data = { | |
| "bio": user.bio, | |
| "projects": user.projects, | |
| "experience": user.experience, | |
| } | |
| return CustomResponse(response=updated_profile_data).get_success_response() |
| path("coupon/", include("api.dashboard.coupon.urls")), | ||
| path("projects/", include("api.dashboard.projects.urls")), | ||
| path("achievement/", include("api.dashboard.achievement.urls")), | ||
| path('profile/user-profile/',UserProfileView.as_view()), |
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This creates a duplicate endpoint. There's already a 'user-profile/' path defined in api/dashboard/profile/urls.py (line 8) which maps to UserProfileAPI. This new endpoint would conflict with or shadow that existing endpoint depending on URL resolution order. The new editable profile fields (bio, projects, experience) should either be added to the existing UserProfileAPI/UserProfileEditView in api/dashboard/profile/profile_view.py, or this new view should be added to api/dashboard/profile/urls.py with a different, non-conflicting path name.
| path('profile/user-profile/',UserProfileView.as_view()), | |
| path('profile/user-profile-view/', UserProfileView.as_view()), |
| from db.user import User | ||
|
|
Copilot
AI
Jan 2, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Import of 'User' is not used.
| from db.user import User |
No description provided.