Skip to content

feat: profile page apis #160

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

Merged
merged 6 commits into from
Feb 2, 2025

Conversation

Sahal-Rasheed
Copy link
Contributor

@Sahal-Rasheed Sahal-Rasheed commented Feb 1, 2025

Description

Added APIs discussed in issue #135. This PR is for verification of the APIs.

Fixes requirements in #135

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

Copy link
Member

@moonlitgrace moonlitgrace left a comment

Choose a reason for hiding this comment

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

hey @Sahal-Rasheed I tested this locally, it works as expected, I just left some minor changes, pls take a look.

Comment on lines 5 to 6
from ....serializers.comment import CommentDetailSerializer
from ....serializers.post import PostSerializer
Copy link
Member

Choose a reason for hiding this comment

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

lets absolute imports them since they are different app serializer instead of this explicit relative imports.

Suggested change
from ....serializers.comment import CommentDetailSerializer
from ....serializers.post import PostSerializer
from apps.api.serializers.comment import CommentDetailSerializer
from apps.api.serializers.post import PostSerializer

Copy link
Contributor Author

Choose a reason for hiding this comment

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

lets absolute imports them since they are different app serializer instead of this explicit relative imports.

@moonlitgrace, yeah i also thought of using absolute imports but as i could see other relative imports in the code base got confused so kept those above imports as relative imports, now as u confirmed i will now use absolute imports 👍

@@ -31,7 +31,8 @@
path('users/', include([
# user view of requested user
path('me/', MeAPIView.as_view(), name='me'),
]))
])),
path("u/<str:username>/overview/", ProfileViewSet.as_view({'get': 'overview'}), name="profile-overview")
Copy link
Member

Choose a reason for hiding this comment

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

pls remove this extra url config, drf will automatically do this in viewset action.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

pls remove this extra url config, drf will automatically do this in viewset action.

@moonlitgrace, ok will remove. i added this extra url config coz, as u mentioned here the api endpoint path as /u/<username>/overview/ but since we are adding extra actions to profileviewset which already has a api path as users/profiles, then our api path for overview action would be like users/profiles/{username}/overview so to overcome this i added that extra url config. but now with the changes u added here ig now that additional url added for action is no more needed.

@@ -19,6 +29,38 @@ class ProfileViewSet(viewsets.ReadOnlyModelViewSet):
filter_backends = (filters.SearchFilter,)
search_fields = ('username',)

@action(detail=False, methods=[HTTPMethod.GET], url_path=r'(?P<username>[^/.]+)/overview')
Copy link
Member

Choose a reason for hiding this comment

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

make this detail=True, so we can utilize self.get_object to retrieve specific instance easily. and also we dont need that url_path, pls remove that too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

make this detail=True, so we can utilize self.get_object to retrieve specific instance easily. and also we dont need that url_path, pls remove that too.

@moonlitgrace sure, also since u said we dont need that url_path i will add lookup_field = 'username' in the profileviewset to match our api requirement 👍

Copy link
Member

Choose a reason for hiding this comment

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

i will add lookup_field = 'username' in the profileviewset to match our api requirement

that part, should we use id or username as lookup_field? im a bit confused abt this part, i've already done frontend integration with id, well thats no problem, i can replace it with username. whadya think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

that part, should we use id or username as lookup_field? im a bit confused abt this part, i've already done frontend integration with id, well thats no problem, i can replace it with username. whadya think?

@moonlitgrace, i used username as in a thought our endpoint should be like smtng/<username>/overview but later now u confirmed that u meant it related to FE so with that we can use id itself instead of username i will revert it.

Comment on lines 33 to 36
def overview(self, request, username=None):
"""Returns a mixed list of posts and comments by the user, ordered by date."""
try:
profile = get_object_or_404(Profile, username=username)
Copy link
Member

Choose a reason for hiding this comment

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

instead of this, create a custom get_object method above, so we can re-use it for later actions, like:

def get_queryset(self) -> QuerySet[Community]: # pyright: ignore
return super().get_queryset()
def get_object(self) -> Community: # pyright: ignore
qs = self.get_queryset()
filter_kwargs = {f'{self.lookup_field}__iexact': self.kwargs[self.lookup_field]}
obj = qs.filter(**filter_kwargs).first()
if not obj:
raise exceptions.NotFound(
f'Community with name <b>{self.kwargs[self.lookup_field]}</b> not found.'
)
return obj

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@moonlitgrace, yeah this will be good since we are reusing it

Comment on lines 38 to 42
posts = (
Post.objects.filter(poster=profile)
.annotate(type=Value("post", CharField()))
.order_by('-created_at')
)
Copy link
Member

Choose a reason for hiding this comment

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

we can access Post relatively here, like: profile.posts.all(), so no need for extra import. and do we need to order/filter this here? cuz we are sorting both in next step anyways.
same goes for comments

Copy link
Contributor Author

@Sahal-Rasheed Sahal-Rasheed Feb 2, 2025

Choose a reason for hiding this comment

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

we can access Post relatively here, like: profile.posts.all(), so no need for extra import.

@moonlitgrace, yeah i forget it we can easily access post and comment relatively which avoids the need for additional imports will use this way as u said.

do we need to order/filter this here? cuz we are sorting both in next step anyways.

@moonlitgrace, yeah thats right, then no need to order the queryset in orm level will remove .order_by sorting.

Comment on lines 50 to 55
post_data = OverviewSerializer(posts, many=True).data
comment_data = OverviewSerializer(comments, many=True).data

combined_data = sorted(
chain(post_data, comment_data), key=lambda x: x['data']['created_at'], reverse=True
)
Copy link
Member

Choose a reason for hiding this comment

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

sorting before serialization is more efficient (sorting raw queryset objects is faster), so sort them directly and pass sorted data to OverviewSerializer at once.

