🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Django

DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

2746閲覧

DjangoのManyToManyFieldを用いたタグ機能の使い方がわからない

tomtakeru

総合スコア4

Django

DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/03/14 08:18

編集2021/03/16 13:35

データベースのMemoテーブル(「プロジェクト構成・ファイル」のmodels.py参照)の一覧表示,タプルの新規作成ができるwebアプリケーションを実装しました.そしてそのテーブルの属性の1つに,ManyToManyFieldでTagテーブルを指定し,実装しました.以下のふたつの疑問を解消したいです.

(疑問①)Memoテーブルの一覧表示にそれぞれのタグを表示するにはどのように実装すれば良いか.

(疑問②)Memoテーブルに指定したMemoCreateViewで,Tagテーブルのタプルを新規作成するフォームをどのように実装すれば良いか.(MemoCreateViewでの実装は一般的ではないのか)

webアプリケーション初心者です.認識なども間違っている点があると思うのでご指摘いただきたいです.

本プロジェクトのGitHubリポジトリ

プロジェクト構成・ファイル

console

1$ tree django_test_project1 2django_test_project1 3├── config 4│   ├── __init__.py 5│   ├── settings.py 6│   ├── urls.py 7│   └── wsgi.py 8├── db.sqlite3 9├── manage.py 10└── memo 11 ├── __init__.py 12 ├── admin.py 13 ├── apps.py 14 ├── forms.py 15 ├── migrations 16 │   ├── 0001_initial.py 17 │   ├── 0002_memo_created_at.py 18 │   └── __init__.py 19 ├── models.py 20 ├── templates 21 │   ├── memo_create.html 22 │   └── memo_list.html 23 ├── tests.py 24 ├── urls.py 25 └── views.py 26 274 directories, 19 files

python

1# memo/models.py 2 3from django.db import models 4 5class Tag(models.Model): 6 name = models.CharField( 7 verbose_name='タグ名', 8 max_length=127, 9 ) 10 def __str__(self): 11 return self.name 12 13class Memo(models.Model): 14 title = models.CharField( 15 verbose_name='タイトル', 16 max_length=255, 17 ) 18 content = models.TextField( 19 verbose_name='本文', 20 blank=True, 21 null=True, 22 ) 23 tags = models.ManyToManyField( 24 Tag, 25 verbose_name='タグ', 26 blank=True, 27 ) 28 created_at = models.DateTimeField( 29 verbose_name='作成日時', 30 auto_now=True, 31 ) 32 33 def __str__(self): 34 return self.title 35

python

1# memo/urls.py 2 3from django.urls import path 4from . import views 5 6app_name = 'memo' 7urlpatterns = [ 8 path('', views.MemoListView.as_view(), name='memo_list'), 9 path('memo_create/', views.MemoCreateView.as_view(), name='memo_create'), 10] 11

python

1# memo/views.py 2 3from django.views.generic import ListView 4from django.views.generic import CreateView 5from django.urls import reverse_lazy 6 7from .models import Memo 8from .forms import MemoCreateForm 9 10class MemoListView(ListView): 11 model = Memo 12 template_name = 'memo_list.html' 13 14 def get_queryset(self): 15 memo_list = Memo.objects.all().order_by('-created_at') 16 return memo_list 17 18class MemoCreateView(CreateView): 19 model = Memo 20 template_name = 'memo_create.html' 21 form_class = MemoCreateForm 22 success_url = reverse_lazy('memo:memo_list') 23

python

1# memo/forms.py 2 3from django.forms import ModelForm 4from .models import Memo 5 6class MemoCreateForm(ModelForm): 7 class Meta: 8 model = Memo 9 fields = ('title', 'content', 'tags') 10

html

1<!-- memo/templates/memo_list.html --> 2 3<!DOCTYPE html> 4<html lang="ja"> 5<head> 6 <meta charset="UTF-8"> 7 <title>Memo List</title> 8</head> 9<body> 10<a href="{% url 'memo:memo_create' %}">新規メモ作成</a> 11<table border="2"> 12 <thead> 13 <tr> 14 <th>タイトル</th> 15 <th>内容</th> 16 <th>タグ</th> 17 </tr> 18 </thead> 19 <tbody> 20 {% for memo in memo_list %} 21 <tr> 22 <td>{{ memo.title }}</td><td>{{ memo.content }}</td><td>{{ memo.tags }}</td> 23 <!-- 上記だとmemo.tagsはmemo.Tag.Noneと表示されてしまいます. --> 24 </tr> 25 {% endfor %} 26 </tbody> 27</table> 28</body> 29</html> 30

html

