Django cascade save? - django

I have a method on my user registration form that looks like this:
def save(self):
user = User(
username = self.cleaned_data['username'],
email = self.cleaned_data['email1'],
first_name = self.cleaned_data['first_name'],
last_name = self.cleaned_data['last_name'],
)
user.set_password(self.cleaned_data['password1'])
user.profile = Profile(
primary_phone = self.cleaned_data['phone'],
)
user.profile.address = Address(
country = self.cleaned_data['country'],
province = self.cleaned_data['province'],
city = self.cleaned_data['city'],
postal_code = self.cleaned_data['postal_code'],
street1 = self.cleaned_data['street1'],
street2 = self.cleaned_data['street2'],
street3 = self.cleaned_data['street3'],
)
user.save()
return user
Problem is when I call form.save() it creates the user object as expected, but doesn't save his profile or address. Why doesn't it cascade and save all the sub-models? I suspect I could call user.profile.save() and user.profile.address.save() manually, but I want the whole thing to succeed or fail together. What's the best way to do this?
Current solution:
def save(self):
address = Address(
country = self.cleaned_data['country'],
province = self.cleaned_data['province'],
city = self.cleaned_data['city'],
postal_code = self.cleaned_data['postal_code'],
street1 = self.cleaned_data['street1'],
street2 = self.cleaned_data['street2'],
street3 = self.cleaned_data['street3'],
)
address.save()
user = User(
username = self.cleaned_data['username'],
email = self.cleaned_data['email1'],
first_name = self.cleaned_data['first_name'],
last_name = self.cleaned_data['last_name'],
)
user.set_password(self.cleaned_data['password1'])
user.save()
profile = Profile(
primary_phone = self.cleaned_data['phone'],
)
profile.address = address
profile.user = user
profile.save()
I had to make profile the "central" object. Needed to set profile.user = user rather than user.profile = profile to make it work (I guess because the key is on the profile model, not on the user model).
Newer solution:
I took a hint from this article suggested in this answer.
Now I have separated my model forms and moved the logic into the view:
def register(request):
if request.POST:
account_type_form = forms.AccountTypeForm(request.POST)
user_form = forms.UserForm(request.POST)
profile_form = forms.ProfileForm(request.POST)
address_form = forms.AddressForm(request.POST)
if user_form.is_valid() and profile_form.is_valid() and address_form.is_valid():
user = user_form.save()
address = address_form.save()
profile = profile_form.save(commit=False)
profile.user = user
profile.address = address
profile.save()
return HttpResponseRedirect('/thanks/')
else:
account_type_form = forms.AccountTypeForm()
user_form = forms.UserForm()
profile_form = forms.ProfileForm()
address_form = forms.AddressForm()
return render_to_response(
'register.html',
{'account_type_form': account_type_form, 'user_form': user_form, 'address_form': address_form, 'profile_form': profile_form},
context_instance=RequestContext(request)
)
I'm not too fond of shifting the burden to the view, but I guess I get a bit more flexibility this way?

It doesn't cascade-save because it doesn't actually know whether or not the other objects need to be saved.
To do it in one go, first start a transaction:
#transaction.commit_on_success
def save(self):
....
Then save the subobjects in order:
user.profile.address.save()
user.profile.save()
user.save()

The problem is that you're trying to create or update fields in a User object that doesn't even exist yet. So the other fields aren't really updated because they aren't associated to any primary keys of the child fields.
Every time you're instantiating a new model field, you have to make sure you're saving so that a child model field has an id (primary key) to associate with.
You need something more like this:
def save(self):
user = User(
username = self.cleaned_data['username'],
email = self.cleaned_data['email1'],
first_name = self.cleaned_data['first_name'],
last_name = self.cleaned_data['last_name'],
)
## save user so we get an id
user.save()
## make sure we have a user.id
if user.id:
## this doesn't save the password, just updates the working instance
user.set_password(self.cleaned_data['password1'])
user.profile = Profile(
primary_phone = self.cleaned_data['phone'],
)
## save the profile so we get an id
user.profile.save()
## make sure we have a profile.id
if user.profile.id:
user.profile.address = Address(
country = self.cleaned_data['country'],
province = self.cleaned_data['province'],
city = self.cleaned_data['city'],
postal_code = self.cleaned_data['postal_code'],
street1 = self.cleaned_data['street1'],
street2 = self.cleaned_data['street2'],
street3 = self.cleaned_data['street3'],
)
## save the profile address
user.profile.address.save()
## final save to commit password and profile changes
user.save()
return user
This cascading save() thing you have going on here just doesn't feel right. You're prone to way too many errors there, if any of the fields doesn't save you'll end up with a partially complete user instance and posisbly end up with duplicates if the user has to go back and try again. Not fun!
Edit: Removed the 2nd half of this because it was not accurate.

