해시태그

김용희·2022년 2월 7일
0

Hashtag

Create

model 작성

# articles/models.py

**class Hashtag(models.Model):
    content = models.TextField(unique=True)

    def __str__(self):
        return self.content**
    

class Article(models.Model):
    **hashtags = models.ManyToManyField(Hashtag)**
$ python manage.py makemigrations
$ python manage.py migrate
# articles/admin.py

from .models import Article, Comment, **Hashtag**

**admin.site.register(Hashtag)**
# articles/views.py

from .models import Article, Comment, **Hashtag**

@login_required
@require_http_methods(['GET', 'POST'])
def create(request):
    if request.method == 'POST':
        form = ArticleForm(request.POST)
        if form.is_valid():
            article = form.save(commit=False)
            article.user = request.user
            article.save()
            **# hashtag
            for word in article.content.split(): # content 를 공백기준으로 리스트로 변경
                if word[0] == '#': # '#'로 시작하는 요소 선택
                    hashtag, created = Hashtag.objects.get_or_create(content=word) # word(content안에 해시태그)랑 같은 기존 해시태그를 찾으면 기존 객체를, 없으면 새로운 객체를 return
                    article.hashtags.add(hashtag)**
            return redirect('articles:detail', article.pk)
	...
# articles/forms.py

from django import forms
from .models import Article, Comment

class ArticleForm(forms.ModelForm):

    class Meta:
        model = Article
        fields = ('title', 'content',)
  • 작성하는 코드는 article.save() 보다 하단에 작성해야 한다.
    • 최종 저장된 content를 조작하기 위함
  • 게시글을 작성해보고 admin 페이지에서 Hashtag가 잘 만들어졌는지 확인해보자. (중복 방지 되는지도 확인)

**unique arguments**

https://docs.djangoproject.com/ko/3.1/ref/models/fields/#unique

  • "True인 경우 이 필드는 테이블 전체에서 고유한 값임을 의미한다."
  • 유효성 검사 단계에서 실행되며 중복 데이터를 저장하려고 하면 .save() 메서드로 인해 IntegrityError 가 발생
  • 이 옵션은 ManyToManyField 및 OneToOneField를 제외한 모든 필드 유형에서 사용할 수 있다.

**get_or_create()**

https://docs.djangoproject.com/ko/3.1/ref/models/querysets/#get-or-create

  • "주어진 kwargs를 사용하여 객체를 찾으며 필요한 경우 하나를 생성한다."
  • (object, created) 형태의 튜플을 return 한다.
  • object
    • 검색 또는 생성 된 객체
  • created
    • 새 객체 생성 여부를 지정하는 boolean 값
    • 새로 만들어진 object 이면 True, 기존에 존재하던 object 일 경우 False
  • 선택 사항인 defaults를 제외하고 전달 된 키워드 인수는 get() 호출에 사용된다.
📌 [주의]

"단, 이 메서드는 DB 가 키워드 인자의 unique 옵션을 강제한다고 가정한다. "

unique 옵션이 없다면 이 메서드를 동시에 호출할 때
동일한 매개 변수가 삽입된 여러 행이 발행 할 수 있기 때문이다.
(hashtag에 unique=True를 주어서 사용가능 한 것)

  • [참고] 요청이 병렬로 이루어질 때 중복 객체가 생성될 수 있는 경우
    try:
        obj = Person.objects.get(first_name='John', last_name='Lennon')
    except Person.DoesNotExist:
        obj = Person(first_name='John', last_name='Lennon', birthday=date(1940, 10, 9))
        obj.save()
    obj, created = Person.objects.get_or_create(
        first_name='John',
        last_name='Lennon',
        defaults={'birthday': date(1940, 10, 9)},
    )

Update

  • 수정 될 때는 기존 게시글의 hashtag 전체를 삭제한 후 다시 등록하는 과정이다.
  • 기존 해시태그가 수정되었는지도 판단해야 하기 때문
    # articles/views.py
    
    @login_required
    @require_http_methods(['GET', 'POST'])
    def update(request, pk):
        article = get_object_or_404(Article, pk=pk)
        if request.user == article.user:
            if request.method == 'POST':
                form = ArticleForm(request.POST, instance=article)
                if form.is_valid():
                    article = form.save()
                    **article.hashtags.clear() # 해당 article 의 hashtag 전체 삭제
                    for word in article.content.split():
                        if word[0] == '#':
                            hashtag, created = Hashtag.objects.get_or_create(content=word)
                            article.hashtags.add(hashtag)**
                    return redirect('articles:detail', article.pk)
    ...
    • 게시글의 본문에서 해시태그를 수정 후 잘 동작 했는지 admin 페이지에서 확인 해보자.
    1. hashtag 테이블의 기존 hashtag는 유지되어 있어야 하고,

    2. 수정이나 새로운 입력으로 생긴 새 hashtag들이 저장되어 있어야 한다.

