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

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

ただいまの
回答率

88.21%

Djangoの会員登録機能実装において、仮登録でメアドとパスワード保存→本登録他の情報を保存するとでIntegrityErrorが出てしまう

受付中

回答 0

投稿 編集

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

future0708

score 0

前提・実現したいこと

DjangoでWebサイトの会員登録機能を作成しようとしています。
コードの参考に以下のサイトを参考にさせていただいています。
https://blog.narito.ninja/detail/38/

実現したいことは、
①会員登録ページからメールアドレス、パスワードを入力する。
②送信ボタンを押すとユーザー情報を保存し、仮登録完了メールを送信する。
③届いたメールに添付されたURLをクリックする。
④開かれたURLのなかで、追加の会員情報として名字、名前、会社名、住所を入力する。
⑤完了ボタンを押して、ユーザー情報を保存し、本登録完了ページへ移行する。
という流れの実装です。

しかし⑥で以下のエラーメッセージが発生しました。
メールアドレス、パスワードを登録した上で、追加で名字、名前、会社名、住所を登録したいと考えているのですが、下記エラーが出てしまいます。解決策を教えていただければ幸いです。
初心者マークをつけ忘れたので一度更新しました。

発生している問題・エラーメッセージ

IntegrityError at user_create/detail/Mze:1k3uvF:CJK6fjLLY4xYk0CaUt1K2qFZqJM/
UNIQUE constraint failed: app_user.email

該当のソースコード

class UserCreateForm(UserCreationForm):
    """ユーザー仮登録用フォーム"""

    class Meta:
        model = User
        fields = ('email',)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs['class'] = 'form-control'

    def clean_email(self):
        email = self.cleaned_data['email']
        User.objects.filter(email=email, is_active=False).delete()
        return email

class UserAddForm(forms.ModelForm):
    """ユーザー本登録用フォーム"""

    class Meta:
        model = User
        fields = ('first_name','last_name','company','address',)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs['class'] = 'form-control'
class UserCreateDetail(generic.CreateView):
    """メール内URLアクセス後のユーザー本登録"""
    model = User
    form_class = UserAddForm
    template_name = 'app/user_create_detail.html'
    timeout_seconds = getattr(settings, 'ACTIVATION_TIMEOUT_SECONDS', 60*60*24) 

    def get(self, request, **kwargs):
        """tokenが正しければ仮登録の情報保存."""
        token = kwargs.get('token')
        try:
            user_pk = loads(token, max_age=self.timeout_seconds)

        # 期限切れ
        except SignatureExpired:
            return HttpResponseBadRequest()

        # tokenが間違っている
        except BadSignature:
            return HttpResponseBadRequest()

        # tokenは問題なし
        else:
            try:
                user = User.objects.get(pk=user_pk)
            except User.DoesNotExist:
                return HttpResponseBadRequest()
            else:
                if not user.is_active:
                    # 問題なければ仮登録の情報保存(本登録はマダ)
                    user.is_active = False
                    user.save()
                    return super().get(request, **kwargs)

        return HttpResponseBadRequest()

    def get_success_url(self):
        return resolve_url('app:user_create_complete', pk=self.kwargs['pk'])

class UserCreateComplete(generic.TemplateView):
    """ユーザー本登録"""
    template_name = 'app/user_create_complete.html'
    timeout_seconds = getattr(settings, 'ACTIVATION_TIMEOUT_SECONDS', 60*60*24)  # デフォルトでは1日以内

    def get(self, request, **kwargs):
        """tokenが正しければ本登録."""
        token = kwargs.get('token')
        try:
            user_pk = loads(token, max_age=self.timeout_seconds)

        # 期限切れ
        except SignatureExpired:
            return HttpResponseBadRequest()

        # tokenが間違っている
        except BadSignature:
            return HttpResponseBadRequest()

        # tokenは問題なし
        else:
            try:
                user = User.objects.get(pk=user_pk)
            except User.DoesNotExist:
                return HttpResponseBadRequest()
            else:
                if not user.is_active:
                    # 問題なければ本登録とする
                    user.is_active = True
                    user.save()
                    return super().get(request, **kwargs)

        return HttpResponseBadRequest()
