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


###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>
```

####出力結果
![スクリーンショット 2021-07-04 162732.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1719742/ac1f3df1-d03c-e160-35ec-a7246acb5679.png)


###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に追加する。


####出力結果
#####次のような投稿をしてみる
注）前の投稿内容は一度削除しました
![スクリーンショット 2021-07-04 221433.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1719742/980b058e-0aef-8e08-06a6-c4e27d89af56.png)


![スクリーンショット 2021-07-04 221505.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1719742/bccbed69-3deb-c484-2b92-c94797107d17.png)
投稿にタグが登録されました

![スクリーンショット 2021-07-04 221522.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1719742/d520173d-8bc7-7156-ff16-c934d2ae5073.png)
タグのクラスも作成されました

###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で検索を絞り込まなくてはいけなかった。
検索については以前に記事にしたので、気になった方はぜひ
[簡単な検索機能を作ってみた](https://qiita.com/haruki00000000/items/1767dc4cab0e21fc1948)


`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という変数に検索ワードを入れる


####出力結果
#####次のような投稿を追加してみた
![スクリーンショット 2021-07-04 224109.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1719742/854b77d7-a67b-f20f-9b7b-0fa290561e72.png)

「2回目」と検索してみる
![スクリーンショット 2021-07-04 224146.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1719742/9881705b-2024-e540-5e4a-12fc1d0c7dbb.png)
検索出来てしっかり絞り込めています

###最後に

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