<실습>
- Profile 페이지 api 만들어보기
- 먼저 url을 작성한다
# users/urls.py
...
urlpatterns = [
...
path('<int:user_id>/', views.ProfileView.as_view(), name='profile_view'),
]
- 다음으로 serializer를 작성한다
# users/serializers.py
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = "__all__"
- 이제 view를 작성한다
# users/views.py
...
class ProfileView(APIView):
def get(self, request, user_id):
"""프로필 페이지"""
user = get_object_or_404(User, id=user_id)
serializer = UserProfileSerializer(user)
return Response(serializer.data)
- 포스트맨에서 띄워본다
- 잘 나오긴 하는데 쓸데없는 정보가 너무 많다 여기서 id, email, followings만 있으면 될 것 같다
- 다시 시리얼라이저를 수정한다
fields = ("id", "email", "followings",)
- UserProfileSerializer에 fields를 "__all__"에서 위와 같이 수정한다
- 다시 포스트맨에서 실행해보면 원하는 값이 잘 나오는 것을 확인할 수 있다
- 그런데 followers도 있으면 좋을 것 같다
- ArticleSerializer에서 likes 필드를 추가한 것처럼 StringRelatedField를 사용한다 follower도 여러 명일 수 있으니 many=True를 꼭 넣어준다
# users/serializers.py
class UserProfileSerializer(serializers.ModelSerializer):
followers = serializers.StringRelatedField(many=True) # 여기 추가!
class Meta:
model = User
fields = ("id", "email", "followings", "followers",) # 여기도 잊지말고!
- 포스트맨에서 다시 실행해보면 followers 필드가 추가된 것을 확인할 수 있다
- 그런데 아무것도 안 보이니 팔로잉, 팔로워를 추가해주면 좋을 것 같다
- 일단 지금 로그인 되어 있으니 포스트맨 follow 리퀘스트에서 다른 사용자를 팔로우 했다
- 이제 팔로워도 만들고 싶다 근데 포스트맨에서 하려면 로그인을 해서 토큰을 다시 복붙하고...굉장히 귀찮은 과정이다
- admin 페이지에 가면 like 했던 것처럼 클릭으로 쉽게 팔로잉을 할 수 있다
- 그런데 user에 들어가서 아무 사용자나 클릭해보면 followings 필드가 없다
- 그리고 유저 목록을 보면 user id가 안 보여서 누가 1번이고 누가 8번인지 알 수 없다
- 데이터베이스에는 id로 나오니까 이 페이지에도 아이디를 같이 볼 수 있게 해보자
# users/admin.py
...
class UserAdmin(BaseUserAdmin):
...
list_display = ["email", "is_admin"]
- list_display는 위 캡쳐처럼 유저목록에 보이는 부분들이다 현재는 email과 is_admin만 있어서 저렇게 보인다
- 여기에 email 앞에 "id"를 추가해보자
list_display = ["id", "email", "is_admin"]
- id를 추가하고 저장한 뒤 admin 페이지를 새로고침 하니 id가 유저 목록에 같이 보이기 시작했다
- 이제 유저 수정 페이지에 followings 필드를 추가해보자
# users/admin.py
...
class UserAdmin(BaseUserAdmin):
...
fieldsets = [
(None, {"fields": ["email", "password"]}),
...
]
- 현재 fieldsets에 email과 password가 있다 여기에 followings를 추가해보자
(None, {"fields": ["email", "password", "followings"]}),
- followings 필드가 추가됐다 이제 여기서 팔로우를 할 수 있다
- 몇몇 유저의 팔로우를 추가하고 다시 포스트맨에서 실행해보면 팔로잉과 팔로워에 추가된 것을 확인할 수 있다
- 근데 followings와 followers가 보이는 게 다르다 하나로 통일해주면 좋을 것 같다
- 메일형식으로 보이게 followings를 고쳐보고자 한다
# users/serializers.py
class UserProfileSerializer(serializers.ModelSerializer):
followers = serializers.StringRelatedField(many=True)
followings = serializers.StringRelatedField(many=True) #여기 추가!
class Meta:
model = User
fields = ("id", "email", "followings", "followers",)
- followers와 똑같이 stringrelatedfield를 사용하면 아이디가 이메일로 보인다
- 혹시 아이디번호로 보이게 하고 싶다면 PrimaryKeyRelatedField가 있는데 이걸 사용하면 된다
followers = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
- followings는 이미 아이디로 보이니까 따로 추가하지 않아도 된다
- read_only=True를 빼먹으면 넣으라고 에러메시지가 뜬다
- 이렇게 수정하고 다시 포스트맨에서 실행하면 팔로잉, 팔로워 모두 아이디 번호로 뜨는 것을 확인할 수 있다
- 근데 이메일로 보이는 게 좋기 때문에 다시 바꿀 예정이다
- 그리고 지금 admin 페이지에서 유저 수정페이지 가서 팔로잉을 선택하지 않고 저장을 누르면 필수값이니 클릭하라고 나온다
- 유저 모델에 가서 followings 필드를 blank=True로 변경해주면 된다
# users/models.py
...
class User(AbstractBaseUser):
...
followings = models.ManyToManyField(
"self", symmetrical=False, related_name="followers", blank=True)
...
- models.py 수정 후에는 makemigrations, migrate를 잊지 말 것!
- 이제 프로필페이지에 내가 쓴 글과 내가 좋아요한 게시글이 나오면 좋을 것 같다는 생각이 든다
- 유저프로필시리얼라이저에 이어서 작성해보자
# users/serializers.py
...
from articles.serializers import ArticleListSerializer
class UserProfileSerializer(serializers.ModelSerializer):
...
article_set = ArticleListSerializer(many=True)
like_articles = ArticleListSerializer(many=True)
class Meta:
model = User
fields = ("id", "email", "followings", "followers",
"article_set", "like_articles")
- 일단 Article모델에 가면 user필드의 related_name이 설정되어 있지 않다
- 유저에서 아티클을 역참조하는 것이므로 article_set이라는 필드명으로 해줘야 한다
- 아티클 모델에 likes필드의 related_name은 like_articles로 설정해놨기 때문에 like_articles 라는 이름으로 사용하면 된다
- 시리얼라이저는 ArticleListSerializer를 사용해서 import를 해줬다
- 이렇게 하고 포스트맨에서 실행하면 내가 쓴 글, 내가 좋아요 한 글이 뜨는 것을 확인할 수 있다
- Feed 페이지 api 만들어보기
- 내가 팔로잉한 사람들의 글만 모아보는 페이지를 만들어보고자 한다
- 공식 문서: Making queries | Django documentation | Django (djangoproject.com)
- 이 기능의 구현은 위 문서를 참조한다
- 일단 url부터 작성한다
# articles/urls.py
urlpatterns = [
...
path('feed/', views.FeedView.as_view(), name='fedd_view'),
...
]
- 이제 views.py로 가서 FeedView를 작성한다
# articles/views.py
from django.db.models.query_utils import Q
...
class FeedView(APIView):
permission_classes = [permissions.IsAuthenticated]
def get(self, request):
"""팔로잉 게시글 모아보기"""
q = Q()
for user in request.user.followings.all():
q.add(Q(user=user), q.OR)
feeds = Article.objects.filter(q)
serializer = ArticleListSerializer(feeds, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
- 위 문서에서 나온 Q라는 건데..뭔지는 잘 모르겠다..
- 필터링 걸 때 필요한 거라고 한다
- q 라는 변수에 Q()를 넣어서 오브젝트를 만든다
- 로그인한 사용자가 팔로잉한 사람들을 for문에 돌려서 한 사람씩 q오브젝트에 추가한다
- q.OR로 써있는 이유는 조건에 user가 팔로잉한 사람 중 한 사람씩 계속 추가되는 건데 and로 엮으면 게시글 작성자가 팔로잉1이고, 팔로잉2고 팔로잉3인...그러니까...작성자가 여러 명인(로그인한 유저가 팔로잉한 그 유저들이 쓴 하나의의) 게시글을 찾을 거라서 send를 보내면 만족하는 게시글이 없어서 빈리스트만 뜬다 현재 게시글은 1명의 유저만 쓰게끔 되어 있어서 조건을 만족할 수가 없다
- feeds라는 변수에 게시글을 가져올 건데 필터를 걸어서 q 조건에 해당하는(로그인한 사용자가 팔로잉한 유저와 글작성자가 같은) 게시글만 가져온다
- 현재 로그인한 유저의 팔로잉 목록은 다음과 같다
- 이중 gu4와 gu7만 게시글을 써서 2개만 보인다