KeyError: 'instance': send_activation_notification(kwargs['instance']) - python

I am trying to implement registration in Django and faced with error:
models.py", line 20, in user_registrated_dispatcher
send_activation_notification(kwargs['instance'])
KeyError: 'instance'
I submitted registration form then received this error message.
How can I solve the error?
Could you recommend good tutorial about registration with email in Django?
Full code of the application located here (branch Login-version-2-site ): https://github.com/mascai/reg_app/tree/Login-version-2-site
models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.dispatch import Signal
from .utilities import send_activation_notification
class AdvUser(AbstractUser):
is_activated = models.BooleanField(default=True, db_index=True,
verbose_name='Пpoшeл активацию?')
send_messages = models.BooleanField(default=True, verbose_name='Слать оповещения о новых комментариях?')
class Meta(AbstractUser.Meta):
pass
user_registrated = Signal(providing_args=['instance'])
def user_registrated_dispatcher(sender, **kwargs):
send_activation_notification(kwargs['instance'])
user_registrated.connect(user_registrated_dispatcher)
forms.py
class RegisterUserForm(forms.ModelForm):
email = forms.EmailField(required=True, label="Email address")
password1 = forms.CharField(label="Password", widget=PasswordInput,
help_text=password_validation.password_validators_help_text_html())
password2 = forms.CharField(label="Password", widget=PasswordInput,
help_text=password_validation.password_validators_help_text_html())
def clean_password1(self):
password1 = self.cleaned_data['password1']
if password1:
password_validation.validate_password(password1)
return password1
def clean(self):
super().clean()
password1 = self.cleaned_data.get('password1')
password2 = self.cleaned_data.get('password2')
if password1 and password2 and password1 != password2:
errors = {'password2': ValidationError('Введённые пароли не совпадают', code='password_mismatch')}
raise ValidationError(errors)
else:
return self.cleaned_data
def save(self, commit=True):
user = super().save(commit=False)
user.set_password(self.cleaned_data['password1'])
user.is_active = False
user.is_activated = False
if commit:
user.save()
user_registrated.send(RegisterUserForm, isinstance=user)
return user
class Meta:
model = AdvUser
fields = ('username', 'email', 'password', 'password2',
'first_name', 'last_name', 'send_messages')
urls.py
from .views import BBLoginView, BBLogoutView, profile, RegisterUserView, RegisterDoneView, user_activate
urlpatterns = [
path('', views.post_list, name='post_list'),
path('accounts/login/', BBLoginView.as_view(), name='login'),
path('accounts/profile/', profile, name='profile'),
path('accounts/logout/', BBLogoutView.as_view(), name='logout'),
path('accounts/register/done', RegisterDoneView.as_view(), name='register_done'),
path('accounts/register/', RegisterUserView.as_view(), name='register'),
path('accounts/register/activate/<str:sign>/', user_activate, name='register_activate'),
]
views.py
def post_list(request):
return render(request, 'blog/post_list.html', {})
class BBLoginView(LoginView):
template_name = 'blog/login.html'
class BBLogoutView(LoginRequiredMixin, LogoutView):
template_name = 'blog/logout.html'
#login_required
def profile(request):
return render(request, 'blog/profile.html')
class RegisterUserView(CreateView):
model = AdvUser
template_name = 'blog/register_user.html'
form_class = RegisterUserForm
success_url = '/register_done'
class RegisterDoneView(TemplateView):
template_name = "blog/register_done.html"
def user_activate(request, sign):
try:
username = signer.unsign(sign)
except:
return render(request, 'blog/bad_signature.html')
user = get_object_or_404(AdvUser, username=username)
if user.is_activated:
template = 'blog/user_is_activated.html'
else:
template = 'blog/activation_done.html'
user.is_active = True
user.is_activated = True
user.save()
return render(request, template)

