Django Forn Not Saving Extra Information - django

I extended the Django AbstratUser so that users can use email to sign in and signup, these work perfectly. The problem I am facing, however, is that the extra information on the extended model is not storing the information in the database, even though the user gets created. Once I hit the submit button, the user and extended model get created, and while the user model stores the information, the extended model is always empty.
I have tried using both signals and #transaction_atomic, yet, I have not been able to figure it out. Maybe I am missing out something, I do not know.
Models.py
class Company(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, primary_key=True)
name= models.CharField(_('Company name'), max_length=250)
...
#more information
...
class Meta:
verbose_name = _('Company')
verbose_name_plural = _('Companies')
def __str__(self):
return self.name
forms.py
class CompanySignUpForm(CustomUserCreationForm):
name = forms.CharField(widget=TextInput(attrs={'placeholder': 'Company name'}))
...
#more fields
...
class Meta(CustomUserCreationForm.Meta):
model = User
#transaction.atomic
def save(self):
user = super().save(commit=False)
user.is_company = True
user.save()
company = Company.objects.create(user=user)
company.name = self.cleaned_data.get('name')
...
#more information
...
return user
Views.py
def company_signup(request):
if request.method == 'POST':
form = CompanySignUpForm(request.POST)
if form.is_valid():
form.save()
return render(request, 'accounts/templates/company_success.html')
else:
form = CompanySignUpForm()
return render(request, 'accounts/templates/company_signup.html', context={
'title': _('Create a Company Account'),
'form': form,
})
Edit:
Thanks to #Mandrup, I was able to extend his solution to fit my need.
forms.py
class CompanySignUpForm(CustomUserCreationForm):
name = forms.CharField(widget=TextInput(attrs={'placeholder': 'Company name'}))
number_of_employees = forms.CharField(widget=NumberInput(attrs={'placeholder': 'Number of employees'}))
phone = forms.CharField(widget=TextInput(attrs={'placeholder': 'Contact Number'}))
country = forms.ModelChoiceField(queryset=Country.objects.all(), required=True, empty_label="Country")
class Meta(CustomUserCreationForm.Meta):
model = User
#transaction.atomic
def save(self, commit=True):
user = super(CompanySignUpForm, self).save(commit=False)
if commit:
user.is_company = True
user.save()
name = self.cleaned_data.get('name')
number_of_employees = self.cleaned_data.get('number_of_employees')
phone = self.cleaned_data.get('phone')
country = self.cleaned_data.get('country')
company = Company(user=user, name=name, number_of_employees=number_of_employees, phone=phone, country=country)
company.save()
return user

Edit:
This worked for me when i tried to create an extended user profile. I changed it to fit your needs.
Model:
class Company(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, primary_key=True)
name= models.CharField(max_length=250)
...
#more information
...
def __str__(self):
return self.name
Form:
class RegisterUserForm(UserCreationForm):
class Meta:
model = User
fields = ["username", "email", "password1", "password2"]#add whatever fields you want to here
def save(self, commit=True):
user = super(RegisterUserForm, self).save(commit=False)
if commit:
user.save()
company = Company(user=user, name='Company name')
company.save()
return user

Related

Django AbstractUser password is not hashing

