補足情報(FW/ツールのバージョンなど)
django 2.2.5
Python 3.7.9
前提・実現したいこと
Djangoで英単語の使い分けの学習をサポートするWebアプリケーションを作成しています。現在構想しているのは、以下のように例文と英単語が存在している状態で、両者の間に品詞と日本語が存在している状態のデータベースです。
上の例だと、まず例文として"You made me happy."と"You made for his home."がある上で、英単語のmakeの使い分けを「動詞: 進む」と「動詞: ~にする」として図示している状態になります。
自分は例文と英単語のM:N対応だと考えたので、発行日や難易度などを追加した上で以下のようなモデルを作成しました。
python:
1# models.py 2import datetime 3 4from django.db import models 5from django.utils import timezone 6 7# Create your models here. 8class EnglishWord(models.Model): 9 """ 10 英語 11 """ 12 spell = models.CharField(max_length=50) 13 difficulty = models.IntegerField(default=0) 14 pub_date = models.DateTimeField('date published', default=timezone.now) 15 16 def __str__(self): 17 return self.spell 18 19 def was_published_recently(self): 20 now = timezone.now() 21 return now - datetime.timedelta(days=1) <= self.pub_date <= now 22 23 was_published_recently.admin_order_field = 'pub_date' 24 was_published_recently.boolean = True 25 was_published_recently.short_description = 'Published Recently?' 26 27class ExampleSentence(models.Model): 28 """ 29 例文 30 """ 31 sentence = models.TextField() 32 english_words = models.ManyToManyField( 33 "EnglishWord", 34 through="EnglishWordExampleSentenceRelation", 35 ) 36 difficulty = models.IntegerField(default=0) 37 pub_date = models.DateTimeField('date published', default=timezone.now) 38 39 def __str__(self): 40 return self.sentence 41 42 def was_published_recently(self): 43 now = timezone.now() 44 return now - datetime.timedelta(days=1) <= self.pub_date <= now 45 46 was_published_recently.admin_order_field = 'pub_date' 47 was_published_recently.boolean = True 48 was_published_recently.short_description = 'Published Recently?' 49 50class EnglishWordExampleSentenceRelation(models.Model): 51 """ 52 英語と例文の中間テーブル 53 """ 54 # 品詞 55 PART_OF_SPEECH = ( 56 ("NOU", "名詞"), 57 ("PRO", "代名詞"), 58 ("ADJ", "形容詞"), 59 ("VER", "動詞"), 60 ("ADV", "副詞"), 61 ("PRE", "前置詞"), 62 ("CON", "接続詞"), 63 ("INT", "間投詞") 64 ) 65 66 example_sentence = models.ForeignKey("ExampleSentence", on_delete=models.SET_NULL, related_name='english_word', null=True) 67 english_word = models.ForeignKey("EnglishWord", on_delete=models.SET_NULL, related_name='example_sentence', null=True) 68 part_of_speech = models.CharField(max_length=3, choices=PART_OF_SPEECH) 69 japanese_mean = models.CharField(max_length=50) 70 pub_date = models.DateTimeField('date published', default=timezone.now) 71 difficulty = models.IntegerField(default=0) 72 73 def __str__(self): 74 return f"sentence: {self.example_sentence}, english_word: {self.english_word}" 75 76 def was_published_recently(self): 77 now = timezone.now() 78 return now - datetime.timedelta(days=1) <= self.pub_date <= now 79 80 was_published_recently.admin_order_field = 'pub_date' 81 was_published_recently.boolean = True 82 was_published_recently.short_description = 'Published Recently?'
その上で、admin.pyは以下のような形にしました。
python
1# admin.py 2from django.contrib import admin 3 4from .models import EnglishWord, ExampleSentence 5# Register your models here. 6 7class EnglishWordInline(admin.TabularInline): 8 model = ExampleSentence.english_words.through 9 extra = 3 10 11class ExampleSentenceInline(admin.TabularInline): 12 model = ExampleSentence.english_words.through 13 extra = 3 14 15class EnglishWordAdmin(admin.ModelAdmin): 16 fieldsets = [ 17 (None, {'fields': ['spell',]}), 18 ('Date Information', {'fields': ['pub_date']}), 19 ('Word Data', {'fields': ['difficulty']}) 20 ] 21 inlines = [ExampleSentenceInline] 22 list_display = ('spell', 'pub_date', 'difficulty', 'was_published_recently',) 23 list_filter = ['spell', 'pub_date'] 24 search_fields = ['spell'] 25 actions = ['make_english_words_difficulty_zero'] 26 27 def make_english_words_difficulty_zero(self, request, queryset): 28 for english_word in queryset: 29 english_word.difficulty = 0 30 english_word.save() 31 make_english_words_difficulty_zero.short_description = "英語の難易度を0にリセット" 32 33class ExampleSentenceAdmin(admin.ModelAdmin): 34 fieldsets = [ 35 (None, {'fields': ['sentence']}), 36 ('Date Information', {'fields': ['pub_date']}), 37 ('Sentence Data', {'fields': ['difficulty']}) 38 ] 39 inlines = [EnglishWordInline] 40 exclude = ('english_words',) 41 42admin.site.register(ExampleSentence, ExampleSentenceAdmin) 43admin.site.register(EnglishWord, EnglishWordAdmin)
発生している問題・エラーメッセージ
上のソースコードを配置して管理サイトにログインしてExampleSentenceを新規作成しようとすると、以下のような画面になります。
この画面で下部のプラスボタンを押して、makeを「動詞: ~にする」という意味で登録しようとすると、ExampleSentenceが存在しないので登録できないというエラーが返ってきます。
これから作成しようとしているのでExampleSentenceが無いのは当然なのですが、この仕様のままだと「例文を無単語で登録→あらためて開き直して英単語を登録」という作業を行わなければならず、二重登録も頻発します。
例文を新規登録する際に、英単語の方も同時に登録する方法はないでしょうか。あるいは、そもそも実現したいものに対してモデルの構想が適切ではないのでしょうか。
試したこと
null値を許容するようにすればよいのかと考え、models.py内のEnglishWordExampleSentenceクラスを、
python
1example_sentence = models.ForeignKey("ExampleSentence", on_delete=models.CASCADE, related_name='english_word') 2english_word = models.ForeignKey("EnglishWord", on_delete=models.CASCADE, related_name='example_sentence')
から、
python
1example_sentence = models.ForeignKey("ExampleSentence", on_delete=models.SET_NULL, related_name='english_word', null=True) 2english_word = models.ForeignKey("EnglishWord", on_delete=models.SET_NULL, related_name='example_sentence', null=True)
のように変更しましたが、効果はありませんでした。
改善案等をご存知の方は、お手数をおかけしますが、よろしくお願い致します。
2021/07/01追記
それぞれにnull=True, blank=True
を追加することで、とりあえず存在しない状態でも登録はできるようになりました。ただ、結局二度手間なのは変わっておりません。引き続きご存知の方に対応をお待ちしております。
あなたの回答
tips
プレビュー