Related

Django: Foreign Key to User -> Form is not validating because field is required

I'm currently creating a Registration-Page with two parts
One part is about the Username and a Passwort.
The second part is about choosing the own PC-Configuration
After defining everything, the User can register to get to the Main-Page.
Therefore I got a Model called "PC_Configuration" with a bunch of Foreign-Keys to the different Database-Models of the Processors/Graphicscards etc.:
class PC_Configuration(models.Model):
user = models.ForeignKey(User, related_name='user_id', on_delete=models.DO_NOTHING)
processor = models.ForeignKey(Processors, related_name='processor_id', on_delete=models.DO_NOTHING)
graphicscard = models.ForeignKey(Graphicscard, related_name='graphicscard_id', on_delete=models.DO_NOTHING)
os = models.ForeignKey(OS, related_name='os_id', on_delete=models.DO_NOTHING)
ram = models.ForeignKey(RAM, related_name='ram_id', on_delete=models.DO_NOTHING)
harddrive = models.ForeignKey(Harddrive, related_name='harddrive_id', on_delete=models.DO_NOTHING)
Also, there is one ForeignKey to the User to connect the Configuration to the respective User-ID.
Inside views.py, I've been creating a DropdownForm for all the Dropdown-Fields which the User shall choose on his own:
class DropdownForm(forms.ModelForm):
class Meta:
model = models.PC_Configuration
exclude = []
def __init__(self, *args, **kwargs):
super(DropdownForm, self).__init__(*args, **kwargs)
self.fields['processors'].queryset = DropdownForm.objects.all()
self.fields['processors'].label_from_instance = lambda obj: "%s" % obj.name
self.fields['graphicscard'].queryset = DropdownForm.objects.all()
self.fields['graphicscard'].label_from_instance = lambda obj: "%s" % obj.name
self.fields['os'].queryset = DropdownForm.objects.all()
self.fields['os'].label_from_instance = lambda obj: "%s" % obj.name
self.fields['ram'].queryset = DropdownForm.objects.all()
self.fields['ram'].label_from_instance = lambda obj: "%s" % obj.name
self.fields['harddrive'].queryset = DropdownForm.objects.all()
self.fields['harddrive'].label_from_instance = lambda obj: "%s" % obj.name
But regarding the fact, that the User-ID shall be assigned to the Configuration automatically, there's no field for that in here.
It is defined in the register_view(request) - Method:
def register_view(request):
form = DropdownForm()
if request.method == "POST":
form = DropdownForm(request.POST)
username = request.POST.get('username')
password = request.POST.get('password')
myuser = User.objects.create_user(username, None, password)
myuser.save()
auth.login(request, myuser)
#form.user = request.user
print(form.errors)
if form.is_valid():
instance = form.save(commit=False)
instance.user = request.user
instance.save()
messages.success(request, "Account has been created successfully")
return redirect(reverse('gamesearch_view'))
else:
print('Failed')
form = DropdownForm()
render(request, 'register.html', dict(form=form))
return render(request, 'register.html', dict(form=form))
And in here, we got the problem, I guess.
While Testing the Registration, the Testaccounts keep creating and login successfully. But the problem is, that there's no PC-Configuration created because the form is not validating.
With
print(form.errors)
I've been trying to figure out why exactly and it said
<ul class="errorlist"><li>user<ul class="errorlist"><li>This field is required.</li></ul></li></ul>
So it seems like it's necessary to define the "user"-field before checking, if the form is validating and defining the user inside an instance afterwards.
That's why I was trying to do this:
form.user = request.user
But it's still not working and I can't figure out, what's exactly the problem since "user" shouldn't be part of the form-validation.
Can you help me out here?
Thank you in Advance!
You'll have a simpler time with something like this...
Your related_names were somewhat bogus; they're supposed to be the reverse name from the "viewpoint" of the other model. (Also, you never need to add _id to your fields by hand in Django.) If you elide the related_names, they'll implicitly be pc_configuration_set.
on_delete=DO_NOTHING is likely not a good idea. PROTECT is a good default.
It's easier to just handle the username and password as fields in the form.
You were missing exclude = ["user"], so if your template didn't render a field for user, of course it'd be missing. However, you also don't want the POSTer of the form to submit any old user id.
Using a FormView removes most of the boilerplate required to manage forms.
We're using transaction.atomic() to make sure the user doesn't get finally saved to the database if saving the PC Configuration fails.
We assign the created user to form.instance, which is the new but as-of-yet unsaved PC Configuration.
(Of course, imagine these are in separate files.)
from django import forms
from django.db import models, transaction
from django.views.generic import FormView
class PC_Configuration(models.Model):
user = models.ForeignKey(User, on_delete=models.PROTECT)
processor = models.ForeignKey(Processors, on_delete=models.PROTECT)
graphicscard = models.ForeignKey(Graphicscard, on_delete=models.PROTECT)
os = models.ForeignKey(OS, on_delete=models.PROTECT)
ram = models.ForeignKey(RAM, on_delete=models.PROTECT)
harddrive = models.ForeignKey(Harddrive, on_delete=models.PROTECT)
class RegisterAndConfigurePCForm(forms.ModelForm):
username = forms.CharField(required=True)
password = forms.CharField(required=True, widget=forms.PasswordInput())
class Meta:
model = PC_Configuration
exclude = ["user"] # we'll assign this by hand
class RegisterAndConfigureView(FormView):
form_class = RegisterAndConfigurePCForm
template_name = "register.html"
def form_valid(self, form):
with transaction.atomic():
user = User.objects.create_user(form.cleaned_data["username"], None, form.cleaned_data["password"])
form.instance.user = user # assign user to the to-be-created PC configuration
form.save()
return redirect(reverse("gamesearch_view"))

