일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 그리디알고리즘
- 장고
- SQL
- 라이엇
- 리그오브레전드
- lol
- 탐욕알고리즘
- 코딩테스트준비
- 프로그래머스
- Riot
- 파이썬
- 코딩테스트
- 백준
- 스파르타내일배움캠프TIL
- greedy
- 그리디
- python
- 롤
- 자바
- 내일배움캠프
- 스파르타내일배움캠프
- API
- sort
- drf
- github
- Django
- 알고리즘
- java
- programmers
- git
- Today
- Total
Lina's Toolbox
Model Relationship (M:N) / 좋아요, 팔로우 기능 구현 본문
Model Relationship (M:N) / 좋아요, 팔로우 기능 구현
Woolina 2024. 9. 16. 05:04
ManyToMany Relationship
M:N 관계에 대해 이해해서 Django Model과 ORM을 활용하여 구현해보자!
좋아요 생각해보기
🤔 좋아요 기능은 어떻게 구현을 해야할까요?
⇒ 모든 기능은 로직을 고민하고 손으로 구현하는 방법 뿐입니다!
- 좋아요 기능이 뭔가요?
- User가 Article에 좋아요(Like)를 누르는 것입니다.
- 저장해야할 데이터는 뭘까요?
- User가 어떤 Article에 좋아요를 눌렀는지 저장하면 됩니다.
1차 구현
- User(1) - Article(N) :
- ⇒ 한 명의 유저는 여러 Article에 좋아요를 누를 수 있으니까요!
articles/models.py
class Article(models.Model):
title = models.CharField(max_length=50)
content = models.TextField()
author = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="articles"
)
...
like_user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="like_articles",
null=True
)
users/models.py
class User(AbstractUser):
...
- 괜찮을까요?
User 1이 Article 1을 좋아요를 눌렀다면?
User 2가 Article 3을 좋아요를 눌렀다면?
User 3이 Article 1에 좋아요를 눌렀다면?
→ 이건 불가능!!
→ 이건 내용은 똑같지만 아예 다른 객체이죠,,?!
⇒ 따라서, 1:N 관계로는 한계가 있습니다!
2차 구현
- User는 여러 Article에 좋아요를 누를 수 있습니다.
→ 좋아요 테이블을 따로 만들어요!- articles/models.py 동일
class Article(models.Model):
title = models.CharField(max_length=50)
content = models.TextField()
author = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="articles"
)
...
users/models.py 동일
class User(AbstractUser):
...
articles/models.py
class ArticleLike(models.Model):
article = models.ForeignKey(
Article, on_delete=models.CASCADE, related_name="likes"
)
user = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="likes"
)
- 괜찮을까요?
- User 1이 Article 1을 좋아요를 눌렀다면?
User 2가 Article 3을 좋아요를 눌렀다면?
User 3이 Article 1에 좋아요를 눌렀다면?
ManyToMany
✔️ 이제 공식문서 익숙하시죠?
https://docs.djangoproject.com/en/4.2/ref/models/fields/#manytomanyfield
- 다대다(M:N) 관계 설정시 사용하는 모델 필드입니다.
- 예시) 좋아요→ 하나의 게시글도 여러명의 유저에게 좋아요를 받을 수 있어요!
→ 한 명의 유저는 여러개의 게시글을 좋아할 수 있어요!
- 예시) 좋아요→ 하나의 게시글도 여러명의 유저에게 좋아요를 받을 수 있어요!
- 중계 테이블을 이용해서 관계를 표현합니다.
- models.ManyToManyField(<classname>)을 이용해서 설정합니다.
- M2M 관계가 설정되면 역참조시 사용가능한 _set이름의 RelatedManager를 생성합니다. (related_name 으로 변경 가능)
- add(), remove()를 이용해서 관련 객체를 추가, 삭제할 수 있습니다.
좋아요 구현하기
- articles/models.py 수정
class Article(models.Model):
...
like_users = models.ManyToManyField(
settings.AUTH_USER_MODEL, related_name="like_articles"
)
related manager를 통해 양방향으로 추가할 수 있다. 조인같은거 필요없이!! → 넘 편리한딩?
수정 후 migration 실행!!
✔️ 중계테이블은요??
- Django는 m2m 필드 추가시 자동으로 중계테이블을 설정합니다!
ORM 연습하기
- article과 user가져오기
2. 좋아요 추가하기 & 조회하기
- user_1 → article_3 : 좋아요
- user_2 → article_3 : 좋아요
- article_3을 좋아하는 모든 user 목록
- user_1 → article_4 : 좋아요
- user_1이 좋아하는 모든 article 목록
3. article_article_like_users 중계테이블
💡 만약에, 좋아요에 ‘정도’를 표현하고 싶다면?
- 중계테이블을 내가 직접 정의해줄 수도 있어요!
- through를 사용하여 중계테이블 지정이 가능합니다
(장고가 만들어주는 테이블말고 이 테이블을 중계테이블로 사용할거야!)
- through를 사용하여 중계테이블 지정이 가능합니다
articles/urls
urlpatterns = [
...
path("<int:pk>/like/", views.like, name="like"),
...
]
articles/views
@require_POST
def like(request, pk):
if request.user.is_authenticated:
article = get_object_or_404(Article, pk=pk)
if article.like_users.filter(pk=request.user.pk).exists():
article.like_users.remove(request.user)
else:
article.like_users.add(request.user)
else:
return redirect("accounts:login")
return redirect("articles:articles")
like을 누르면 DB에 수정이 일어나므로 post요청임
articles/templates/articles/articles.html (1차)
{% for article in articles %}
...
<form actions="{% url "article:like" article.pk %}" method="POST">
{% csrf_token &}
<input type="sumit" value="좋아요">
</form>
{% endfor %}
articles/templates/articles/articles.html (2차)
{% for article in articles %}
...
<form actions="{% url "article:like" article.pk %}" method="POST">
{% csrf_token &}
{% if user in article.like_users.all %}
<input type="sumit" value="좋아요 취소">
{% else %}
<input type="sumit" value="좋아요">
{% endif %}
</form>
{% endfor %}
팔로우 생각해보기
🤔 팔로우는 어떻게 구현할 수 있을까요?
- 한 명의 유저는 여러명의 유저를 팔로우 할 수 있어요!
- 한 명의 유저는 여러명의 팔로워를 가질 수 있어요!
⇒ M:N 관계
그런데 말입니다!
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
following = models.ManyToManyField(????, related_name='followers')
누구랑 m2m 관계를 맺어야 할까요?
⇒ USER - USER 즉, 관계를 맺는것도 내 자신입니다!
following = models.ManyToManyField('self', related_name='followers')
💡 symmetrical
- M2M Field가 동일한 모델(self)과 관계를 맺는 경우에 사용합니다.
- symmetrical=True인 경우 한 방향에서 관계를 맺으면 반대 관계도 설정됩니다. (대칭)
- 기본값은 True입니다.
'self'를 사용하는 순간 symmetrical 옵션 사용가능하다.
내가 나를 팔로우 할수는 없으므로 여기서는 False사용!
팔로우 구현하기
- accounts/models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
following = models.ManyToManyField(
"self", symmetrical=False, related_name="followers"
)
- 마이그레이션
생성된 테이블 확인
users/urls.py
from django.urls import path
from . import views
app_name = "users"
urlpatterns = [
...
path("<int:user_id>/follow/", views.follow, name="follow"),
]
users/views.py
from django.shortcuts import render, redirect
from django.views.decorators.http import require_POST
from django.shortcuts import get_object_or_404
from django.contrib.auth import get_user_model
def users(request):
return render(request, "users/users.html")
def profile(request, username):
member = get_object_or_404(get_user_model(), username=username)
context = {
"member": member,
}
return render(request, "users/profile.html", context)
@require_POST
def follow(request, user_pk):
if request.user.is_authenticated:
member = get_object_or_404(get_user_model(), pk=user_pk)
if request.user != member:
if request.user in member.followers.all():
member.followers.remove(request.user)
else:
member.followers.add(request.user)
return redirect("users:profile", member.username)
return redirect("accounts:login")
users/templates/users/profile.html (1차)
{% extends 'base.html' %}
{% block content %}
<h1>{{ member.username }}의 프로필 페이지</h1>
<div>
<h2>username : {{ member.username }}</h2>
<p>
팔로워 : {{ member.followers.count }}명
팔로잉 : {{ member.following.count }}명
</p>
</div>
<div>
<form action="{% url "users:follow" member.pk %}" method="POST">
{% csrf_token %}
<button type="submit">팔로우</button>
</form>
</div>
<a href="/index/">홈으로 돌아가기</a>
{% endblock content %}
users/templates/users/profile.html (2차)
{% extends 'base.html' %}
{% block content %}
<h1>{{ member.username }}의 프로필 페이지</h1>
<div>
<h2>username : {{ member.username }}</h2>
<p>
팔로워 : {{ member.followers.count }}명
팔로잉 : {{ member.following.count }}명
</p>
</div>
<div>
<form action="{% url "users:follow" member.pk %}" method="POST">
{% csrf_token %}
{% if user in member.followers.all %}
<button type="submit">언팔로우</button>
{% else %}
<button type="submit">팔로우</button>
{% endif %}
</form>
</div>
<a href="/index/">홈으로 돌아가기</a>
{% endblock content %}
'스파르타 내일 배움 캠프 AI 웹개발 과정 > Django framework' 카테고리의 다른 글
Django에서 부트스트랩, Fontawesome사용 (0) | 2024.09.19 |
---|---|
Custom UserModel 활용하기 (1) | 2024.09.16 |
Django Model Relationship (1:N) (0) | 2024.09.16 |
Django Admin (1) | 2024.09.12 |
Django Static & Media (0) | 2024.09.12 |