Django 게시물 & 댓글 & 좋아요 & follow
westagram project를 진행하며 초기세팅부터 로그인 기능까지 구현하였다. 이제 추가미션인 게시물, 댓글, 좋아요, follow 기능을 구현하면서 Django API를 만드는데 더욱 익숙해지도록 하자
Model
Django에서는 주로 다루는 데이터의 종류가 달라지는 시점에서 앱을 분리하는게 좋다. 그러므로 새로 postings라는 app을 만들었고 새로운 테이블간의 Modeling을 진행하여 ERD를 만들었다.
ERD는 다음과 같다.
Modeling한 ERD를 토대로 하여 Model을 작성하였다.
# posings/models.py
from django.db import models
class Posting(models.Model):
image_url = models.URLField()
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey('users.User', on_delete=models.CASCADE)
class Meta:
db_table = 'postings'
class Comment(models.Model):
user = models.ForeignKey('users.User', on_delete=models.CASCADE)
posting = models.ForeignKey('Posting', on_delete=models.CASCADE)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'comments'
class Like(models.Model):
user = models.ForeignKey('users.User', on_delete=models.CASCADE)
posting = models.ForeignKey('Posting', on_delete=models.CASCADE)
class Meta:
db_table = 'likes'
postings app에서 users App의 User Class를 ForeignKey를 사용할 경우 참조할 테이블의 이름을 AppName.TableName으로 지정해야 제대로 적용이 된다.
# users/models.py
from django.db import models
class User(models.Model):
name = models.CharField(max_length=30)
email = models.EmailField(max_length=50, unique=True)
password = models.CharField(max_length=120)
phone_number = models.CharField(max_length=20)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'users'
class Follow(models.Model):
follow = models.ForeignKey('User', on_delete=models.CASCADE, related_name='follow')
followed = models.ForeignKey('User', on_delete=models.CASCADE, related_name='followed')
class Meta:
db_table = 'follows'
follow와 following은 user들 사이에서 일어나는 기능이기 때문에 users app에서 작성하였다.
ERD를 보면 두개의 Foreignkey로 하나의 클래스에서 같은 객체를 참조하게 되는데 related_name을 사용하지 않으면 다음의 에러가 발생한다.
Follow 클래스에서 follow, followed Field가 같은 User 클래스 객체를 참조하므로 둘 중 어느 인스턴스를 부르는건지 따로 지정해주지 않으면 구분할 수 없다.
따라서 related_name으로 User 클래스 객체를 참조하는 Field를 구분해주어야 한다.
Posting View
class PostingView(View):
def post(self, request):
try:
data = json.loads(request.body)
image_url = data['image_url']
content = data['content']
user = User.objects.get(email=data['email'])
if not User.objects.filter(email=data['email']).exists():
return JsonResponse({'message':'INVALID_USER'}, status=401)
Posting.objects.create(
image_url = image_url,
content = content,
user = user
)
return JsonResponse({'message':'SUCCESS'}, status=201)
except KeyError:
return JsonResponse({'message':'KEY_ERROR'}, status=400)
def get(self, request, posting_id):
if not Posting.objects.filter(id=posting_id).exists():
return JsonResponse({'message':'NON_POST'}, status=400)
posting = Posting.objects.get(id=posting_id)
results = []
results.append(
{
'user' : posting.user.email,
'image_url' : posting.image_url,
'content' : posting.content,
'created_at' : posting.created_at
}
)
return JsonResponse({'results':results}, status=200)
# postings/urls.py
urlpatterns = [
path('/posting', PostingView.as_view()),
path('/posting/<int:posting_id>', PostingView.as_view()),
]
특정 게시글의 정보만 GET을 하기 위해 urls.py에 정수형의 id를 참조하는 코드를 추가하였다. 그리고 GET 메소드에도 posting_id argument를 추가해 준다. 이제 post/1이면 id가 1번인 게시물을 불러올 수 있다.
http -v POST 127.0.0.1:8000/postings/posting email='r123@gma
il.com' image_url='https://cdn.pixabay.com/photo/2022/01/29/11/02/cat-6977096_1280.jpg' content='고양이 사진'
http -v GET 127.0.0.1:8000/postings/posting/2
Comment View
class CommentView(View):
def post(self, request):
try:
data = json.loads(request.body)
content = data['content']
user = User.objects.get(email=data['email'])
if not User.objects.filter(email=data['email']).exists():
return JsonResponse({'message':'INVALID_USER'}, status=401)
if not Posting.objects.filter(id=data['posting_id']).exists():
return JsonResponse({'message':'NON_POST'}, status=401)
Comment.objects.create(
user = user,
posting_id = data['posting_id'],
content = content
)
return JsonResponse({'message':'SUCCESS'}, status=201)
except KeyError:
return JsonResponse({'message':'KEY_ERROR'}, status=400)
def get(self, request, posting_id):
comments = Comment.objects.filter(posting_id=posting_id)
results = [{
'user' : comment.user.email,
'content' : comment.content,
'created_at' : comment.created_at
} for comment in comments]
return JsonResponse({'reuslts':results}, status=200)
urlpatterns = [
path('/posting', PostingView.as_view()),
path('/posting/<int:posting_id>', PostingView.as_view()),
path('/comment', CommentView.as_view()),
path('/<int:posting_id>/comment', CommentView.as_view()),
]
PostingView처럼 특정 게시물에 대한 댓글만 GET하기 위해 urls.py에 정수형의 id를 참조하는 코드를 추가하고 argument도 추가하였다.
http -v POST 127.0.0.1:8000/postings/comment email='[email protected]' content='귀여운 고양이네요' posting_id=2
http -v GET 127.0.0.1:8000/postings/2/comment
Like View & Follow View
class LikeView(View):
def post(self, request):
try:
data = json.loads(request.body)
user = User.objects.get(email=data['email'])
if not User.objects.filter(email=data['email']).exists():
return JsonResponse({'message':'INVALID_USER'}, status=401)
if not Posting.objects.filter(id=data['posting_id']).exists():
return JsonResponse({'message':'NON_POST'}, status=401)
like = Like.objects.filter(user=user, posting_id=data['posting_id'])
if like.exists():
like.delete()
return JsonResponse({'message':'LIKE_DELETE'}, status=200)
Like.objects.create(
user = user,
posting_id = data['posting_id']
)
# like, is_like = Like.objects.get_or_create(
# user = user,
# posting_id = data['posting_id']
# )
# if not is_like:
# like.delete()
# return JsonResponse({"message":"LIKE_DELETE"}, status=200)
return JsonResponse({'message':'SUCCESS'}, status=201)
except KeyError:
return JsonResponse({'message':'KEY_ERROR'}, status=400)
class FollowView(View):
def post(self, request):
try:
data = json.loads(request.body)
follow_id = data['follow_id']
followed_id = data['followed_id']
if not User.objects.filter(id=follow_id).exists():
return JsonResponse({'message':'INVALID_USER'}, status=401)
if not User.objects.filter(id=followed_id).exists():
return JsonResponse({'message':'INVALID_USER'}, status=401)
follow = Follow.objects.filter(follow_id=follow_id, followed_id=followed_id)
if follow.exists():
follow.delete()
return JsonResponse({'message':'UNFOLLOWED'}, status=200)
Follow.objects.create(
follow_id = follow_id,
followed_id = followed_id
)
# follow, is_follow = Follow.objects.get_or_create(
# follow = User.objects.get(email=data['follow']),
# followed = User.objects.get(email=data['followed'])
# )
# if not is_follow:
# follow.delete()
# return JsonResponse({"MESSAGE":"UNFOLLOWED"}, status=200)
return JsonResponse({'message':'FOLLOWED'}, status=201)
except KeyError:
return JsonResponse({'message':'KEY_ERROR'}, status=400)
http -v POST 127.0.0.1:8000/postings/like email='[email protected]' posting_id=2
http -v POST 127.0.0.1:8000/postings/follow follow_id=5 followed_id=4
이미 좋아요랑 팔로우를 한 상태에서 다시 같은 요청을 보내면 좋아요가 해제되고 언팔로우가 되도록 구현하였다.
예전 기수분이 get_or_create 메소드를 사용해서 구현하신것을 보았는데 확실히 코드가 간결해지면서 create, delete기능을 동시에 처리할 수 있어서 좋은 방법인 것 같다.
Author And Source
이 문제에 관하여(Django 게시물 & 댓글 & 좋아요 & follow), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@cghy4862/Django-게시물-댓글-좋아요-follow저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)