Set form field value before is_valid()

I'm having a bit of trouble grasping how to do this. I've put my best effort into searching Google without any luck.
I'll start with a bit of code and explain what I'm trying to do as I go:
models.py
class Action(models.Model):
title = models.CharField(max_length=200)
owner = models.ForeignKey(User, related_name='actions')
created_by = models.ForeignKey(User, related_name='+', editable=False)
modified_by = models.ForeignKey(User, related_name='+', editable=False)
class ActionForm(ModelForm):
class Meta:
model = Action
views.py
By default, there is a dropdown field for owner. I have an icon that allows the user to enter a new username in a text field instead for owner. I check to see if owner_new was submitted and if so, create that user. I then need to set the owner field to that value so that form.is_valid() will be true.
def action_create(request):
if request.method == 'POST':
form = ActionForm(request.POST)
# check if new user should be created
if 'owner_new' in request.POST:
# check if user already exists
user = User.objects.get(username=request.POST.get('owner_new'))
if not user:
user = User.objects.create_user(request.POST.get('owner_new'))
# HERE IS WHERE I'M STUMPED
form.owner = user.id
if form.is_valid(): # THIS FAILS BECAUSE form.owner ISN'T SET
action = form.save(commit=False)
action.created_by = request.user
action.modified_by = request.user
action.save()
return redirect('action_register:index')
else:
form = ActionForm()
return render(request, 'actions/create.html', {'form': form})
You can try this:
def action_create(request):
if request.method == 'POST':
form = ActionForm(request.POST)
# check if new user should be created
if 'owner_new' in request.POST:
# check if user already exists
user, _ = User.objects.get_or_create(username=request.POST.get('owner_new'))
updated_data = request.POST.copy()
updated_data.update({'owner': user})
form = MyForm(data=updated_data)
if form.is_valid(): # THIS FAILS BECAUSE form.owner ISN'T SET
action = form.save(commit=False)
action.created_by = request.user
action.modified_by = request.user
action.save()
return redirect('action_register:index')
else:
form = ActionForm()
return render(request, 'actions/create.html', {'form': form})
A cleaner way of doing this is:
add required=False to the owner field.
Now,
if form.is_valid(): # THIS DOES NOT FAIL EVEN IF form.owner ISN'T SET
action = form.save(commit=False)
if 'owner_new' in request.POST:
user, _ = User.objects.get_or_create(username=request.POST.get('owner_new'))
action.owner = user
action.created_by = request.user
action.modified_by = request.user
action.save()
return redirect('action_register:index')
I came into a similar situation and couldn't figure out how to do it the way I wanted. What I ended up with was putting a link to a UserForm which allows a user to create a new owner, and then redirect back to the ActionForm with the argument initial={owner: new_owner} included when instantiating the form.

Extended django user profile data overwrite issue