You passed isinstance=user instead of instance=user when you send the signal inside the save method.
(Note, all this seems like a lot of work. There's already a general post_save signal you could use; but actually there doesn't seem to be much reason to use a signal at all. Why not just send the notification directly?

Related

How can I save the username in the database as an email?>

I want a signup page with 3 fields (email, password and repeat password). My goal is that when the user enters the email address, it is also saved in the database as a username. I would be super happy if someone could help me, I've been sitting for x hours trying to solve this problem. Thanks very much!
model.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
email_confirmed = models.BooleanField(default=False)
#receiver(post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
forms.py
class CreateUserForm(UserCreationForm):
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
# Sign Up Form
class SignUpForm(UserCreationForm):
# first_name = forms.CharField(max_length=30, required=False, help_text='Optional')
# last_name = forms.CharField(max_length=30, required=False, help_text='Optional')
email = forms.EmailField(max_length=254, help_text='Enter a valid email address')
class Meta:
model = User
fields = [
'username',
'password1',
'password2',
]
views.py
from django.contrib import messages
from django.contrib.auth.models import Group
from django.contrib.sites.shortcuts import get_current_site
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
from django.template.loader import render_to_string
from .token import AccountActivationTokenGenerator, account_activation_token
from django.shortcuts import render, redirect
from .forms import *
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth import get_user_model, login
from django.utils.http import urlsafe_base64_decode
from django.views.generic import View, UpdateView
from django.contrib.auth.decorators import login_required
from .decorators import *
from django.urls import reverse_lazy
from django.utils.encoding import force_str
#unauthenticatedUser
def Example_login(request):
if request.method == 'POST':
email = request.POST.get('email')
password = request.POST.get('password')
user = authenticate(request, username=email, password=password)
if user is not None:
login(request, user)
return redirect('Example_dashboard')
else:
messages.info(request, 'Username OR password is incorrecct')
context = {}
return render(request, 'accounds/templates/Example_login.html', context)
def reset_passwrd(request):
return render(request, "reset_password.html")
#login_required(login_url='login')
def Example_dashboard(request):
form = MembersForm()
current_user = request.user
name = current_user.username.split(".")[0]
context = {'form': form, "cunrrent_user": name}
return render(request, 'example_dashboard.html', context)
def Login(request):
if request.method == 'POST':
email = request.POST.get('Benutzername')
password = request.POST.get('Passwort')
user = authenticate(request, username=email, password=password)
if user is not None:
login(request, user)
return redirect('Example_dashboard')
else:
messages.info(request, 'Username OR password is incorrecct')
return render(request, "login.html")
def logoutUser(request):
logout(request)
return redirect('login')
def registrierung(request):
return render(request, "registrierung.html")
#unauthenticatedUser
def Example_register(request):
form = CreateUserForm()
if request.method == 'POST':
form = CreateUserForm(request.POST)
if form.is_valid():
user = form.save()
#username = form.cleaned_data.get('usernname')
group = Group.objects.get(name='studends')
user.groups.add(group)
messages.success(request, 'Account was created' )
return redirect('login')
contex = {'form' : form}
return render(request, 'exampl_register.html',contex)
# Sign Up View
class SignUpView(View):
form_class = SignUpForm
template_name = 'signup.html'
def get(self, request, *args, **kwargs):
form = self.form_class()
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
user = form.save(commit=False)
user.is_active = False # Deactivate account till it is confirmed
user.save()
current_site = get_current_site(request)
subject = 'Activate Your MySite Account'
message = render_to_string('account_activation_email.html', {
'user': user,
'domain': current_site.domain,
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'token': account_activation_token.make_token(user),
})
user.email_user(subject, message)
messages.success(request, ('Please Confirm your email to complete registration.'))
return redirect('login')
return render(request, self.template_name, {'form': form})
class ActivateAccount(View):
def get(self, request, uidb64, token, *args, **kwargs):
try:
uid = force_str(urlsafe_base64_decode(uidb64))
user = User.objects.get(pk=uid)
except (TypeError, ValueError, OverflowError, User.DoesNotExist):
user = None
if user is not None and account_activation_token.check_token(user, token):
user.is_active = True
user.profile.email_confirmed = True
user.save()
login(request, user)
messages.success(request, ('Your account have been confirmed.'))
return redirect('login')
else:
messages.warning(request, ('The confirmation link was invalid, possibly because it has already been used.'))
return redirect('login')
I Need your help
If you want to use email instead of the default username, you have to overwrite the default User model with the custom one
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin
class User(AbstractBaseUser, PermissionsMixin):
# Use the email for logging in
email = models.EmailField(max_length=254, unique=True)
USERNAME_FIELD = 'email'

Django Login with Email or Phone Number

I have been trying to find a solution of a problem in Django for a very long time. The problem is I am trying to develop a login system than can use either email or phone number to authenticate user.
Well, that can be done using by creating a custom user model . I have tested this. It works.
First steps
The first thing you need to do is create a new Django project. Make sure you don't run migrations because there are still a few things we need to do before then.
After creating your new Django project, create a new app called accounts with the following command:
python manage.py startapp accounts
Creating the User Model
By default, the User model provided by Django has a username field, and an email field. However, we also need a phone number field. In order to add this field, we need to extend the Django user model. In the accounts app's models.py file, type in the following code:
models.py
phone_validator = RegexValidator(r"^(\+?\d{0,4})?\s?-?\s?(\(?\d{3}\)?)\s?-?\s?(\(?\d{3}\)?)\s?-?\s?(\(?\d{4}\)?)?$", "The phone number provided is invalid")
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=100, unique=True)
phone_number = models.CharField(max_length=16, validators=[phone_validator], unique=True)
full_name = models.CharField(max_length=30)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
# is_translator = models.BooleanField(default=False)
objects = CustomUserManager()
USERNAME_FIELD = 'phone_number'
REQUIRED_FIELDS = ['email', 'full_name']
def __str__(self):
return self.email
#staticmethod
def has_perm(perm, obj=None, **kwargs):
return True
#staticmethod
def has_module_perms(app_label, **kwargs):
return True
#property
def is_staff(self):
return self.is_admin
Register the model with the admin
admin.py
class UserAdmin(BaseUserAdmin):
form = UserChangeForm
add_form = UserCreationForm
list_display = ('email', 'phone_number', 'full_name', 'is_active', 'is_admin')
list_filter = ('is_active', 'is_admin')
fieldsets = (
(None, {'fields': ('full_name', 'email', 'phone_number', 'password')}),
('Permissions', {'fields': ('is_active', 'is_admin', 'is_superuser', 'last_login', 'groups', 'user_permissions')}),
)
add_fieldsets = (
(None, {'fields': ('full_name', 'phone_number', 'email', 'password1', 'password2')}),
)
search_fields = ('email', 'full_name')
ordering = ('email',)
filter_horizontal = ('groups', 'user_permissions')
def get_form(self, request, obj=None, **kwargs):
form = super().get_form(request, obj, **kwargs)
is_superuser = request.user.is_superuser
if is_superuser:
form.base_fields['is_superuser'].disabled = True
return form
admin.site.register(User, UserAdmin)
forms.py
class UserLoginForm(forms.Form):
email = forms.CharField(max_length=50)
password = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'form-control'}))
for login costumer in login.html
views.py
import random
from .backends import EmailPhoneUsernameAuthenticationBackend as EoP
class UserLoginView(View):
form_class = UserLoginForm
template_name = 'accounts/login.html'
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated:
return redirect('core:home')
return super().dispatch(request, *args, **kwargs)
def get(self, request):
form = self.form_class
return render(request, self.template_name, {'form': form})
def post(self, request):
form = self.form_class(request.POST)
if form.is_valid():
cd = form.cleaned_data
user = EoP.authenticate(request, username=cd['email'], password=cd['password'])
if user is not None:
login(request, user)
messages.success(request, 'You have successfully logged in!', 'success')
return redirect('core:home')
else:
messages.error(request, 'Your email or password is incorrect!', 'danger')
return render(request, self.template_name, {'form': form})
Writing a Custom Backend
backends.py
from django.contrib.auth.hashers import check_password
from django.contrib.auth import get_user_model
from django.db.models import Q
User = get_user_model()
class EmailPhoneUsernameAuthenticationBackend(object):
#staticmethod
def authenticate(request, username=None, password=None):
try:
user = User.objects.get(
Q(phone_number=username) | Q(email=username)
)
except User.DoesNotExist:
return None
if user and check_password(password, user.password):
return user
return None
#staticmethod
def get_user(user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
Update the settings (3 Options)
settings.py
INSTALLED_APPS = [
...
# Third-party apps
'accounts.apps.AccountsConfig',
...
]
AUTH_USER_MODEL = 'accounts.User'
AUTHENTICATION_BACKENDS = [
'accounts.backends.EmailPhoneUsernameAuthenticationBackend'
]
I hope your problem will be solved and others will use it.
Well that can be done using by creating a custom user model . This is the only way known to me to achieve the result.
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
class MyAccountManager(BaseUserManager):
def create_user(self, email, username, password=None):
if not email:
raise ValueError('Users must have an email address')
if not username:
raise ValueError('Users must have a username')
user = self.model(
email=self.normalize_email(email),
username=username,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self, email, username, password):
user = self.create_user(
email=self.normalize_email(email),
password=password,
username=username,
)
user.is_admin = True
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
def get_profile_image_filepath(self , filepath):
return 'profile_images/' + str(self.pk) + '/profile_image.png'
def get_default_profile_image():
return "dummy_image.png"
class Account(AbstractBaseUser):
email = models.EmailField(verbose_name="email", max_length=60, unique=True)
username = models.CharField(max_length=30, unique=True)
date_joined = models.DateTimeField(verbose_name='date joined', auto_now_add=True)
last_login = models.DateTimeField(verbose_name='last login', auto_now=True)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
profile_image = models.ImageField(max_length=255,
upload_to=get_profile_image_filepath, null=True, blank=True, default=get_default_profile_image)
hide_email = models.BooleanField(default=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
objects = MyAccountManager()
def __str__(self):
return self.username
def get_profile_image_filename(self):
return str(self.profile_image)[str(self.profile_image).index('profile_images/' + str(self.pk) + "/"):]
# For checking permissions. to keep it simple all admin have ALL permissons
def has_perm(self, perm, obj=None):
return self.is_admin
# Does this user have permission to view this app? (ALWAYS YES FOR SIMPLICITY)
def has_module_perms(self, app_label):
return True
This is the custom user model and this will login via the email of the user .
You have to define in the settings.py for the user auth model .
In settings.py add this
AUTH_USER_MODEL = "user_app.Account"
replace the "user_app" with the app in whose models.py this model is stored .
And also you need to tell to the admin to do so .
go to admin.py of the app and add this .
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from account.models import Account
class AccountAdmin(UserAdmin) :
list_display = ("email" , "username" , "date_joined" , "last_login" , "is_admin" , "is_staff")
search_fields = ("email" , "username")
readonly_fields = ("id" , "date_joined" , "last_login")
filter_horizontal = ()
list_filter = ()
fieldsets = ()
admin.site.register(Account , AccountAdmin)
Another way of achieving the result is to take email or phone number credential and password from the user and then in the views find out the username and then log the user in..
Create a form and then create a save method to do this , or diectly do it in the views.
Like this :
def loginView(request) :
if request.POST :
email = request.POST.get("email")
password = request.POST.get("password")
user = User.objects.get(email = email)
if user.check_password(password) :
login(request, user)
return redirect("home")

KeyError: 'password1': password1 = self.cleaned_data['password1']

I am trying to implement registration in Django and faced with error:
forms.py line 17, in clean_password
KeyError: 'password1': password1 = self.cleaned_data['password1']
How can I solve the error?
Full code of the application located here (branch Login-version-2-site ): https://github.com/mascai/reg_app/tree/Login-version-2-site
models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.dispatch import Signal
from .utilities import send_activation_notification
class AdvUser(AbstractUser):
is_activated = models.BooleanField(default=True, db_index=True,
verbose_name='Пpoшeл активацию?')
send_messages = models.BooleanField(default=True, verbose_name='Слать оповещения о новых комментариях?')
class Meta(AbstractUser.Meta):
pass
user_registrated = Signal(providing_args=['instance'])
def user_registrated_dispatcher(sender, **kwargs):
send_activation_notification(kwargs['instance'])
user_registrated.connect(user_registrated_dispatcher)
forms.py
class RegisterUserForm(forms.ModelForm):
email = forms.EmailField(required=True, label="Email address")
password1 = forms.CharField(label="Password", widget=PasswordInput,
help_text=password_validation.password_validators_help_text_html())
password2 = forms.CharField(label="Password", widget=PasswordInput,
help_text=password_validation.password_validators_help_text_html())
def clean_password(self):
password1 = self.cleaned_data['password1']
if password1:
password_validation.validate_password(password1)
return password1
def clean(self):
super().clean()
password1 = self.cleaned_data.get('password1')
password2 = self.cleaned_data.get('password2')
if password1 and password2 and password1 != password2:
errors = {'password2': ValidationError('Введённые пароли не совпадают', code='password_mismatch')}
raise ValidationError(errors)
else:
return self.cleaned_data
def save(self, commit=True):
user = super().save(commit=False)
user.set_password(self.cleaned_data['password1'])
user.is_active = False
user.is_activated = False
if commit:
user.save()
user_registrated.send(RegisterUserForm, isinstance=user)
return user
class Meta:
model = AdvUser
fields = ('username', 'email', 'password', 'password2',
'first_name', 'last_name', 'send_messages')
urls.py
from .views import BBLoginView, BBLogoutView, profile, RegisterUserView, RegisterDoneView, user_activate
urlpatterns = [
path('', views.post_list, name='post_list'),
path('accounts/login/', BBLoginView.as_view(), name='login'),
path('accounts/profile/', profile, name='profile'),
path('accounts/logout/', BBLogoutView.as_view(), name='logout'),
path('accounts/register/done', RegisterDoneView.as_view(), name='register_done'),
path('accounts/register/', RegisterUserView.as_view(), name='register'),
path('accounts/register/activate/<str:sign>/', user_activate, name='register_activate'),
]
views.py
def post_list(request):
return render(request, 'blog/post_list.html', {})
class BBLoginView(LoginView):
template_name = 'blog/login.html'
class BBLogoutView(LoginRequiredMixin, LogoutView):
template_name = 'blog/logout.html'
#login_required
def profile(request):
return render(request, 'blog/profile.html')
class RegisterUserView(CreateView):
model = AdvUser
template_name = 'blog/register_user.html'
form_class = RegisterUserForm
success_url = '/register_done'
class RegisterDoneView(TemplateView):
template_name = "blog/register_done.html"
def user_activate(request, sign):
try:
username = signer.unsign(sign)
except:
return render(request, 'blog/bad_signature.html')
user = get_object_or_404(AdvUser, username=username)
if user.is_activated:
template = 'blog/user_is_activated.html'
else:
template = 'blog/activation_done.html'
user.is_active = True
user.is_activated = True
user.save()
return render(request, template)
In a Django form every clean_<field_name> can safely handle only <field_name>, there is no guarantee that another field is available in cleaned_data at that point, it's available only if it has been already cleaned.
Since what you want to do is to clean password1, rename clean_password to clean_password1.

Django + Angular 405 Method not allowed thinkster.io tutorial

I am going through thinkster.io tutorial about Django and AngularJS. I am about half way of tutorial, but I am getting the error 405 Method not allowed for my POST request of user registration. I tried all sugestions but none of them seems to work ( I also tried to copying whole code from tutorial ). Here is my code:
views.py
from django.shortcuts import render
from rest_framework import permissions, viewsets
from authentication.models import Account
from authentication.permissions import IsAccountOwner
from authentication.serializers import AccountSerializer
class AccountViewSet(viewsets.ModelViewSet):
lookup_field = 'username'
queryset = Account.objects.all()
serializer_class = AccountSerializer
def get_permissions(self):
if self.request.method in permissions.SAFE_METHODS:
return (permissions.AllowAny(),)
if self.request.method == 'POST':
return (permissions.AllowAny(),)
return (permissions.IsAuthenticated(), IsAccountOwner(),)
def create(self, request):
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
Account.objects.create_user(**serializer.validated_data)
return Response(serializer.validated_data, status=status.HTTP_201_CREATED)
return Response({
'status': 'Bad request',
'message': 'Account could not be created with received data.'
}, status=status.HTTP_400_BAD_REQUEST)
serializers.py
from django.contrib.auth import update_session_auth_hash
from rest_framework import serializers
from authentication.models import Account
class AccountSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True, required=False)
confirm_password = serializers.CharField(write_only=True, required=False)
class Meta:
model = Account
fields = ('id', 'email', 'username', 'created_at', 'updated_at',
'first_name', 'last_name', 'tagline', 'password',
'confirm_password',)
read_only_fields = ('created_at', 'updated_at',)
def create(self, validated_data):
return Account.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.username = validated_data.get('username', instance.username)
instance.tagline = validated_data.get('tagline', instance.tagline)
instance.save()
password = validated_data.get('password', None)
confirm_password = validated_data.get('confirm_password', None)
if password and confirm_password and password == confirm_password:
instance.set_password(password)
instance.save()
update_session_auth_hash(self.context.get('request'), instance)
return instance
permissions.py
from rest_framework import permissions
class IsAccountOwner(permissions.BasePermission):
def has_object_permission(self, request, view, account):
if request.user:
return account == request.user
return False
urls.py
from django.conf.urls import patterns, url, include
from thinkster_django_angular_boilerplate.views import IndexView
from rest_framework_nested import routers
from authentication.views import AccountViewSet
router = routers.SimpleRouter()
router.register(r'accounts', AccountViewSet)
urlpatterns = patterns(
'',
# ... URLs
url(r'^/api/v1/', include(router.urls)),
url('^.*$', IndexView.as_view(), name='index'),
)
models.py
from django.contrib.auth.models import AbstractBaseUser
from django.contrib.auth.models import BaseUserManager
from django.db import models
class AccountManager(BaseUserManager):
def create_user(self, email, password=None, **kwargs):
if not email:
raise ValueError('Users must have a valid email address.')
if not kwargs.get('username'):
raise ValueError('Users must have a valid username.')
account = self.model(
email=self.normalize_email(email), username=kwargs.get('username')
)
account.set_password(password)
account.save()
return account
def create_superuser(self, email, password, **kwargs):
account = self.create_user(email, password, **kwargs)
account.is_admin = True
account.save()
return account
class Account(AbstractBaseUser):
email = models.EmailField(unique=True)
username = models.CharField(max_length=40, unique=True)
first_name = models.CharField(max_length=40, blank=True)
last_name = models.CharField(max_length=40, blank=True)
tagline = models.CharField(max_length=140, blank=True)
is_admin = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
objects = AccountManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
def __unicode__(self):
return self.email
def get_full_name(self):
return ' '.join([self.first_name, self.last_name])
def get_short_name(self):
return self.first_name
I think the problem is with Django code, because AngularJS seems to not throw any errors and I can see that request is coming to the django. I am using python3.4, Django 1.8.3, and Django-rest-framework 3.1.

UserCreationForm in Django with extra fields when a foreign key exists

I am very new to Django and I am trying to create a registration form. This is the relevant part of my model:
class UserProfile(models.Model):
user = models.OneToOneField(User)
university = models.ForeignKey('University')
course_title = models.ForeignKey('Course_Title')
class Meta:
db_table = 'user_profile'
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
This is the form I am using:
from django.contrib.auth.forms import UserCreationForm
from django import forms
from django.contrib.auth.models import User
from shelf.models import University, Course_Title, UserProfile
class RegistrationForm(UserCreationForm):
error_messages = {
'duplicate_email': "A user with that e-mail already exists.",
'email_mismatch': "The two e-mail fields didn't match.",
}
# An e-mail is being used instead of the usual Django username.
username = forms.EmailField(label="E-mail")
username2 = forms.EmailField(label="Confirm E-mail")
university = forms.ModelChoiceField(queryset=University.objects.all(), empty_label="Please choose a university")
course_title = forms.ModelChoiceField(queryset=Course_Title.objects.all(), empty_label="Please choose a course title")
class Meta:
model = User
fields = ("first_name", "last_name", "university", "course_title", "username", "username2")
.
.
.
def save(self, commit=True):
user = super(RegistrationForm, self).save(commit=False)
user.save()
user_profile = UserProfile(user=user, university=self.cleaned_data["university"], course_title=self.cleaned_data["course_title"])
if commit:
user_profile.save()
return user_profile
And, this is my view:
def index(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
new_user = form.save()
return HttpResponseRedirect("/")
else:
form = RegistrationForm()
return render(request, "shelf/register.html", {'form': form, })
I get the error user_profile.university_id may not be NULL. I have looked a lot for why this happens and I understand why it happens. But I can't seem to fix it.
Thanks in advance.
It looks like the problem is caused by your signal handler:
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance) # no university here
post_save.connect(create_user_profile, sender=User)
Please try disable this handler and test again.
If you want the signal handler, please try changing the form.save method:
def save(self, commit=True):
user = super(RegistrationForm, self).save(commit=False) #this should trigger the signal handler to create a UserProfile instance, but not commit
user_profile = user.get_profile()
user_profile.university=self.cleaned_data["university"]
user_profile.course_title=self.cleaned_data["course_title"]
if commit:
user.save()
user_profile.save()
Be careful, if you create a user in the shell, you still get the same error, I think maybe you could add default value for university and course_title field in UserProfile model:
class UserProfile(models.Model):
user = models.OneToOneField(User)
university = models.ForeignKey('University', default=1)
course_title = models.ForeignKey('Course_Title', default=1)
I've the same issue but couldn't solve it though I followed the steps
Model
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
class UserProfile(models.Model):
url = models.URLField()
home_address = models.TextField()
user = models.ForeignKey(User, unique=True)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
View
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User, Group
from django import forms
from django.shortcuts import render_to_response
from django.http import HttpResponse
from django.template.context import RequestContext
from django.views.decorators.csrf import csrf_protect
class RegisterForm(UserCreationForm):
permission_choices = (
('1', 'test1'),
('2', 'test2'),
)
email = forms.EmailField(required=False)
first_name = forms.CharField(required=False)
last_name = forms.CharField(required=False)
url = forms.URLField()
permission = forms.ChoiceField(choices=permission_choices)
class Meta(UserCreationForm.Meta):
model = User
fields = ('first_name', 'last_name', 'url', 'username', 'email', 'password1', 'password2', 'permission')
def save(self, commit=True):
user = super(RegisterForm, self).save(commit=False)
user_profile = user.get_profile()
user.email = self.cleaned_data['email']
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.permission = self.cleaned_data['permission']
user_profile.url = self.cleaned_data['url']
if commit:
user.save()
user_profile.save()
return user
#csrf_protect
def register(request):
if request.method == 'POST':
form = RegisterForm(request.POST)
if form.is_valid():
new_user = form.save()
if new_user.permission == 'q':
try:
new_user.groups.add(Group.objects.get(name='test1'))
except Group.DoesNotExist:
u = User.objects.get(pk=new_user.pk)
u.delete()
return HttpResponse('The Organisation you\'ve just choosed doesn\' exist')
return HttpResponse('OK')
else:
form = RegisterForm()
return render_to_response('registration/user.html', {'form': form}, context_instance=RequestContext(request))

Categories