1<!-- memo/templates/memo_create.html --> 2 3<!DOCTYPE html> 4<html lang="ja"> 5<head> 6 <meta charset="UTF-8"> 7 <title>Memo Create</title> 8</head> 9<body> 10<form method="post"> 11 <table> 12 {% csrf_token %} 13 {{ form.as_table }} 14 </table> 15 <button type="submit">作成</button> 16</form> 17</body> 18</html> 19

疑問①に関した試したこと

  • memo.tagsをforタグでまわす

memo_list.htmlのなかでmemo.tagsをforタグでまわそうとしたところ,ブラウサにDjangoのエラーが出力されてしまいました.エラーによると「memo.tagsはManyRelatedMnager objectであり,iterableではない」とのことなのですが,ManyRelatedManagerとはなんなのでしょうか.

html

1# memo_list.html 2 3<!-- 略 --> 4 5 <tbody> 6 {% for memo in memo_list %} 7 <tr> 8 <td>{{ memo.title }}</td><td>{{ memo.content }}</td> 9 <td> 10 {% for tag in memo.tags %} 11 {{ tag.name }} 12 {% endfor %} 13 </td> 14 </tr> 15 {% endfor %} 16 </tbody> 17 18<!-- 略 --> 19

browser

1# エラー 2TypeError at / 3'ManyRelatedManager' object is not iterable 4 5Request Method: GET 6Request URL: http://127.0.0.1:8000/ 7Django Version: 2.2.5 8Exception Type: TypeError 9Exception Value: 'ManyRelatedManager' object is not iterable
  • get_querysetメソッドでタグをわたす

MemoListViewクラスのget_querysetメソッドの中でmemoオブジェクトにmemo.tag_listという属性を追加したところ,表示できました.この実装は最も良い方法なのでしょうか.また,select_relatedメソッドはDBの読み込み回数を省略できるとのことだったので,後ろに付けたのですが,使い方は正しいのでしょうか.

python

1# views.py 2 3# 略 4 5class MemoListView(ListView): 6 model = Memo 7 template_name = 'memo_list.html' 8 9 def get_queryset(self): 10 memo_list = Memo.objects.all().order_by('-created_at') 11 for memo in memo_list: 12 memo.tag_list = memo.tags.all().select_related() 13 return memo_list 14 15# 略 16

html

1# memo_list.html 2 3<!-- 略 --> 4 5 <tbody> 6 {% for memo in memo_list %} 7 <tr> 8 <td>{{ memo.title }}</td><td>{{ memo.content }}</td> 9 <td> 10 {% for tag in memo.tag_list %} 11 「{{ tag.name }}」 12 {% endfor %} 13 </td> 14 </tr> 15 {% endfor %} 16 </tbody> 17 18<!-- 略 --> 19

疑問②に関して試したこと

django.views.generic.CreateViewを継承したViewClassでのタグの新規作成フォームの作成方法はわかりませんでした.django.views.generic.Viewを継承したクラスを用いて,自分でformをテンプレートに記述してみましたが,Memoテーブルのタプルの新規作成用のformタグの中で,Tagテーブルのタプルの新規作成用のフォームタグを実装する方法がわかりませんでした.調べたところformタグのネストはできないようです.このような状況の場合の良い実装方法を教えてください.別々に記述するしかないのでしょうか.

python

1# views.py 2 3# 略 4 5class MemoCreateView(View): 6 7 def get(self, request, *args, **kwargs): 8 tag_list = Tag.objects.all() 9 context = { 10 'tag_list': tag_list, 11 } 12 return render(request, 'memo_create.html', context) 13 14 def post(self, request, *args, **kwargs): 15 if request.POST["title"] != "": 16 memo = Memo(title=request.POST['title'], content=request.POST['content']) 17 memo.save() 18 for tag in request.POST.getlist('tags'): 19 memo.tags.add(tag) 20 return redirect(reverse('memo:memo_list')) 21 22# 略 23

html