Hashtag 모아보기

  • 해시태그를 클릭했을 때 해당 해시태그를 가진 게시물들만 모아서 보여주는 페이지
# articles/urls.py

urlpatterns = [
    ...,
    path('<int:hash_pk>/hashtag/', views.hashtag, name='hashtag'),
]
# articles/views.py

def hashtag(request, hash_pk):
    hashtag = get_object_or_404(Hashtag, pk=hash_pk)
    articles = hashtag.article_set.order_by('-pk')
    context = {
        'hashtag': hashtag, 
        'articles': articles,
    }
    return render(request, 'articles/hashtag.html', context)
<!-- articles/hashtag.html -->

{% extends 'base.html' %}

{% block content %}
  <h1>{{ hashtag.content }}</h1>
  <p>{{ articles|length }}개의 게시글이 {{ hashtag.content }}를 태그중입니다.</p>
  
  <hr>

  <h2>{{ hashtag.content }}를 태그한 게시글</h2>
  {% for article in articles %}
    <p>제목 : {{ article.title }}</p>
    <a href="{% url 'articles:detail' article.pk %}">보러가기</a>
    <hr>
  {% endfor %}
{% endblock %}

http://127.0.0.1:8000/articles/1/hashtag/ 예시


Custom template filters

https://docs.djangoproject.com/en/3.1/howto/custom-template-tags/#custom-template-tags-and-filters

Hashtag 링크 만들기

💡 Django 사용자 정의 필터 (Custom Template Filter)를 활용하여 해시태그 링크 구현하기
  • article의 content 중에서 해시태그에 해당되는 부분만 링크로 만들어주기 위해, 해당 기능을 수행할 수 있는 우리만의 필터(Custom Template Filter)를 만들어서 사용해야 한다.
  1. articles 앱 안에 templatetags 폴더 생성 (폴더명은 반드시 templatetags ****)

  2. templatetags 폴더안에 다음과 같은 파일트리 만들기

    **articles/**
        __init__.py
        models.py
        **templatetags/**
            __init__.py
            **make_hashtag_link.py**
        views.py
  3. 사용자 정의 필터 작성

    # articles/templatetags/make_hashtag_link.py
    
    from django import template
    
    register = template.Library()
    
    @register.filter 
    def hashtag_link(word):
        content = word.content + ' '
        hashtags = word.hashtags.all()
        
        for hashtag in hashtags:
            content = content.replace(hashtag.content + ' ', f'<a href="/articles/{hashtag.pk}/hashtag/">{hashtag.content}</a> ')	# 마지막 공백 있음!
    
        return content
    • hashtag_link 라는 이름의 함수를 정의한다. (템플릿에서 사용할 커스텀 필터의 이름이 됨)
    • 해당 article이 가지고 있는 모든 hashtags를 순회하며, content 내에서 해당 문자열(해시태그)을 링크를 포함한 문자열로 replace 하는 기능을 하는 함수이다.
      • ex) #스타벅스 => <a href="/articles/1/hashtag/">#스타벅스</a>
    • 결과는 replace가 완료된 result 를 return 한다.
  4. templatetags 폴더를 추가하고 나서 반드시 서버를 재시작 해야 한다.

  • 이제 직접 만든 필터를 적용해보자.
    <!-- articles/detail.html -->
    
    {% extends 'base.html' %}
    **{% load make_hashtag_link %}**
    
    {% block content %}
      <h2>DETAIL</h2>
      <h3>{{ article.pk }} 번째 글</h3>
      <hr>
      <p>제목 : {{ article.title }}</p>
      **<p>내용 : {{ article|hashtag_link|safe }}</p>**
    • 템플릿 최상단에 우리가 만든 커스텀 템플릿 파일명을 load 해준다.
    • DTL에서 filter 함수의 인자는 | (파이프 라인) 앞에(왼쪽) 변수가 인자로 들어간다.
      • 그렇다면 현재 우리 코드에서는 hashtag_link(article) 로 동작하게 된다.

**safe filter**

https://docs.djangoproject.com/en/3.1/ref/templates/builtins/#safe

  • 출력 전에 추가 HTML escape 가 필요하지 않은 모습으로 문자열을 표시한다.

detail 페이지 결과 확인

profile
He threw his knapsack over the brick wall

0개의 댓글