-
Notifications
You must be signed in to change notification settings - Fork 8
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
feat: profile page apis #160
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.
hey @Sahal-Rasheed I tested this locally, it works as expected, I just left some minor changes, pls take a look.
from ....serializers.comment import CommentDetailSerializer | ||
from ....serializers.post import PostSerializer |
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.
lets absolute imports them since they are different app serializer instead of this explicit relative imports.
from ....serializers.comment import CommentDetailSerializer | |
from ....serializers.post import PostSerializer | |
from apps.api.serializers.comment import CommentDetailSerializer | |
from apps.api.serializers.post import PostSerializer |
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.
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 👍
backend/apps/api/urls.py
Outdated
@@ -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") |
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.
pls remove this extra url config, drf will automatically do this in viewset action.
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.
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') |
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.
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.
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.
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 👍
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.
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?
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.
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.
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) |
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.
instead of this, create a custom get_object
method above, so we can re-use it for later actions, like:
Quibble/backend/apps/api/viewsets/community/__init__.py
Lines 36 to 48 in 2bc30b3
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 |
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.
@moonlitgrace, yeah this will be good since we are reusing it
posts = ( | ||
Post.objects.filter(poster=profile) | ||
.annotate(type=Value("post", CharField())) | ||
.order_by('-created_at') | ||
) |
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.
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
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.
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.
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 | ||
) |
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.
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) |
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.
we can remove this explicit status code, it is 200 by default, since we're handling error case in self.get_object
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.
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.
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.
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:
Quibble/backend/apps/api/viewsets/community/__init__.py
Lines 50 to 53 in 2bc30b3
def get_serializer_class(self): # pyright: ignore | |
if self.action in self.serializer_classes: | |
return self.serializer_classes[self.action] | |
return self.serializer_class |
@Sahal-Rasheed I did some patches on api schema: PR #161 |
@moonlitgrace, here since you have changed the api path for |
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. |
@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 |
@Sahal-Rasheed, yes looks good.
i dont get this part, what do u mean by you cant access them? |
@moonlitgrace |
@Sahal-Rasheed i pushed a patch to fix this for now: 41fb09f. |
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.
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. |
@moonlitgrace, have added latest changes. plz review and merge. let me know if anything wrong. |
@Sahal-Rasheed nope, its perfect, thanks again ❤️ |
@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! 🙌 |
Description
Added APIs discussed in issue #135. This PR is for verification of the APIs.
Fixes requirements in #135
Type of change