1<!-- memo_create.html --> 2<!DOCTYPE html> 3<html lang="ja"> 4<head> 5 <meta charset="UTF-8"> 6 <title>Memo Create</title> 7</head> 8<body> 9<form method="post" action="{% url 'memo:memo_create' %}"> 10 <table> 11 {% csrf_token %} 12 <input type="hidden" name="csrfmiddlewaretoken" 13 value="U2W58fChfSjAmFz1J55beZXviW7AOi0tyRD52QHxst7MpfCrRfhRoVnss8mWsVZH" kl_vkbd_parsed="true"> 14 <tbody> 15 <tr> 16 <th><label for="title">タイトル:</label></th> 17 <td><input type="text" name="title" maxlength="255" required="" id="title" kl_vkbd_parsed="true"></td> 18 </tr> 19 <tr> 20 <th><label for="content">本文:</label></th> 21 <td><textarea name="content" cols="40" rows="10" id="content"></textarea></td> 22 </tr> 23 <tr> 24 <th><label for="tags">タグ:</label></th> 25 <td><select name="tags" multiple> 26 {% for tag in tag_list %} 27 <option value="{{ tag.pk }}">{{ tag.name }}</option> 28 {% endfor %} 29 </select></td> 30 </tr> 31 </tbody> 32 </table> 33 <button type="submit">作成</button> 34</form> 35</body> 36</html> 37

バージョン

  • Python 3.7
  • Django 2.2.5
  • python-dotenv 0.15.0

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2021/03/15 12:13

何点か質問の修正依頼があります。 (1)CSRFトークンが直書きされているので消して欲しい github上のmemo/templates/memo_create.htmlと疑問②の当該ソースに <input type="hidden" name="csrfmiddlewaretoken" value="U2W58fChfSjAmFz1J55beZXviW7AOi0tyRD52QHxst7MpfCrRfhRoVnss8mWsVZH" kl_vkbd_parsed="true"> という余計なINPUT要素が含まれてしまっているので消してください。 これは{% csrf_token %}から生成される要素です。 (2)必要なパッケージを記載してください。 pipでinstallが必要なものはきちんと記述してください(python-dotenv==0.15.0)。 すぐ動かせるように.envの例などの記載があった方がいいかもです。 あなたの質問と回答は他の質問者さんも見る可能性がありますので。 (3)githubとの差分だけの記載にしてほしい これは希望であって、依頼ではありません。 githubと質問に記載されているソースの違いが紛らわしく、質問文側は文字数制限もあるので、差分のみ記載にした方がいいかもしれません。 その代わり、github側はpushしたりもすると思うので、URLをリビジョン指定付きにした方がいいかもしれません。 例) https://github.com/tom-takeru/django_test_project1/tree/51d23805c3b3deb8b80f0df098cf9ad7153b5857 以下はヒントです。 疑問①については、memo.tags.allならどうですか? select_relatedはちゃんとDBアクセスを自力で調べられるようになってから使った方がいいかもです。 疑問②については、タグの追加画面でも作ればいいだけです。 方法はいくつかありますが、まずは自力で調べて出来ることで実現可能であることを1つずつ確認していった方が近道だと思います。 関連しますが、疑問も2つまとめてではなく、現象を再現する最も短いコードを2つ書いて、それぞれ別々に質問した方が個人的には好ましいと思います。 申し訳ありませんが、やり取りしてる時間がないので、私からは以上になります。 質問を修正されても、以降私からは回答やコメントができないということです。
tomtakeru

2021/03/16 13:16

質問を読んでくださって、ありがとうございます。とても長くなってしまったのに読んでいただいてありがたい限りです。 修正依頼(1) {% csrf_token %}がそのコードになっていることを知りませんでした。教えていただきありがとうございます。消しました。 修正依頼(2) すみません。不足している情報がありました。確認して追加しておきます。 修正依頼(3) 今後はリビジョン機能を活用したいと思います。 ご指摘ありがとうございます。 ヒントに関して 疑問① ご指摘いただいた方法で解決いたしました。select_related()に関してはDBの仕組みをより深く理解してからもう一度勉強しようと思います。 疑問② とりあえずタグの追加画面を作成して対応しようと思います。 最後のご指摘に関しても、今後はできるだけ質問を簡潔に1つの事柄に関してにするよう気をつけます。 コメントしていただきありがとうございました。
guest

回答1

0

ベストアンサー

質問1について

html

1{% for tag in memo.tags.all %}

です。
ManyToManyFieldの値を参照したい場合は、views.pyとかなら

Python

1tags = memo.tags.all()

で、テンプレート内(HTML上)では、

HTML

1{{memo.tags.all}}

とします(カッコなし)。

質問2について

CreateViewMemoを新規に作るものなので、Tagを作るのはまた別のViewでいいと思います。
django-taggitとか使うと、タグをカンマ区切りで入力して登録できるのですが、無いタグを登録しようとすると新規登録されるのでいいかもしれません。

投稿2021/03/15 21:03

編集2023/07/25 23:13
ForestSeo

総合スコア2722

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

tomtakeru

2021/03/16 14:11

質問を読んでいただき、ありがとうございます。 質問1に関してはご指摘いただいた方法で実装できました。質問2に関しては実現できる方法を探しながら進めていこうと思います。ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問