티스토리 뷰

반응형

0. 들어가면서

좋아요 기능은 Article(게시글)과 User(사용자)와의 관계이다. 기본적으로 우리는 N:M이기 때문에 manytomanyfield를 사용하여 models.py를 아래와 같이 꾸밀 것이다.

class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    user = models.ForeignKey(settings.AUTH_USER_MODEL,
                             on_delete=models.CASCADE)
    users = models.ManyToManyField(settings.AUTH_USER_MODEL)

이러한 경우는

article.user           -            작성자

article.users          -            좋아요를 누른 사람

이 된다. 그리고 이 때 발생하는 문제점이

user.article_set에서 article_set이 유저가 작성한 글인지, 좋아요를 누른 글인지를 알 수 없게된다. 따라서 동일한 모델에서 위와 같이 진행 할 때는 반드시 역참조를 해줘야한다. 만약 위와같은 상황에서 migrations를 하면 relate_name을 추가하라고 코드에 뜬다.

 

따라서 아래와 같이 작성을 해주자.

class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    user = models.ForeignKey(settings.AUTH_USER_MODEL,
                             on_delete=models.CASCADE)
    like_users = models.ManyToManyField(settings.AUTH_USER_MODEL,
                            related_name='like_articles')

 

즉 아래 한줄을 추가하면, articles_article_like_users라는 테이블을 만들어지고 여기서 관리를 한다. column을 추가 했지만 table이 생성이 된다. 

 

 

정리) manytomanyfield를 통해 새로운 table을 생성 시킬 수 있다. 그러나 추가적 정보를 넣어야 된다면 중개 모델이 필요하다. 즉, 중개 table의 모델을 만드는 경우는 id의 추가적 필드가 필요한 경우 through 옵션으로 연결하면 되고, 중개 모델을 안만드는 경우는 좋아요나 팔오우 기능을 구현할 때는 안만들어도 된다.

 

M:N -> MTM(ManyToMany의 위치는 어디든 상관 없다.)

이떄 related_name으로 모델을 지정할 때는 복수형으로 지정한다.(like_users, like_articles) 왜냐하면 naming만 봐도 관계 유추가 가능하다.

ex) article.faverites_users   /      user.favorites_articles     이러한 것을 보면 M:N임을 알 수 있고 즐겨찾는 글로 설정하는것이라는 걸 생각해 볼 수 있다.

 

 

1. 좋아요 기능 구현

1. url

몇 번째 글인지를 확인 하기 위해서는 variable routing 사용이 필수 이다.

articlesd/<pk>/like/     로 사용하면 될듯하다.

2. view

views 함수 내에서 아래처럼 분기를 하면 될듯하다.

- 좋아요를 눌렀으면 취소

- 좋아요를 안눌렀으면 좋아요

3. template

만들 필요 없이 redirect만 해주자.

 

 

 

 

2. 코드 구현

1. articles/urls.py

path('<int:pk>/like/', views.like, name='like'),

 

2. articles/views.py