I've used as a model AbstractUser extended by custom fields, created form automatically by ModelForm. The problem is that, users except superuser cannot log in to system. I think it's reason, their passwords are not hashing. Where should I make it ? Here are my codes.
forms.py:
class CustomUserSignUpForm(ModelForm):
class Meta:
model = CustomUser
fields = ['username', 'password', 'user_image', 'role', 'branch', 'license_number', 'fin_number', 'first_name', 'last_name', 'patronymic', 'phone_number', 'email', 'voen_number', 'is_active']
views.py:
def sign_up(request):
if request.method == 'POST':
form = CustomUserSignUpForm(request.POST)
if form.is_valid():
form.save()
else:
form = CustomUserSignUpForm()
context = {
'form': form,
}
return render(request, 'sign_up.html', context)
models.py:
class CustomUser(AbstractUser):
patronymic = models.CharField(_('Ata adı'), max_length=150, blank=True)
role = models.ForeignKey(Role, on_delete=models.CASCADE, blank=True, null=True)
user_image = models.FileField(_('Profil şəkli'), upload_to='static/assets/images/user-images', blank=True)
branch = models.ForeignKey(Branch, on_delete=models.CASCADE, blank=True, null=True)
phone_number = models.CharField(_('Telefon'), max_length=20, blank=True)
voen_number = models.CharField(_('VÖEN'), max_length=30, blank=True)
fin_number = models.CharField(_('FİN'), max_length=20, blank=True)
license_number = models.CharField(_('Lisenziya'), max_length=40, blank=True)
def __str__(self):
return self.username
To define a function to hash that password, you must inherited save method for you user form
class CustomUserSignUpForm(forms.ModelForm):
............
def save(self, commit=True):
# Save the provided password in hashed format
user = super(CustomUserSignUpForm, self).save(commit=False)
user.set_password(self.cleaned_data["password"])
if commit:
user.save()
return user
This override of ModelForm is better off, because:
I check if the user exists.
I Hash de password if the password is not encoded.
class UsuarioAdmin(admin.ModelAdmin):
...
def save_model(self, request, obj, form, change):
try:
user_database = USUARIO.objects.get(pk=obj.pk)
except Exception:
user_database = None
if user_database is None \
or not (check_password(form.data['password'], user_database.password)
or user_database.password == form.data['password']):
obj.password = make_password(obj.password)
else:
obj.password = user_database.password
super().save_model(request, obj, form, change)

Unable to limit choices of form field to a specific user