I create an userprofile as following:
class UserProfile(models.Model):
# This field is required:
user = models.OneToOneField(User, related_name="User can view study permission")
# Other fields:
phone = models.CharField(max_length=20)
disease = models.ManyToManyField(Disease)
date_assigned = models.DateTimeField("Date Assigned")
query = models.ManyToManyField(Query, blank=True)
def __unicode__(self):
studies = ', '.join(study.name for study in self.disease.all())
return "%s can view %s" % (self.user, studies)
It's extended information for users.
Now I am creating a admin page by myself to allow the admin to update the users account information which included the above user profile.
The form and code for this is as following:
def edit(request, user_id):
"""Edit a user's details"""
try:
user = User.objects.get(id=user_id)
except User.DoesNotExist:
user = None
# user exists:
if user:
# Initial form field data:
initial={'user_id': user_id,
'username': user.username,
'fname': user.first_name,
'lname': user.last_name,
'email': user.email,
'phone': user.get_profile().phone,
'groups': user.groups.all(),
'studies': user.get_profile().disease.all(),
'is_admin': user.is_staff,
'is_active': user.is_active
}
request.breadcrumbs(
(_("Home"), reverse('home')),
(_("All Users"), reverse('all users')),
(_("User Details"), reverse('user details', args=[user_id])),
)
if request.method == "GET":
form = UserProfileEditForm(initial=initial,extra=request.user.id)
response = {'heading': 'Edit', 'form': form}
return render_to_response('accounts/edit.html',
response,
context_instance=RequestContext(request)
)
elif request.method == "POST":
form = UserProfileEditForm(request.POST,extra=request.user.id)
if form.is_valid():
Log().add(request, "Edit", "W", "userprofile", user_id)
if form.cleaned_data['password1'] and form.cleaned_data['password2']:
user.set_password(form.cleaned_data['password1'])
user.username = form.cleaned_data['username']
user.first_name = form.cleaned_data['fname']
user.last_name = form.cleaned_data['lname']
user.email = form.cleaned_data['email']
user.groups = form.cleaned_data['groups']
user.is_staff = form.cleaned_data['is_admin']
user.is_active = form.cleaned_data['is_active']
user.save()
# Oddly to make the extra fields found in UserProfile are saved, you
# have to call get_profile().ATTRIBUTE, assign a value, then call
# get_profile().save(). Calling user.save() as the last step won't
# save any changes made to UserProfile:
disease_pks = form.cleaned_data['studies']
user.get_profile().disease = Disease.objects.filter(pk__in=disease_pks)
user.get_profile().phone = form.cleaned_data['phone']
user.get_profile().save()
return HttpResponseRedirect("/accounts/view/%s" % user_id)
else:
# form is not valid:
return render_to_response("accounts/edit.html",
{'form': form, 'heading': 'Edit'},
context_instance=RequestContext(request)
)
# user does not exist:
else:
error = "User #%s cannot be found. Press the 'BACK' button on your browser." % user_id
return HttpResponseRedirect(reverse('DigitalRecords.views.error', args=(error,)))
class UserProfileEditForm(forms.Form):
user_id = forms.IntegerField(widget=forms.HiddenInput())
username = forms.CharField(label=_("Username"))
password1 = forms.CharField(label=_("Password"),
widget=forms.PasswordInput(),
required=False
)
password2 = forms.CharField(label=_("Password (again)"),
widget=forms.PasswordInput(),
required=False
)
fname = forms.CharField(label=_("First name"))
lname = forms.CharField(label=_("Last name"))
email = forms.EmailField()
phone = forms.CharField(label=_("Phone"))
is_admin = forms.BooleanField(label=_("Is an administrator?"), required=False)
is_active = forms.BooleanField(label=_("Is an active user?"), required=False)
groups = forms.ModelMultipleChoiceField(queryset=Group.objects.all(),
widget=forms.CheckboxSelectMultiple()
)
#Attach a form helper to this class
helper = FormHelper()
helper.form_id = "edituserprofile"
helper.form_class = "userprofile"
#Add in a submit and reset button
submit = Submit("Save", "Save Changes")
helper.add_input(submit)
reset = Reset("Reset", "Reset")
helper.add_input(reset)
def __init__(self, *args,**kwargs):
extra = kwargs.pop('extra')
super(UserProfileEditForm, self).__init__(*args, **kwargs)
self.fields["studies"] = forms.ModelMultipleChoiceField(queryset=User.objects.get(id=extra).get_profile().disease.all(),
widget=forms.CheckboxSelectMultiple()
)
def clean_username(self):
"""Check if the username does not already exist"""
username = self.cleaned_data['username']
user_id = self.cleaned_data['user_id']
# handle:
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
return self.cleaned_data['username']
else:
if user.id == user_id:
return self.cleaned_data['username']
else:
raise forms.ValidationError('User "%s" already exists' % username)
def clean_fname(self):
fname = self.cleaned_data['fname']
# Remove any diacritics/accented characters:
fname = strip_diacritic(fname).strip()
# Match names that may have hyphens, apostrophes, or periods in them.
# example: John Doe, O'Brien, Leroy-Brown, Cpt. James T. Kirk
pattern = '^([a-zA-Z]+(?:\.)?(?:[\-\' ][a-zA-Z]+(?:\.)?)*)$'
# match the regex to the input string.
results = re.match(pattern, str(fname))
if results == None:
raise ValidationError(u'%s is not a valid name' % fname)
return fname
def clean_lname(self):
"""
Determine if the patient's name is valid. It removed any accented/diacritic
characters and replaces them with the base character for simplicity. Names with
hyphens and/ore apostrophes like Hanna-Barbara and O'Brien are allowed. If the
check fails a validation error is raised.
"""
lname = self.cleaned_data['lname']
# Remove any diacritics/accented characters:
lname = strip_diacritic(lname).strip()
# Match names that may have hyphens, apostrophes, or periods in them.
# example: John Doe, O'Brien, Leroy-Brown, Cpt. James T. Kirk
pattern = '^([a-zA-Z]+(?:\.)?(?:[\-\' ][a-zA-Z]+(?:\.)?)*)$'
# match the regex to the input string.
results = re.match(pattern, str(lname))
if results == None:
raise ValidationError(u'%s is not a valid name' % lname)
return lname
def clean(self):
"""Check if password1 and password2 match"""
cleaned_data = self.cleaned_data
password1 = cleaned_data.get('password1')
password2 = cleaned_data.get('password2')
# Notice it's an 'or' condition because a password change is optional:
if password1 or password2:
if password1 != password2:
msg = "Passwords do not match"
self._errors['password1'] = self.error_class([msg])
self._errors['password2'] = self.error_class([msg])
del cleaned_data['password1']
del cleaned_data['password2']
return cleaned_data
return self.cleaned_data
It works fine if an administrator trying to edit other user's account information.
However, when an administrator tried to update his own account. The disease field will always be blank whatever this field was changed or not.
Does anyone the reason and how should I change my code?
Thank you very much.
I'm not sure about other code, but these lines in your view does not seem appropriate.
user.get_profile().disease = Disease.objects.filter(pk__in=disease_pks)
user.get_profile().phone = form.cleaned_data['phone']
user.get_profile().save()
You are getting new profile object each time by calling user.get_profile() and updating it. Not saving it, instead a new object. So change it to
profile = user.get_profile()
profile.disease = Disease.objects.filter(pk__in=disease_pks)
profile.phone = form.cleaned_data['phone']
profile.save()

