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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Django

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

Python 3.x

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

Q&A

1回答

576閲覧

Django 会員登録フォームに2種類のモデルのデータを表示したい

topy

総合スコア12

Django

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

Python 3.x

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

0グッド

0クリップ

投稿2019/03/14 03:08

編集2019/03/15 06:51

前提・実現したいこと

ユーザー登録画面(/signup)に、下記の情報を表示させたいです。

・Userモデル定義したemail(unique=True)、password
・UserモデルとOneToOneでリレーションしている「Customerモデル」の情報(nickname、sex、birthday、profileImg)

なお、アプリはUserとCustomerで分けて作成しています。

現状は下記のエラーが出ている状況です。

type object 'Customer' has no attribute 'USERNAME_FIELD'

過去にhttps://teratail.com/questions/101936の投稿があり、直接リレーション先のオブジェクトを扱えるわけではない、とのことでしたが、どこを修正すれば/signupに全てのカラムを表示できるでしょうか。

forms.py、template、models.py、views.pyは下記のように記載している状況です。

forms

1from django import forms 2from django.core.mail import send_mail 3from django.conf import settings 4from .models import Customer 5from django.contrib.auth import get_user_model 6from django.contrib.auth.forms import AuthenticationForm, UserCreationForm 7 8User = get_user_model() 9 10class UserCreateForm(UserCreationForm): 11 """ユーザー登録用フォーム""" 12 13 class Meta: 14 model = Customer 15 fields = '__all__' 16 17 def __init__(self, *args, **kwargs): 18 super().__init__(*args, **kwargs) 19 for field in self.fields.values(): 20 field.widget.attrs['class'] = 'form-control'

signuphtml

1<form action="" method="POST"> 2 {% csrf_token %} 3 <div class="formStyle__itemBox"> 4 {{ form.user.email.label_tag }} 5 {{ form.user.email }} 6 {{ form.user.email.help_text }} 7 {{ form.user.email.errors }} 8 </div> 9 <div class="formStyle__itemBox"> 10 {{ form.user.password.label_tag }} 11 {{ form.user.password }} 12 {{ form.user.password.help_text }} 13 {{ form.user.password.errors }} 14 </div> 15 <div class="formStyle__itemBox"> 16 {{ form.nick_name.label_tag }} 17 {{ form.nick_name }} 18 {{ form.nick_name.help_text }} 19 {{ form.nick_name.errors }} 20 </div> 21 <div class="formStyle__itemBox"> 22 {{ form.sex.label_tag }} 23 {{ form.sex }} 24 {{ form.sex.help_text }} 25 {{ form.sex.errors }} 26 </div> 27 <div class="formStyle__itemBox"> 28 {{ form.birthday.label_tag }} 29 {{ form.birthday }} 30 {{ form.birthday.help_text }} 31 {{ form.birthday.errors }} 32 </div> 33 <div class="formStyle__itemBox"> 34 {{ form.profile_img.label_tag }} 35 {{ form.profile_img }} 36 {{ form.profile_img.help_text }} 37 {{ form.profile_img.errors }} 38 </div> 39 <button type="submit" class="btn btn-primary btn-lg">送信</button> 40</form>

Customermodels

1from django.db import models 2from django.contrib.auth import get_user_model 3from django.utils.translation import ugettext_lazy as _ 4 5User = get_user_model() 6 7class Customer(models.Model): 8 9 class Meta: 10 verbose_name = verbose_name_plural = '00. カスタマー' 11 12 user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='customer') 13 14 SEX = ( 15 ('man', '男性'), 16 ('woman', '女性'), 17 ('transgender', 'その他'), 18 ) 19 20 nick_name = models.CharField(_('ニックネーム'), max_length=64) 21 sex = models.CharField(_('性別'), max_length=12, choices=SEX) 22 birthday = models.DateField(_('生年月日'), null=True) 23 profile_img = models.ImageField(_('プロフィール画像'), upload_to='uploads/', blank=True) 24 25 def __str__(self): 26 return self.user.email

Usermodels

