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으로 기본적으로 위와 같다.