Quantcast
Channel: プログラミング
Viewing all articles
Browse latest Browse all 8587

Djangoでのtag付け機能の追加 - Engineer's Memorandum

$
0
0

tag付け機能の実装例

WebアプリフレームワークDjangoの書籍1を勉強中です。 書籍の1章で説明されているコードSnippetの共有アプリをベースに機能を追加して勉強を継続しています。

今回、Snippetにtagを付ける機能を追加したので、その内容をメモしています。

機能追加前

tag付け機能は、Snippetの編集時に設定可能とし、topページにtagを表示するようにしました。

tag付与前の編集ページ

tag付与前のtopページ

機能追加後

Snippetの編集時に、チェックボックスをonにしたtagを付与する機能を追加し、topページにtagを表示させた結果です。

tag付与後の編集ページ

tag付与後のtopページ

ソースコード

Djangoはtag機能のためにdjango-taggitというライブラリが利用できます。 変更が必要なファイルは、settings.py, model.py, form.py, view.py, HTML templateと少し多いですが、個々のファイルの変更はそれほど多くありません。

django-taggitの使用準備

django-taggitがインストールされていない場合、以下でインストール可能です。

$ pip install django-taggit

Djangoプロジェクト中の、settings.pyに以下のように'taggit'を追加します。

INSTALLED_APPS = [
    ...
    'taggit',
    ...
]

migrateすれば、taggitが使用可能となります。

$ python3 manage.py migrate

model.py

追加後

以下、更新箇所は僅かなので、追加後のソースコードのみ記載します。 TaggableManager のコンストラクタの引数のblankのデフォルトはFalseです。 Snippetに一つもtagがない状況を許容するため、Trueとしています2。 TaggableManager のリレーションは何になるか気になったのでソースコードを見ると、 クラス変数を変更しない限りはManyToManyが使用されるようでした。

from django.conf import settings
from django.db import models
from taggit.managers import TaggableManager # 追加classSnippet(models.Model):
    title = models.CharField("タイトル", max_length=128)
    code = models.TextField("コード", blank=True)
    description = models.TextField("説明", blank=True)
    created_by = models.ForeignKey(settings.AUTH_USER_MODEL,
                                   verbose_name="投稿者",
                                   on_delete=models.CASCADE)
    created_at = models.DateTimeField("投稿日", auto_now_add=True)
    updated_at = models.DateTimeField("更新日", auto_now=True)
    tags = TaggableManager(blank=True) # 追加def__str__(self):
        return self.title

管理画面からのtag追加

model更新後に、以下でDBを更新すれば、adminの管理画面からtagが追加・編集できるようになります。

$ python3 manage.py makemigrations
$ python3 manage.py migrate

tagの追加画面から、例えばPythonというtagを追加します。 なお、slugはURLの生成に利用される項目で3、この画面ではnameを記入すると自動でslugも記入されます。 ただし、日本語や記号はslugには使われないため、自分で記入する必要があります(次の画面のC++タグのslugは自分でcppとしています)。

管理画面でのtagの追加・編集

本記事では、tagにPythonC++を追加しました。

tag追加後の例

form.py

追加後

tagの入力フォームをチェックボックスにするため、 ModelMultipleChoiceFieldのwidgetの設定を用いています。 Formクラスのクラス変数fieldsに"tags"を追加すれば、入力フォームにtagsの項目が追加できます。

from django import forms
from snippets.models import Snippet, Comment
from taggit.models import Tag # 追加classSnippetForm(forms.ModelForm):
    # 追加
    tags = forms.ModelMultipleChoiceField(queryset=Tag.objects.all(),
                                          widget=forms.CheckboxSelectMultiple())
 
    classMeta:
        model = Snippet
        fields = ("title", "code", "description", "tags") # "tags"追加

views.py

追加後

form.save(commit=False)とした場合、save_m2m()を呼び出す必要があります4。 これが無いと、tagフォームの入力結果が保存されません。 ManyToManyのリレーションは、親オブジェクトをまず保存する必要がありますが、comit=Falseの時はこの操作が行われないためです5

classSnippetNewView(LoginRequiredMixin, View):
    form_class = SnippetForm
    template_name = "snippets/snippet_new.html"
    login_url = "/snippets/accounts/login/"defget(self, request, *args, **kargs):
        form = self.form_class()
        return render(request, self.template_name, {"form": form})
 
    defpost(self, request, *args, **kargs):
        form = self.form_class(request.POST)
        if form.is_valid():
            snippet = form.save(commit=False)
            snippet.created_by = request.user
            snippet.save()
            # 追加。commit=False時にはこれが無いとtagが保存されない。
            form.save_m2m()
            return redirect(snippet_detail, snippet_id=snippet.pk)
        return render(request, self.template_name, {"form": form})

HTML template

追加後

tag用の列をtableに追加しています。 forloop.lastを用いて、forループの最後だけ","を表示しないようにしています(もっとスマートなやり方がありそう)。 tagがない場合は"-"を表示させています。

<tableclass="table"><thead><tr><th>投稿者</th><th>投稿日</th><th>タイトル</th><th>タグ</th></tr></thead><tbody>
  {% for snippet in snippets %}
    <tr><th>{{ snippet.created_by.username }}</th><th>{{ snippet.created_at }}</th><th><ahref="{% url 'snippet_detail' snippet.id %}">{{ snippet.title }}</a></th><!-- 付与されたtagを全て表示 --><th>
        {% if snippet.tags.names %}
          {% for tag in snippet.tags.names %}
            {% if forloop.last %}
              {{ tag }}
            {% else %}
              {{ tag }},
            {% endif %}
          {% endfor %}
        {% else %}
          -
        {% endif %}
      </th></tr>
  {% endfor %}
  </tbody></table>

書籍

まず実際にWebアプリを作り、その後詳細な機能を解説する形式で、Djangoを学ぶことができます。


Viewing all articles
Browse latest Browse all 8587

Trending Articles