1from django.db import models 2from django.core.mail import send_mail 3from django.contrib.auth import get_user_model 4from django.contrib.auth.models import PermissionsMixin 5from django.contrib.auth.models import AbstractBaseUser, BaseUserManager 6from django.utils import timezone 7from django.utils.translation import ugettext_lazy as _ 8 9 10class UserManager(BaseUserManager): 11 12 use_in_migrations = True 13 14 def _create_user(self, email, password, **extra_fields): 15 if not email: 16 raise ValueError('メールアドレスが入力されていません') 17 email = self.normalize_email(email) 18 19 user = self.model(email=email, **extra_fields) 20 user.set_password(password) 21 user.save(using=self._db) 22 return user 23 24 def create_user(self, email, password=None, **extra_fields): 25 extra_fields.setdefault('is_staff', False) 26 extra_fields.setdefault('is_superuser', False) 27 return self._create_user(email, password, **extra_fields) 28 29 def create_superuser(self, email, password, **extra_fields): 30 extra_fields.setdefault('is_staff', True) 31 extra_fields.setdefault('is_superuser', True) 32 33 if extra_fields.get('is_staff') is not True: 34 raise ValueError('スーパーユーザーはis_staff=Trueが必須です') 35 if extra_fields.get('is_superuser') is not True: 36 raise ValueError('スーパーユーザーはis_superuser=Trueが必須です') 37 38 return self._create_user(email, password, **extra_fields) 39 40 41class User(AbstractBaseUser, PermissionsMixin): 42 class Meta: 43 verbose_name = verbose_name_plural = '00. ユーザー' 44 45 email = models.EmailField(_('メールアドレス'), max_length=255, unique=True, null=True) 46 created = models.DateTimeField(_('作成日時'), auto_now_add=True) 47 updated = models.DateTimeField(_('更新日時'), auto_now=True) 48 49 is_staff = models.BooleanField(_('staff status'), default=False) 50 is_active = models.BooleanField(_('有効フラグ'), default=True) 51 52 date_joined = models.DateTimeField(_('date joined'), default=timezone.now) 53 54 objects = UserManager() 55 USERNAME_FIELD = 'email' 56 57 def __str__(self): 58 return self.email 59 60 def email_user(self, subject, message, from_email=None, **kwargs): 61 """Send an email to this user.""" 62 send_mail(subject, message, from_email, [self.email], **kwargs) 63 64 @property 65 def username(self): 66 return self.email 67 68 @property 69 def is_owner(self): 70 return hasattr(self, 'owner')

views

1# 会員登録 2class SignUpCreateView(generic.CreateView): 3 """ユーザー仮登録""" 4 template_name = 'customer/signup.html' 5 form_class = UserCreateForm 6 model = Customer 7 8 def get_context_data(self, **kwargs): 9 context = super(SignUpCreateView, self).get_context_data(**kwargs) 10 11 context['user'] = User.objects.all() 12 13 return context 14 15 def form_valid(self, form): 16 """仮登録と本登録用メールの発行.""" 17 # 仮登録と本登録の切り替えは、is_active属性を使うと簡単です。 18 # 退会処理も、is_activeをFalseにするだけにしておくと捗ります。 19 user = form.save(commit=False) 20 user.is_active = False 21 user.save() 22 23 # アクティベーションURLの送付 24 current_site = get_current_site(self.request) 25 domain = current_site.domain 26 context = { 27 'protocol': self.request.scheme, 28 'domain': domain, 29 'token': dumps(user.pk), 30 'user': user, 31 } 32 33 subject_template = get_template('customer/mail_template/create/subject.txt') 34 subject = subject_template.render(context) 35 36 message_template = get_template('customer/mail_template/create/message.txt') 37 message = message_template.render(context) 38 39 user.email_user(subject, message) 40 return redirect('customer:signup_done') 41 42 43class SignUpDoneTemplateView(generic.TemplateView): 44 """ユーザー仮登録""" 45 template_name = 'customer/signup_done.html' 46 47 48class SignUpCompleteTemplateView(generic.TemplateView): 49 """本登録用に発行したメール内のURLアクセス後のユーザー本登録""" 50 template_name = 'customer/signup_complete.html' 51 timeout_seconds = getattr(settings, 'ACTIVATION_TIMEOUT_SECONDS', 60*60*24) # デフォルトでは1日以内 52 53 def get(self, request, **kwargs): 54 """tokenが正しければ本登録.""" 55 token = kwargs.get('token') 56 try: 57 user_pk = loads(token, max_age=self.timeout_seconds) 58 59 # 期限切れ 60 except SignatureExpired: 61 return HttpResponseBadRequest() 62 63 # tokenが間違っている 64 except BadSignature: 65 return HttpResponseBadRequest() 66 67 # tokenは問題なし 68 else: 69 try: 70 user = User.objects.get(pk=user_pk) 71 customer = Customer() 72 customer.user = User.objects.get(pk=user_pk) 73 customer.save() 74 except User.DoesNotExist: 75 return HttpResponseBadRequest() 76 else: 77 if not user.is_active: 78 # 問題なければ本登録とする 79 user.is_active = True 80 user.save() 81 return super().get(request, **kwargs) 82 83 return HttpResponseBadRequest()

補足情報(FW/ツールのバージョンなど)

Python:3.7
Django:2.1.2

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

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

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

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

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

guest

回答1

0

OneToOnefieldは、逆参照できますので、ドキュメントを確認なさるといいです。

ForeignKeyを逆参照する時にはN+1問題などの無駄なSQLクエリ発行があったりします。

投稿2021/02/22 13:09

prof

総合スコア179

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問