like:

combined_data = sorted(
    chain(posts, comments),
    key=lambda obj: obj.created_at,
    reverse=True
)

serialized_data = OverviewSerializer(combined_data, many=True).data

chain(post_data, comment_data), key=lambda x: x['data']['created_at'], reverse=True
)

return response.Response(combined_data, status=status.HTTP_200_OK)
Copy link
Member

@moonlitgrace moonlitgrace Feb 2, 2025

Choose a reason for hiding this comment

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

we can remove this explicit status code, it is 200 by default, since we're handling error case in self.get_object

@moonlitgrace moonlitgrace changed the title added: profile overview api endpoint feat: profile overview api endpoint Feb 2, 2025
@moonlitgrace moonlitgrace marked this pull request as draft February 2, 2025 02:39
Copy link
Member

@moonlitgrace moonlitgrace left a comment

Choose a reason for hiding this comment

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

also one more important thing i noticed is, it returns urls in relative format, not absolute urls (with domain and meta). drf requires a request object as context to build absolute urls, so pass it to OverviewSerializer and pass to nested ones inside that.

Copy link
Member

@moonlitgrace moonlitgrace left a comment

Choose a reason for hiding this comment

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

can you also add extend_schema for openapi generation, and a custom self.get_serializer_class method for drf to pick correct serializer at runtime.
eg:

def get_serializer_class(self): # pyright: ignore
if self.action in self.serializer_classes:
return self.serializer_classes[self.action]
return self.serializer_class

@moonlitgrace
Copy link
Member

@Sahal-Rasheed I did some patches on api schema: PR #161
see if there's anything to be changed here.

@Sahal-Rasheed
Copy link
Contributor Author

@Sahal-Rasheed I did some patches on api schema: PR #161 see if there's anything to be changed here.

@moonlitgrace, here since you have changed the api path for profileviewset to u/profiles our overview and related apis will have a api path like u/profiles/{username}/overview unlike u/{username}/overview as u mentioned previously, likewise for others. so if this is expected behaviour then no issues.

@moonlitgrace
Copy link
Member

related apis will have a api path like u/profiles/{username}/overview unlike u/{username}/overview as u mentioned previously

i was actually referring to how the frontend url would be, not the api. yea sry for confusing u. you can proceed like this, we can make changes later if needed, otherwise this LGTM.

@Sahal-Rasheed Sahal-Rasheed marked this pull request as ready for review February 2, 2025 14:13
@Sahal-Rasheed
Copy link
Contributor Author

@moonlitgrace, have added the recommendations plz verify and let me know if its okay, once done will proceed with the rest of apis in #135. also once working on these i found that apis were not able to access in both swagger docs and in default way as api prefix api/v1 are not added to the api urls. i found that the issue was due to setting 'SCHEMA_PATH_PREFIX_TRIM': True, so i have commented it as of now.

@moonlitgrace
Copy link
Member

@Sahal-Rasheed, yes looks good.

also once working on these i found that apis were not able to access in both swagger docs and in default way as api prefix api/v1 are not added to the api urls. i found that the issue was due to setting 'SCHEMA_PATH_PREFIX_TRIM': True, so i have commented it as of now.

i dont get this part, what do u mean by you cant access them? SCHEMA_PATH_PREFIX_TRIM is just for openapi schema generation, how does it affect u? I actually tried it locally, seems working for me? could u tell a bit more or if u have something to show, much better.

@Sahal-Rasheed
Copy link
Contributor Author

i dont get this part, what do u mean by you cant access them? SCHEMA_PATH_PREFIX_TRIM is just for openapi schema generation, how does it affect u? I actually tried it locally, seems working for me? could u tell a bit more or if u have something to show, much better.

@moonlitgrace
Screenshot from 2025-02-02 22-01-04
here when i try to execute a api from swagger as u can in the attachement the api prefix is not there it got trimmed thus getting 404 error. this is what i meant above.

@moonlitgrace
Copy link
Member

@Sahal-Rasheed i pushed a patch to fix this for now: 41fb09f.
so, i think we are good to merge this big boy. whadya think

Copy link
Member

@moonlitgrace moonlitgrace left a comment

Choose a reason for hiding this comment

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

LGTM, thanks for your effort.
pls add a reaction, then we can merge this.

ps: make sure you've installed pre-commit next time ❤️
https://github.com/quibble-dev/Quibble/blob/main/CONTRIBUTING.md#getting-started

@Sahal-Rasheed
Copy link
Contributor Author

LGTM, thanks for your effort. pls add a reaction, then we can merge this.

ps: make sure you've installed pre-commit next time ❤️ https://github.com/quibble-dev/Quibble/blob/main/CONTRIBUTING.md#getting-started

@moonlitgrace wait a sec. i will commit the other apis in a bit as its ready so after that u can merge this PR so we can close this all at once. and regarding pre-commit i have already installed and setup it at time of project initial setup.

@Sahal-Rasheed Sahal-Rasheed changed the title feat: profile overview api endpoint feat: profile page apis Feb 2, 2025
@Sahal-Rasheed
Copy link
Contributor Author

@moonlitgrace, have added latest changes. plz review and merge. let me know if anything wrong.

@moonlitgrace
Copy link
Member

@Sahal-Rasheed nope, its perfect, thanks again ❤️

@moonlitgrace moonlitgrace merged commit 2315cc4 into quibble-dev:main Feb 2, 2025
3 checks passed
Copy link

github-actions bot commented Feb 2, 2025

@Sahal-Rasheed Your contribution makes Quibble a better place for open discussions and thriving communities! ✨ Thank you for helping shape the platform with your efforts. We appreciate your time and dedication—keep building, keep quibbling! 💬 Your impact matters! 🙌

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.

2 participants