Adding ForeignKey to UserProfile giving foreign key constraint fails

I've been trying to figure this out for hours, and believe me, I really looked everywhere on Stack Overflow.
In my UserProfile, I have a ForeignKey reference to another model (called "Company"), and upon registration, I create a new Company and point my UserProfile ForeignKey to that Company.
models.py is as follows:
class UserProfile(models.Model):
company = models.ForeignKey(Company)
title = models.CharField(max_length = 50, default = '')
user = models.OneToOneField(User, default = 0, null = True)
class Company(models.Model):
"""A company profile."""
name = models.CharField(max_length = 50)
I use a Form to do the signing up. Here's the form:
class SignupForm(ModelForm):
name = forms.CharField(label = "Name")
company = forms.CharField(max_length = 50)
email = forms.EmailField(label = "Email")
password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = User
fields = ("name", "company", "email", "password")
def save(self, commit=True):
user = super(SignupForm, self).save(commit=False)
name = self.cleaned_data["name"].split()
if len(name) == 1:
# User did not enter a last name.
user.first_name = name
else:
user.first_name, user.last_name = name
user.email = self.cleaned_data["email"]
user.set_password(self.cleaned_data["password"])
user.username = user.email
if commit:
user.save()
return user
and here's the signup view:
def signup(request):
if request.method == 'POST':
form = SignupForm(request.POST)
if form.is_valid():
# Check if email has already been used for an account.
email = request.POST['email']
existing_account = User.objects.filter(email = email)
if existing_account:
form = SignupForm()
return render_to_response('registration/signup.html',
{ 'form': form,
'error': 'duplicate_email',
})
# Otherwise, save the form and create a new user.
new_user = form.save()
company = Company(name=request.POST['company'])
company.save()
user_profile = new_user.get_profile()
user_profile.company = company
user_profile.save()
new_user = authenticate(
username = email,
password = request.POST['password']
)
# Log the user in automatically.
login(request, new_user)
# Redirect user to Thank You page.
return HttpResponseRedirect('/thanks')
else:
form = SignupForm()
return render_to_response('registration/signup.html', {
'form': form,
})
The error I am getting is telling me that company_id cannot be null. I clearly add a new Company. Please let me know what you think might be wrong.
Thanks
I've had this exact error today, with no reason, except that it was caused by SQLite. With SQLite, the id field of one table went from INTEGER PRIMARY KEY to INTEGER. If you're using SQLite, try deleting the offending table and recreate it with a syncdb.
What is the value of
user_profile = new_user.get_profile()
?
Not sure if this feels too hackish for your tastes but perhaps you could create/save the Company object and pass it in to your SignupForm.save() method as a positional/keyword argument.
The issue you'd get there is that you'd be expecting a CharField and you'd be passing in a company object. So you'd probably want to give company.pk to the company field in your form.

