質問をすることでしか得られない、回答やアドバイスがある。

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

ただいまの
回答率

90.52%

  • Python

    11244questions

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

  • Python 3.x

    9365questions

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

  • Django

    1543questions

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

OneToOneFieldの使い方について

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 108

gon000000

score 5

先日、こちらで質問し、また疑問が生まれたので改めて質問しようと思った次第です。

聞きたいこと

現在、modelにOneToOneFieldを持たせています。
しかし、CreateViewによって、別objectとしての新規作成ができてしまいます。
その原因と改善方法が知りたいです。

また、私が実装したい機能に対し、そもそもの書き方が間違っているのではないかという不安もあります。ご意見いただければ幸いです。

実装したいこと

Twitter認証でログインしたユーザが、メモを作成し公開すること。
ただし、メモは一つのみ作成可能。
メモは編集による上書き・削除が可能。

私がこれまで行なったこと

1.social-auth-app-djangoによるtwitterログイン機能の実装
こちらを参考にし、それ以外に何も記述はしていません。
twitterログイン以外のログイン機能はつけないので、他にUserモデルは必要ないという認識をしています。(ここが間違いなのでしょうか)

2.モデルの作成
ユーザとモデルを一対一の関係になるようにしているつもりです。

import uuid
from django.contrib.auth.models import User
from django.db import models

class Memo(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    title = models.CharField(max_length=20)
    content = models.TextField(max_length=5000)
    image = models.ImageField(upload_to=get_memo_image_path, null=True, blank=True)
    posted_date = models.DateTimeField(auto_now=True)

3.View作成

#views.py
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse_lazy
from django.views.generic import TemplateView, DetailView, CreateView, UpdateView, DeleteView
from .forms import MemoForm
from .models import Memo


class IndexView(TemplateView):
    template_name = 'index.html'


class MemoDetailView(DetailView):
    model = Memo


class MemoCreateView(LoginRequiredMixin, CreateView):
    model = Memo
    form_class = MemoForm

    def get_success_url(self):
        return reverse_lazy('detail', kwargs={'pk': Memo.id})

    login_url = '/login'


class MemoUpdateView(LoginRequiredMixin, UpdateView):
    model = Memo
    form_class = MemoForm
    login_url = '/login'

    def get_success_url(self):
        url = reverse_lazy("detail", kwargs={"pk": Memo.id})
        return url


class MemoDeleteView(LoginRequiredMixin, DeleteView):
    model = Memo
    success_url = reverse_lazy("index")
    login_url = '/login'

余談

質問に関連する余談ですが、model.pyでのuuidをprimaryKeyとしたurlにアクセスできないのです。
これはどういうことなのでしょうか?(別問題でしたら改めて質問します)

#url.py
urlpatterns = [
    path('', IndexView.as_view(), name="index"),
    path('<uuid:pk>', MemoDetailView.as_view(), name="detail"),
    path('<uuid:pk>/update', MemoUpdateView.as_view(), name="update"),
    path('<uuid:pk>/delete', MemoDeleteView.as_view(), name="delete"),

    path('create/', MemoCreateView.as_view(), name="create"),
    path('admin/', admin.site.urls),
    path('', include('social_django.urls', namespace='social')),
]


上記view.pyのCreateViewにてurlを作成しているイメージです。
そこでhtmlは、

<a class="btn btn-outline-primary" href="{% url 'detail' object.id %}">確認する</a>


と、記述すれど

NoReverseMatch at /create/
Reverse for 'detail' with keyword arguments '{'pk': <django.db.models.query_utils.DeferredAttribute object at 0x10e83d860>}' not found. 1 pattern(s) tried: ['(?P<pk>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$']


このようなエラーメッセージがでます。

以上になります。
そもそも論でも構いません。ご教授お願いします。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

現在、modelにOneToOneFieldを持たせています。
しかし、CreateViewによって、別objectとしての新規作成ができてしまいます。
その原因と改善方法が知りたいです。

これは、同一のユーザで CreateView を複数回使ってもエラーが出ずにいくつも Memo レコードが作れてしまう、という状況だと理解しましたが、この理解で正しいですか?

MemoForm の実装を見せていただかないと正確にはわかりませんが、パッと見の印象では Memo model の user フィールドを null=True とされているので user フィールドが空の memo オブジェクトが複数作れてしまうから、ではないでしょうか。

改善方法については、まず null=True を辞めて、あとは MemoCreateView のところで、同一のユーザに対して複数の memo が作れないようにブロックするとよいのではないでしょうか。アプローチはいろいろあると思いますが、基本は「対象のユーザに対して memo が存在するかどうかをチェックして、存在するときはフォームが送信できない・送信を受け付けないようにする」になるかと思います。

(余談については別問題だと思いますので、この回答ではメインの問題の解消に専念します)

追記 1

上記エラーについてご意見をいただけないでしょうか?

そうですね、 OneToOneField を使っているときに「 NOT NULL constraint failed 」というエラーが出た場合の解決策は、必ずしも null=True とすることではありません。

おそらく今回の場合は MemoCreateView の中で memo オブジェクトを永続化するときに user フィールドを適切にセットされていないために起こっているのではないかと思いますが、満たすべき仕様と問題の実態にあわせて適切な解決策を採る必要があるものと思います。

ちなみに、 model object の関連フィールドにカレントユーザをセットするには、次のページで紹介されているようなコードを記述する必要があります(あくまでもいちサンプルです)。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/04/10 18:27

    回答ありがとうございます。

    >これは、同一のユーザで CreateView を複数回使ってもエラーが出ずにいくつも Memo レコードが作れてしまう、という状況だと理解しましたが、この理解で正しいですか?

    はい、その通りです。
    また、userフィールドにnull=Trueとした理由としては、エラー文で、
    IntegrityError at /create/
    NOT NULL constraint failed: memo_memo.user_id
    が発生し、こちら(http://nihaoshijie.hatenadiary.jp/entry/2014/06/12/090008)を参考にしたことからです。そしてnull=Trueを指定すると余談部分のエラーが発生するという状態です。
    今後の方針としては、アドバイス通りCreateViewを変更する予定ですが、上記エラーについてご意見をいただけないでしょうか?

    キャンセル

  • 2019/04/10 21:27

    > 上記エラーについてご意見をいただけないでしょうか?

    追記で回答させていただきました。ご覧になってみてください :D

    キャンセル

  • 2019/04/14 17:55

    ありがとうございます。
    その後別のエラーも出てしまい、戸惑いましたが、余談を含め全て解決しました。
    これから精進します。

    キャンセル

  • 2019/04/15 19:59

    そうでしたか。それはよかったです!ぜひがんばってください :D

    キャンセル

0

CreateViewで作成したデータ(detailview)にすぐにリンクで飛ばそうとする場合、views.pyファイルでreverse_lazyを使うんのではなく、models.pyファイルの中でget_absolute_urlを使わないといけないようです。

models.pyファイルのMemoに
def get_absolute_url(self):
reverse('detail', kwargs={'pk':object.pk})
と書くとうまくいくかと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/04/10 13:49

    ありがとうございます。
    いただいた回答を参考に試行錯誤しましたが、エラーの解決には至りませんでした。
    url部分についてはまた別途考えようと思います。(モデル関連のエラーだと考えているため)

    キャンセル

同じタグがついた質問を見る

  • Python

    11244questions

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

  • Python 3.x

    9365questions

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

  • Django

    1543questions

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