def like(request, pk):
	article = get_object_or_404(Article, pk=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:detail', article.pk)

 

코드해석

article = get_object_or_404(Article, pk=pk)

어떤 글을 수정 삭제 해야하기 때문에 object를 가져와야한다.

 

if request.user in article.like_users.all():

article.like_users.all()을 사용해서 article에 좋아요를 누른 사람들을 불러오면 쿼리의 결과에 object들이 모여있다. 그 중에 request.user로 로그인한 유저정보가 그 안에 있는지를 검사한다.

위의 if request.user in article.like_users.all(): 대신 아래의

if article.like_users.filter(id=request.user.pk).exists(): 을 사용해도된다.

article 오브젝트를 불러온 후 like_users로 QuerySet을 검사하여 id로 찾는것이다.

 

article.like_users.remove(request.user)

좋아요 취소

 

article.like_users.add(request.user)

좋아요 등록

 

return redirect('articles:detail', article.pk)

원래 있던 곳으로 돌아가면 된다.

 

 

 

3. articles/detail.html

여기서 좋아요라는 글자를 ♥로 바꿔보자.

https://fontawesome.com/

 

Font Awesome

The world’s most popular and easiest to use icon set just got an upgrade. More icons. More styles. More Options.

fontawesome.com

'위의 사이트를 들어가서 문서를 참고하여 'start for free'를 이용해서 사용하자.

이메일 인증 후에 base.html에 넣고 사용하면 된다.

<i class="fas fa-heart fa-lg animated hinge delay-1s" style="color: red;"></i> 

이런식으로 사용할 수 있는데 장점은 일반 태그로 받아 들여 style을 넣어서 색을 조정할 수 있다.

그리고 여기다가 css를 추가 할 수 있다. 아래의 사이트에 들어가면 된다.

 

 

 

 

https://animate.style/

 

Animate.css

Animate.css v4 brought some breaking changes, please refer to the migration guide before updating from v3.x and under. Animate.css is a library of ready-to-use, cross-browser animations for use in your web projects. Great for emphasis, home pages, sliders,

animate.style

CDN 추가로 움직이는 것들도 가능하다. (하트가 움직이면서 바뀐다.)

위의 사이트의 장점은 별도로 만들필요 없이 기존에 있는 코드에 글자만 추가해 주면 된다. 즉, 위에서 적은 i 태그안에

코드를 추가 하기만 하면된다. 사용예는 아래와 같다.

<i class="far fa-heart fa-lg animated infinite bounce delay-1s" style="color: gray;"></i>

 

 

 

따라서 최종 detail.html에 추가하면 되는 코드는

{% if request.user in article.like_users.all %}
<a href="{% url 'articles:like' article.pk %}">
    <i class="fas fa-heart fa-lg animated hinge delay-1s" style="color: red;"></i>
</a>
{% else %}
<a href="{% url 'articles:like' article.pk %}">
    <i class="far fa-heart fa-lg animated infinite bounce delay-1s" style="color: gray;"></i>
</a>
{% endif %}
<p>{{ article.like_users.count }}명이 좋아합니다.</p>

 

 

3-1. with 활용(캐싱)

위의 html에서 변수에 값을 할당하여 사용하는 방법을 배워보자. 결과를 변수에 넣어서 with 구문안에서 재활용이 가능하다(캐싱). 이렇게 하면 추가적인 Query를 안 날리고 사용 할 수 있다. 쉽게 말해서 필요 할 때마다 Query 호출을 하지 않고 쿼리를 받아온걸 저장하고 그걸 계속 쓴다. object가 이미 들어와 있기 때문에 length도 쓸 수 있다.

 

{% with article_like_users=article.like_users.all %}
    {% if request.user in article_like_users %}
    <a href="{% url 'articles:like' article.pk %}">
        <i class="fas fa-heart fa-lg animated delay-1s" style="color: red;"></i>
    </a>
    {% else %}
    <a href="{% url 'articles:like' article.pk %}">
        <i class="far fa-heart fa-lg animated infinite bounce delay-1s" style="color: gray;"></i>
    </a>
    {% endif %}
    <p>{{ article_like_users|length }}명이 좋아합니다.</p>
{% endwith %}

{{ article_like_users|length }} 이거 대신에 {{ article.like_users.conut }}도 위에 처럼 가능하다.

 

+ count 공식 분서를 보면 len() 대신 count()를 항상 사용하는게 낫다고 나온다. 따라서 count를 항상 사용하도록 하자. 그러나 with처럼 QuerySet이 다 가져와진 상태에서는 len()을 쓰는게 추가 Query를 발생시키지 않아서 좋다.

 

 

 


좋아요 MODEL 정리(역참조)

위에서 models.py에서 작성한 Article를 재정리 한다

 

class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    user = models.ForeignKey(settings.AUTH_USER_MODEL,
                             on_delete=models.CASCADE)
    like_users = models.ManyToManyField(settings.AUTH_USER_MODEL,
                            related_name='like_articles')

 

 

가입한 회원이 게시글에 좋아요를 누른다. user 입장에서는 여러 게시물에 좋아요를 누르기가 가능하고, 게시물 입장에서는 여러 유저가 좋아요를 누르기 떄문에 M:N의 관계이다.

 

위에서 말한 것 처럼 related_name을 안써서 역참조를 안하면, 

게시글 : 게시글쓴 유저 = N : 1 = article.user : user.article_set.all

게시글 : 게시글에 좋아요 누른 유저 = N : M = article.like_user.all : user.article_set.all

이와 같이 게시글을 쓴 유저와 게시글에 좋아요를 누른 유저가 같아지므로 안된다.

 

다시 정리하면

기본

id가 1인 게시글 article=Article.objects.get(id=1)
로그인한 유저 user=request.user
모든 글의 정보 가저오기 article=Article.objects.all()

 

헷갈리는 부분(역참조한 최종)

1. 이 게시글의 유저 정보 article.user(.email)
article.user(.username)
2. 이 게시글에 좋아요를 누른 모든 유저 정보를 불러오기 article.like_users()
3. 로그인 한 유저가 작성한 모든 게시글 목록 정보 user.article_set.all()
4. 로그인 한 유저가 누른 좋아요 게시글 목록 정보 user.like_articles.all()
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
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
글 보관함