As the title states, I am unable to limit the choices of a form field based on a specific user. For example, in the choices for the enrolled field of the form all “riders” are selectable to all “users”, rather than just the “riders” that are “owned” by the user.
I’ve tried this question and answer that essentially asks the same question, as well as some other possible solutions that deal with m2m model fields, limit_choices_to, but have not been successful.
Any advise would be greatly appreciated.
models.py
class Event(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=200, null=True)
description = models.TextField(max_length=255, null=True, blank=True)
start = models.DateTimeField(null=True, blank=True)
end = models.DateTimeField(null=True, blank=True)
enrolled = models.ManyToManyField('riders.Rider',
related_name='events', blank=True)
def __str__(self):
return self.title
model.py (different app)
class Rider(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
birthdate = models.DateField(verbose_name=None, auto_now=False)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.last_name + ', ' +self.first_name
views.py
#login_required
def enroll(request, event_id):
event = Event.objects.get(id=event_id)
if request.method != 'POST':
form = EventForm(instance=event)
else:
form = EventForm(instance=event, data=request.POST)
if form.is_valid():
enroll = form.save(commit=False)
enroll.save()
form.save_m2m()
return HttpResponseRedirect(reverse('riding_schedule:view_events'))
forms.py
class EventForm(forms.ModelForm):
class Meta:
model = Event
fields = ['title', 'start', 'end', 'enrolled']
labels = {'text':''}
widgets = {
'enrolled': forms.CheckboxSelectMultiple()
}
You can try like this:
First, send the current user information to the Form when form is initiated:
#login_required
def enroll(request, event_id):
event = Event.objects.get(id=event_id)
if request.method != 'POST':
form = EventForm(instance=event, user=request.user) # <-- Here
else:
form = EventForm(instance=event, data=request.POST)
# ....
Then use this information in the Form like this:
class EventForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super(EventForm, self).__init__(*args, **kwargs)
if user:
self.fields['enrolled'].queryset = Rider.objects.filter(owner=user) # overriding the queryset for enrolled here
class Meta:
model = Event
fields = ['title', 'start', 'end', 'enrolled']
labels = {'text':''}
widgets = {
'enrolled': forms.CheckboxSelectMultiple()
}

Register user info to separate tables

I'd like to save the basic info to Users and additional info to other table. I don't know how I can save email to other heepoo table's user_id field. Could you please help me on?
models.py
class School(models.Model):
id = models.IntegerField(primary_key=True)
Name = models.CharField(max_length=50, null=False)
def delete(self, *args, **kwargs):
self.Mascot.delete()
super(School, self).delete(*args, **kwargs)
def __str__(self):
return self.Name
forms.py
class UserForm(forms.ModelForm):
email = forms.EmailField(max_length=50, required=True)
password = forms.CharField(widget=forms.PasswordInput())
username = forms.HiddenInput()
class Meta:
model = User
fields = ('email', 'password',)
class RegisterForm(forms.ModelForm):
class Meta:
model = HeepooUser
exclude = ('allow_phone')
views.py
def register(request):
registered = False
if request.method == 'POST':
user_form = UserForm(request.POST)
profile_form = RegisterForm(request.POST)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save(commit=False)
user.set_password(user.password)
user = user_form.save()
profile = profile_form.save(commit=False)
profile.user = user
profile = profile_form.save()
registered = True
All the best.
Sure lot of method is there prefer way is OneToOneField to User table
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
additional_info = models.CharField(max_length=200)

Django - ModelForm initial data from database

In my Django project i create an app to have additional information about registered users. So my model looks like this:
class UserProfile(models.Model):
class Meta:
verbose_name_plural = u'User Profile'
user = models.OneToOneField(User)
birthday = models.DateField(blank=True, null=True)
avatar = models.ImageField(upload_to='media/profile/avatar', blank=True, null=True)
name = models.CharField(blank=True, null=True, max_length=20)
surname = models.CharField(blank=True, null=True, max_length=50)
phone = models.CharField(blank=True, null=True, max_length=12)
def __unicode__(self):
return '%s' % self.user
In user profile i create modelform where user can fill or edit the fields from UserProfile model:
class ExtraProfileDataForm(ModelForm):
name = forms.CharField(label=(u'Enter your name'))
surname = forms.CharField(label=(u'Enter your surname'))
phone = forms.CharField(label=(u'Enter your phone'))
birthday = forms.DateField(label=(u'Enter birthday'))
avatar = forms.ImageField(label=(u'Enter avatar'))
class Meta:
model = UserProfile
fields = ('name', 'surname', 'phone', 'birthday', 'avatar')
def __init__(self, *args, **kwargs):
super(ExtraProfileDataForm, self).__init__(*args, **kwargs)
for key in self.fields:
self.fields[key].required = False
This is the view of the model form:
#login_required
def UserFullDataForm(request):
if request.method == 'POST':
form = ExtraProfileDataForm(request.POST)
if form.is_valid():
profile_user = request.user
user_profile = UserProfile(user=profile_user)
user_profile.name = form.cleaned_data['name']
user_profile.surname = form.cleaned_data['surname']
user_profile.phone = form.cleaned_data['phone']
user_profile.birthday = form.cleaned_data['birthday']
user_profile.avatar = form.cleaned_data['avatar']
user_profile.save()
return HttpResponseRedirect('/profile/')
else:
return render(request, 'profiles/extra_profile.html', {'form':form})
else:
form = ExtraProfileDataForm()
context = {'form':form}
return render (request, 'profiles/extra_profile.html', context)
But i want to load on ExtraProfileDataForm initial data from model UserProfile if the fields not empty. I searched how to do that on Django documentation website, but nothing found. Can somebody help me to understand how to do it? Thanks a lot.
You use the instance parameter.
Note that you are doing much more work than necessary here; most of your view can be cut.
#login_required
def UserFullDataForm(request):
try:
profile = request.user.userprofile
except UserProfile.DoesNotExist:
profile = UserProfile(user=request.user)
if request.method == 'POST':
form = ExtraProfileDataForm(request.POST, instance=profile)
if form.is_valid():
form.save()
return HttpResponseRedirect('/profile/')
else:
form = ExtraProfileDataForm(instance=profile)
return render(request, 'profiles/extra_profile.html', {'form':form})
Similarly, in your form, you don't need the overridden __init__ method because you're manually specifying all the fields anyway; you can add required=False on each one there. However, you could make this even shorter by adding the labels in the model definition; then your entire modelform could just be:
class ExtraProfileDataForm(ModelForm):
class Meta:
model = UserProfile
fields = ('name', 'surname', 'phone', 'birthday', 'avatar')
One final note: you're consistently using three-space indentation, which is a bit, well, odd. Most Python programmers prefer two or four.

Django form with many-to-many relationship does not save

I have a custom registration form for my users to add a profile on my app. However, a bug has recently popped up in that the form is not saving the information that is put into all the fields.
My user model, MyUser has a ManyToMany relationship with another model, Interest, and this is where the issues are arising. I am not sure if it is the RegistrationForm or the register view that is causing it, so I have included both below, as well as the model code.
I also have a view for the users to update their profile, also included, once it is created, and this is working absolutely perfectly. This is the personal view.
As I say, it is only the Interest field that is not being returned, even though it is being filled in on the registration page.
Any help or advice is much appreciated, thanks.
models.py
class Interest(models.Model):
title = models.TextField()
def __unicode__(self):
return self.title
class MyUser(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=40)
date_of_birth = models.DateField()
course = models.ForeignKey(Course, null=True)
location = models.ForeignKey(Location, null=True)
interests = models.ManyToManyField(Interest, null=True)
bio = models.TextField(blank=True)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = MyUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['date_of_birth']
views.py
def register(request):
if request.method == 'POST':
form = RegistrationForm(data=request.POST)
if form.is_valid():
form.save()
return redirect('/friends/home/')
else:
form = RegistrationForm()
template = "adduser.html"
data = { 'form': form, }
return render_to_response(template, data, context_instance=RequestContext(request))
#login_required(login_url='/friends/login/')
def personal(request):
"""
Personal data of the user profile
"""
profile = request.user
if request.method == "POST":
form = ProfileForm(request.POST, instance=profile)
if form.is_valid():
form.save()
messages.add_message(request, messages.INFO, _("Your profile information has been updated successfully."))
return redirect('/friends/success/')
else:
form = ProfileForm(instance=profile)
template = "update_profile.html"
data = { 'section': 'personal', 'form': form, }
return render_to_response(template, data, context_instance=RequestContext(request))
forms.py
class RegistrationForm(forms.ModelForm):
"""
Form for registering a new account.
"""
email = forms.EmailField(widget=forms.TextInput, label="Email")
password1 = forms.CharField(widget=forms.PasswordInput,
label="Password")
password2 = forms.CharField(widget=forms.PasswordInput,
label="Password (again)")
course = forms.ModelChoiceField(queryset=Course.objects.order_by('title'))
location = forms.ModelChoiceField(queryset=Location.objects.order_by('location'))
class Meta:
model = MyUser
fields = [
'first_name',
'last_name',
'date_of_birth',
'email',
'password1',
'password2',
'course',
'location',
'interests',
'bio',
]
def __init__(self, *args, **kwargs):#Sort interests alphabetically
super(RegistrationForm, self).__init__(*args, **kwargs)
self.fields['interests'].queryset = Interest.objects.order_by('title')
def clean(self):
cleaned_data = super(RegistrationForm, self).clean()
if 'password1' in self.cleaned_data and 'password2' in self.cleaned_data:
if self.cleaned_data['password1'] != self.cleaned_data['password2']:
raise forms.ValidationError("Passwords don't match. Please enter again.")
return self.cleaned_data
def save(self, commit=True):
user = super(RegistrationForm, self).save(commit=False)
user.set_password(self.cleaned_data['password1'])
if commit:
user.save()
return user
Since you use commit=false for the super(RegistrationForm, self).save call, it doesn't save the many-to-many field. You therefore need to add self.save_m2m() after user.save() in your save() method of RegistrationForm.
See https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#the-save-method
EDIT: save_m2m() is on the Form, not the Model