class User(AbstractBaseUser, PermissionsMixin):
    """カスタムユーザーモデル."""

    email = models.EmailField(_('メールアドレス'), unique=True)
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
    last_name = models.CharField(_('last name'), max_length=150, blank=True)
    company = models.CharField(_('会社名'), max_length=150, blank=True)
    address = models.CharField(_('住所'), max_length=150, blank=True)

    is_staff = models.BooleanField(
        _('staff status'),
        default=False,
        help_text=_(
            'Designates whether the user can log into this admin site.'),
    )
    is_active = models.BooleanField(
        _('active'),
        default=True,
        help_text=_(
            'Designates whether this user should be treated as active. '
            'Unselect this instead of deleting accounts.'
        ),
    )
    date_joined = models.DateTimeField(_('date joined'), default=timezone.now)

    objects = CustomUserManager()
    last_login = models.DateTimeField(_('last_login'), default=timezone.now)
    EMAIL_FIELD = 'email'
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')

    def get_full_name(self):
        """Return the first_name plus the last_name, with a space in
        between."""
        full_name = '%s %s' % (self.first_name, self.last_name)
        return full_name.strip()

    def get_short_name(self):
        """Return the short name for the user."""
        return self.first_name

    def email_user(self, subject, message, from_email=None, **kwargs):
        """Send an email to this user."""
        send_mail(subject, message, from_email, [self.email], **kwargs)

    @property
    def username(self):
        """username属性のゲッター

        他アプリケーションが、username属性にアクセスした場合に備えて定義
        メールアドレスを返す
        """
        return self.email
from django.contrib import admin
from django.urls import path,include
from . import views

app_name = 'app'
urlpatterns = [
~ 省略~
   path('user_create/detail/<token>/', views.UserCreateDetail.as_view(), name='user_create_detail'),
   path('user_create/complete/<int:pk>/', views.UserCreateComplete.as_view(), name='user_create_complete'),
]

試したこと

class UserCreateDetail(generic.CreateView):にある def get_success_url(self):の中身に、
user = form.save(commit=False)
user.is_active = False
user.save()
など、ユーザー情報を保存するための方法を色々試しました。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正の依頼

  • tatamyiwathy

    2020/08/07 17:47

    同じemailがすでに登録されていませんか?

    キャンセル

  • future0708

    2020/08/07 19:00

    質問ありがとうございます。
    確認しsq.lite3も消してみたのですがうまくいきませんでした。

    キャンセル

  • dameo

    2020/08/08 01:33

    色々試すのではなく、100%起きる現象ならデバッガでおっかけて、正確な原因を確かめてください。
    また、デバッグ実行しているならスタックトレースも出ているはずなので、それを貼りましょう。
    正確な原因が分かった上で対策が分からないのであれば、現象を再現できる最小のそれだけで動くコードを作成して、それをベースに質問してください。原因となる部分が掲載されているかどうか分からない、部分コードだけ見せられても、対応してくれる人は稀だと思います。

    キャンセル

  • ForestSeo

    2020/08/08 15:53

    なぜか
    if not user.is_active:
    ⠀⠀⠀# 問題なければ仮登録の情報保存(本登録はマダ)
    ⠀⠀⠀user.is_active = False
    ⠀⠀⠀user.save()
    ⠀⠀⠀return super().get(request, **kwargs)
    で、if not user.is_activeでuser.is_active=Falseにして保存してるのが気になる。

    キャンセル

まだ回答がついていません

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

  • ただいまの回答率 88.21%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

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

  • トップ
  • Pythonに関する質問
  • Djangoの会員登録機能実装において、仮登録でメアドとパスワード保存→本登録他の情報を保存するとでIntegrityErrorが出てしまう