LIKE 좋아요 버튼
게시글에 좋아요버튼을 추가해본다.
models.py
class Article(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
like_users = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='like_articles')
title = models.CharField(max_length=10)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
Article 모델에 like_users를 추가했다. user를 이용한 M:N관계이다.
related_name을 설정하지 않으면, article-user에서 생겼던 article_set 역참조 manager을 이미 사용하고 있기 때문에 구분할 수 없다. 그래서 related_name을 설정하는데 보통 M:N에서 설정해준다.
여기서는 like_articles라는 이름으로 설정했다.
이와 같은 user-article간의 manager를 사용할 수 있다.
urls.py
path('<int:article_pk>/likes/', views.likes, name='likes'),
url에 좋아요 관련 views를 추가한다.
views.py
@require_POST
def likes(request, article_pk):
article = Article.objects.get(pk=article_pk)
# 좋아요 취소 (remove)
article.like_users.remove(request.user)
# 좋아요 추가 (add)
article.like_users.add(request.user)
간단하게 어떤 게시글인지 article 인스턴스를 가져오고
article에서 참조하는 like_users를 remove, add를 사용해서 좋아요 취소, 추가 동작을 할 수 있는 뼈대를 만들었다.
좋아요 추가, 취소는 article의 like_users에 있는지 없는지를 확인하면 된다.
(현재 게시글에 좋아요를 누른 유저목록에 좋아요를 요청하는 유저가 있는지 없는지)
if request.user in article.like_users.all():
게시글의 좋아요 유저 모든 목록을 가져와서 in을 사용해서 확인할 수 있다.
def likes(request, article_pk):
article = Article.objects.get(pk=article_pk)
if request.user in article.like_users.all():
article.like_users.remove(request.user)
else:
article.like_users.add(request.user)
return redirect('articles:index')
이것을 이용하면 위와 같이 작성할 수 있다.
def likes(request, article_pk):
article = Article.objects.get(pk=article_pk)
if article.like_users.filter(pk=request.user.pk).exists():
article.like_users.remove(request.user)
else:
article.like_users.add(request.user)
return redirect('articles:index')
현재 게시글에 좋아요를 누른 유저중에 현재 좋아요를 요청하는 유저를 검색해서 좋아하는 지 확인하는 방법이다.
in대신에 filter를 사용할 수 있다. 존재하는지를 exists()를 이용한다.
get은 검색했을 때 없으면 에러가 뜬다. filter는 없어도 빈 쿼리셋을 리턴하기 때문에 filter를 이용해야 한다.
데이터가 많아졌을 때는 이 방식이 유리하다.
.exists()
QuerySet에 결과가 포함되어 있으면 True반환, 그렇지 않으면 False 반환
큰 QuerySet에 있는 특정 개체의 존재와 관련된 검색에 유용하다.
@require_POST
def likes(request, article_pk):
if request.user.is_authenticated:
article = Article.objects.get(pk=article_pk)
if article.like_users.filter(pk=request.user.pk).exists():
article.like_users.remove(request.user)
else:
article.like_users.add(request.user)
return redirect('articles:index')
return redirect('accounts:login')
인증된 사용자인지 확인하고 아니면 로그인 화면으로 redirect하는 것을 추가했다.
template - index.html
<form action="{% url 'articles:likes' article.pk %}" method="POST">
{% csrf_token %}
{% if request.user in article.like_users.all %}
<input type="submit" value="좋아요 취소">
{% else %}
<input type="submit" value="좋아요">
{% endif %}
</form>
index의 좋아요 버튼 form을 만든다.
user가 like_users에 존재하면 좋아요취소버튼이 뜨고, 아니면 좋아요버튼이 뜨게된다.
프로필 페이지
M:N 만들었던 것을 확인하기 위해 프로필을 추가했다.
accounts/urls.py
path('profile/<str:username>/', views.profile, name='profile'),
앞에 profile/없이 <str:>으로 시작하게되면 login, index 등등의 주소도 문자열로 인식하는 점을 주의해야 한다.
views.py
def profile(request, username):
User = get_user_model()
person = User.objects.get(username=username)
context = {
'person': person,
}
return render(request, 'accounts/profile.html', context)
accounts - template - profile.html
{% extends 'base.html' %}
{% block content %}
<h1>{{ person.username }}님의 프로필</h1>
<h2>{{ person.username }}이 작성한 모든 게시글</h2>
{% for article in person.article_set.all %}
<div>{{ article.title }}</div>
{% endfor %}
<h2>{{ person.username }}이 작성한 모든 댓글</h2>
{% for comment in person.comment_set.all %}
<div>{{ comment.content }}</div>
{% endfor %}
<h2>{{ person.username }}이 좋아요 한 모든 게시글</h2>
{% for article in person.like_articles.all %}
<div>{{ article.title }}</div>
{% endfor %}
{% endblock content %}
person.article_set.all : 유저가 작성한 모든 게시글
person.comment_set.all : 유저가 작성한 모든 댓글
person.like_articles.all : 유저가 좋아요한 모든 게시글
역참조를 이용해서 원하는 정보를 가져왔다.
articles - template - index.html
<b>작성자 : <a href="{% url 'accounts:profile' article.user %}">{{ article.user }}</a></b>
index의 글 목록에 작성자 부분을 수정한다.
그냥 user는 로그인한 유저가 되기 때문에 article.user.username를 지정해야 한다.
user의 __str__이 username으로 기본적으로 되어있기 때문에 article.user.username은 article.user와 동일하다.
models.py
class User(AbstractUser):
followings = models.ManyToManyField('self', symmetrical=False, related_name='followers')
def __str__(self):
return self.username
__str__ 작성하지 않아도 built-in으로 기본적으로 위와 같다.
'Back-end > Django' 카테고리의 다른 글
Fixtures - dumpdata, loaddata (0) | 2022.10.12 |
---|---|
Django M:N 관계 - User끼리의 follow 구현하기 (0) | 2022.10.12 |
Aggregation (Grouping data) (0) | 2022.10.12 |
Django M:N 관계 - ManyToManyField (0) | 2022.10.12 |
Django : static files 관리 / image 업로드, 출력, 수정, resizing (0) | 2022.10.11 |