ハッシュタグ機能を作ってみよう


1.簡単な投稿機能を作成

構造
mysite
    ├─mysite
    │  └─__pycache__
    ├─post
    │  ├─migrations
    │  │  └─__pycache__
    │  └─__pycache__
    └─templates

mysite/post/models.py

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=20)
    text = models.CharField(max_length=200)

    def __str__(self):
        return self.title

mysite/post/views.py

from django.views.generic import ListView, CreateView
from django.urls import reverse_lazy
from .models import Post


class ListClass(ListView):
    template_name = 'list.html'
    model = Post


class FormClass(CreateView):
    template_name = 'form.html'
    model = Post
    fields = ('title', 'text')
    success_url = reverse_lazy('list')

mysite/post/urls.py

from django.urls import path
from .views import FormClass, ListClass

urlpatterns = [
    path('', ListClass.as_view(), name='list'),
    path('form/', FormClass.as_view(), name='form'),
]

templatesファイルを作るのでsettings.pyのTEMPLATEを設定する

'DIRS': [BASE_DIR, 'templates'],

mysite/templates/form.html

<form method="POST">
  {% csrf_token %}
  <p>タイトル<br /><input type="text" name="title" /></p>
  <p>
    内容<br /><textarea type="text" name="text" rows="10" cols="40"></textarea>
  </p>
  <input type="submit" value="投稿する" />
</form>

mysite/templates/list.html

<div class="container">
  {% for post in object_list %}
  <div class="post">
    <p>タイトル<br/>{{ post.title }}</p>
    <p>内容<br/>{{ post.text|linebreaksbr  }}</p>
    <p>--------------------</p>
  </div>
  {% endfor %}
  <div class="form">
    <a href="{% url 'form' %}">新規投稿</a>
  </div>
</div>

出力結果

2.タグ機能を実際に作っていく

mysite/post/models.py

from django.db import models

class Tag(models.Model):
    name = models.CharField(max_length=50)

    def __str__(self):
        return self.name


class Post(models.Model):
    title = models.CharField(max_length=20)
    text = models.CharField(max_length=200)
    tag = models.ManyToManyField(Tag)

    def __str__(self):
        return self.title

tagクラスを作り、postクラスと結びつけた
postクラスはたくさんのタグを持つことができる

mysite/post/views.py

from django.views.generic import ListView, CreateView
from django.urls import reverse_lazy
from django.shortcuts import redirect
from .models import Post, Tag


class ListClass(ListView):
    template_name = 'list.html'
    model = Post


class FormClass(CreateView):
    template_name = 'form.html'
    model = Post
    fields = ('title', 'text')
    success_url = reverse_lazy('list')

    def form_valid(self, form):
        post = Post(
            title=form.cleaned_data["title"], text=form.cleaned_data["text"])
        post.save()
        words = form.cleaned_data["text"].split()
        for word in words:
            if word[0] == "#":
                if Tag.objects.filter(name=word[1:]).exists():
                    tag = Tag.objects.get(name=word[1:])
                else:
                    tag = Tag.objects.create(name=word[1:])
                post.tag.add(tag)
        return redirect('list')

splitの中を指定しないことで空白、改行などで分割ができる
分割して受け取った単語ごとにforを回す。先頭文字が「#」ならタグとみなす。
「#」以降の文字のタグが存在するかしないかで、新たにタグを作るか分岐する。
post.tag.add(tag)でPostクラスのtagに追加する。

出力結果

次のような投稿をしてみる

注)前の投稿内容は一度削除しました


投稿にタグが登録されました


タグのクラスも作成されました

3.タグの検索機能作ってみる

mysite/post/views.py

class ListClass(ListView):
    template_name = 'list.html'
    model = Post
    def get_queryset(self):
        tag = self.request.GET.get('tag')

        if tag:
            post_list = Post.objects.filter(
                tag__name__icontains=tag)
        else:
            post_list = Post.objects.all()
        return post_list

ListClassを少し編集する
filterで注意ないといけなかったのが
Post.objects.filter(tag__name__icontains=tag)
PostのtagはManyToManyField(Tag)なのでTagクラスのnameで検索を絞り込まなくてはいけなかった。
検索については以前に記事にしたので、気になった方はぜひ
簡単な検索機能を作ってみた

mysite/templates/list.html

<div class="container">
  <form action="" method="get">
    <input name="tag" value="{{ request.GET.tag }}" type="text" />
    <button type="submit">検索する</button>
  </form>
  {% for post in object_list %}
  <div class="post">
    <p>タイトル<br />{{ post.title }}</p>
    <p>内容<br />{{ post.text|linebreaksbr }}</p>
    <p>--------------------</p>
  </div>
  {% endfor %}
  <div class="form">
    <a href="{% url 'form' %}">新規投稿</a>
  </div>
</div>

tagという変数に検索ワードを入れる

出力結果

次のような投稿を追加してみた

「2回目」と検索してみる

検索出来てしっかり絞り込めています

最後に

今回は#タグでタグ付けの機能を作りました。検索で投稿を絞るところまで作ってみました。
ここから、タグごとにaタグなどで絞り込んだりしてみても面白いなと思います。
今回作ったもののgithub