Simple form not validating

I have found here on stackoverflow a method to extend django's built-in authentication using signals. My base User is defined by 'email' and passwords (so no username there). So I'm trying to modify it to my needs, but I'm geting a validation error for my form. Strange thing is that error is connected to the User.email field and I'm getting 'already in use' even though I'm just registering at the moment. Is it trying to save it 2 times or what ? I've discovered it when I was sending dictionary with data to form's contstructor in shell: form = MyForm(data={}). After this form was still invalid, but changing email to different value finally gave me True.
The user_created function connected to registration signal :
def user_created(sender, user, request, **kwargs):
form = CustomRegistrationForm(request.POST, request.FILES)
if form.is_valid():
data = UserProfile(user=user)
data.is_active = False
data.first_name = form.cleaned_data['first_name']
data.last_name = form.cleaned_data['last_name']
data.street = form.cleaned_data['street']
data.city = form.cleaned_data['city']
data.save()
else:
return render_to_response('user/data_operations/error.html', {'errors': form._errors}, context_instance=RequestContext(request))
user_registered.connect(user_created)
My form :
class CustomRegistrationForm(RegistrationForm):
first_name = forms.CharField(widget=forms.TextInput(attrs=attrs_dict), max_length=50)
last_name = forms.CharField(widget=forms.TextInput(attrs=attrs_dict), max_length=50)
street = forms.CharField(widget=forms.TextInput(attrs=attrs_dict), max_length=50)
city = forms.CharField(widget=forms.TextInput(attrs=attrs_dict), max_length=50)
My model :
class UserProfile(models.Model):
first_name = models.CharField(_("Name"), max_length=50, blank=False,)
last_name = models.CharField(_("Last name"), max_length=50, blank=False,)
street = models.CharField(_("Street"), max_length=50, blank=False,)
city = models.CharField(_("City"), max_length=50, blank=False,)
user = models.ForeignKey(User, unique=True, related_name='profile',)
Registration form :
class RegistrationForm(forms.Form):
email = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict,
maxlength=75)),
label=_("Adres email"))
password1 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False),
label=_("Haslo"))
password2 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False),
label=_("Haslo powtorzone"))
def clean_email(self):
email = self.cleaned_data.get("email")
if email and User.objects.filter(email=email).count() > 0:
raise forms.ValidationError(
_(u"Already in use."))
return email
your 'user_registered' signal is sent after the User is saved. So it already has an 'email' field defined.
UPDATE
Using restless thinking :
form = CustomRegistrationForm(request.POST, request.FILES, notvalidateemail=True)
and in form :
def __init__(self, *args, **kwargs):
self.notvalidateemail = kwargs.pop('notvalidateemail',False)
super(CustomRegistrationForm, self).__init__(*args, **kwargs)
def clean_email(self):
if self.notvalidateemail:
return
else:
#your cleaning here
return email
Problem:
Your form is first saved by django-registration. Then you save it again in user_created.
Solutions:
Use a different form in user_created. One that won't have already saved fields (these from User model like email). You just want to save additional data in user_created, right?
Add some parameters to the form like:
in user_created:
form = CustomRegistrationForm(dontvalidateemail=True, request.POST, request.FILES)
and in form's init;
self.dontvalidateemail = dontvalidateemail
then just check it in clean_email.