Custom Admin Creation Form - django

So in my models I have a specfic profile that has a one to one relationship with a more general profile that has a one to one relationship with the django user model. I want to be able to fill out one form in the django admin and create instances of all three models, with relationships already set up.
I haven't messed around with the django admin too much, so I'm not entirely sure how to make it work. Here is my failed attempt:
class CreateSpecializedProfileAdminForm(forms.ModelForm):
class Meta:
exclude = ['profile']
first_name = forms.CharField(max_length=30)
last_name = forms.CharField(max_length=30)
email = forms.EmailField(max_length=30)
password = forms.CharField(max_length=30)
confirm_password = forms.CharField(max_length=30)
def clean(self):
password = self.cleaned_data['password']
confirm_password = self.cleaned_data['confirm_password']
if len(self.cleaned_data['password']) < 6:
raise forms.ValidationError('Password must be at least 6 characters.')
if password != confirm_password:
raise forms.ValidationError('Passwords must match.')
return super(CreateSpecializedProfileAdminForm, self).clean()
def save(self, commit=True):
from django.contrib.auth.models import User
first = self.cleaned_data['first_name']
last = self.cleaned_data['last_name']
email = self.cleaned_data['email']
password = self.cleaned_data['password']
user = User.objects.create_user(email, email, password)
user.first_name = first
user.last_name = last
user.save()
profile = UserProfile()
profile.user_auth = user
profile.save()
specialized_profile = SpecializedProfile()
specialized_profile.profile = profile
specialized_profile.save()
return specialized_profile
class SpecializedProfileAdmin(admin.ModelAdmin):
form = CreateSpecializedProfileAdminForm
admin.site.register(SpecializedProfile, SpecializedProfileAdmin)

https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.form
remove class Meta and move exclude = ['profile'] to ModelAdmin
class SpecializedProfileAdmin(admin.ModelAdmin):
exclude = ['profile']
form = CreateSpecializedProfileAdminForm

Related

How to create User and User Profile in a single Django Admin form

I am struggling to figure out how to save User Profile for a new user created within Django Admin.
I have a custom User model and a simple user Profile with OneToOneField:
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('email address'), unique=True)
is_staff = models.BooleanField(_('staff'), default=False)
is_active = models.BooleanField(_('active'), default=True)
...
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
first_name = models.CharField(_('first name'), max_length=80, blank=False)
last_name = models.CharField(_('last name'), max_length=80, blank=False)
...
I have the following user creation form:
class UserAdminCreationForm(forms.ModelForm):
password1 = forms.CharField(label="Password", widget=forms.PasswordInput)
password2 = forms.CharField(
label="Password confirmation", widget=forms.PasswordInput
)
first_name = forms.CharField(label="First name")
last_name = forms.CharField(label="Last name")
class Meta:
model = User
fields = ("email",)
def save(self, commit=True):
user = super(UserAdminCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
My admin Add user form is rendered correctly and includes fields from both User and Profile models. After saving the form, a new user and a new profile are created in the database. However, the first_name and last_name fields in profile table are empty.
What I need to do to ensure that profile is saved together with the new user?
UPDATE
Overwriting the save method and ignoring the commit parameter worked for me:
def save(self, commit=True):
user = super(UserAdminCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
user.save()
profile, created = Profile.objects.update_or_create(user=user)
profile.first_name = self.cleaned_data["first_name"]
profile.last_name = self.cleaned_data["last_name"]
profile.save()
return user
Rather than making a custom form you can edit both models in one admin change form by using an inline.
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from .models import Profile
class ProfileInline(admin.StackedInline):
model = Profile
can_delete = False
verbose_name_plural = 'Profile'
fk_name = 'user'
class CustomUserAdmin(UserAdmin):
inlines = (ProfileInline, )
def get_inline_instances(self, request, obj=None):
if not obj:
return list()
return super().get_inline_instances(request, obj)
admin.site.unregister(User)
admin.site.register(User, CustomUserAdmin)
Since you're already defining your own custom user model, I would recommend doing away with the profile model entirely. It's just going to cause excess queries retrieving profile fields from the user instances.
UDPATE if you want to continue using your form:
def save(self, commit=True):
user = super(UserAdminCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
Profile.objects.update_or_create(
user=user,
defaults={
'first_name': self.cleaned_data['first_name'],
'last_name': self.cleaned_data['last_name'],
}
)
profile.first_name = self.cleaned_data["first_name"]
profile.save()
return user

django registration and login with custom user model

Custom User Model (models.py):
class Users(AbstractBaseUser):
user_id = models.AutoField(primary_key=True)
user_email = models.EmailField(max_length=100, unique=True)
password = models.CharField(max_length=100)
registration_date = models.DateField(auto_now_add=True)
last_login = models.DateField(auto_now_add=True)
is_administrator = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
email_verified = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'user_email'
Custom User Manager (models.py)
class UserManager(BaseUserManager):
def create_user(self, user_email, password):
user = self.model(
email=self.normalize_email(user_email)
)
user.set_password(password)
user.save(using=self._db)
return user
Registration Form (forms.py):
class RegistrationForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput)
password_confirm = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = get_user_model()
fields = ('user_email',)
def clean_password(self):
password1 = self.cleaned_data.get("password")
password2 = self.cleaned_data.get("password_confirm")
if password1 and password2 and password1 != password2:
raise forms.ValidationError( self.error_messages['password_mismatch'],
code='password_mismatch' )
return password2
def save(self, commit=True):
user = super(RegistrationForm, self).save(commit=False)
user.registration_date = datetime.date.today()
user.last_login = datetime.date.today()
user.set_password("password")
if commit:
user.save()
return user
Register view (views.py)
def register(request):
if request.method == "POST":
form = RegistrationForm(request.POST)
if form.is_valid:
form.save()
else:
form = RegistrationForm()
return render(request, 'web/page.html', {'form' : form})
This is my first question on .. anything online (I'm an avid user of the search facility), but I've been struggling with this for days and I feel like it shouldn't be so difficult.
For my question, it is more like a two part query.
1) I want a custom user model, but want to use as much of what Django is offering. But getting rid of the username field and adding a couple other fields seems to require me to make "custom" everything else. Including a custom user manager and custom forms - not ideal! The registration form above works but I was hoping for something a bit more simple, along the lines of:
class RegistrationForm(UserCreationForm):
class Meta(UserCreationForm.Meta):
model = get_user_model()
fields = ('user_email', 'password1', 'password2')
but I seem to be struggling, running it gives me the error:
The Users could not be created because the data didn't validate.
Ideally I would want something simple like that without the error. I'm hoping it to give fewer errors and hopefully more future proof if Django adds new security features.
2) Why tf won't the login code below work?
Login Form (forms.py):
class LoginForm(forms.Form):
user_email = forms.EmailField(max_length=100)
password = forms.CharField(max_length=100)
Login View (views.py):
def login_user(request):
if request.method == "POST":
user_email = request.POST.get('email')
password = request.POST.get('password')
UserModel = get_user_model()
user = UserModel.objects.get(user_email=user_email)
if user.check_password(password):
login(request, user)
return render(request, 'login.html')
I'm pretty sure the problem lies with the second if statement. It just won't give a True value when the correct password is entered. Having tried print(user.user_id), the correct id is given for the email and password entered. Having played with some plaintext passwords, it works, but I rather like the set_password and check_password functions - provided they work!
Many thanks!
I worked it out. Both naive errors!
For point two, the Login View (views.py) code works well, the problem was the Registration Form (forms.py). In particular:
user.set_password("password")
was literally saving 'password' as the password. Changed to:
user.set_password(self.cleaned_data["password_confirm"])
made it work.
Not that it matters (for point one), because the 4 line Registration Form inheriting the UserCreationForm worked all along and will replace the first one. Turns out the issue was I wasn't putting a valid password (minimum 8 letters). Why it validates the data in views.py during:
if form.is_valid:
but then tells me off later I have no idea. I wish Django would be a bit more detailed on what the issue was!
I would recommend that you look into Django all-auth. It's an open source solution to user registration and management that offers a lot of customization, and works well with custom user models.
https://github.com/pennersr/django-allauth

Using a registration form to add to custom user fields (django)

I'm creating custom user models for a registration form in django, but I'm having some problems getting the data into the database. Currently the users can register and their username, password, email, first name and last name are saved, but all the other form data isn't saving into the object. Below are my forms and the output:
Models.py
class Profile(models.Model):
user = models.OneToOneField(User)
gender = models.CharField(max_length=20)
medication = models.CharField(max_length=50, blank=True)
medical_history = models.CharField(max_length=50,blank=True)
DOB = models.CharField(max_length=20)
telephone = models.CharField(max_length=20)
address = models.CharField(max_length=30)
city = models.CharField(max_length=20)
state = models.CharField(max_length=20)
postcode = models.CharField(max_length=30)
forms.py:
class CreateAccountForm(ModelForm):
class Meta:
model = Profile
fields = ("username","password","password2","first_name","last_name",
"gender","medication","medical_history","DOB","email","telephone",
"address","city","state","postcode")
views.py:
def create_account(request):
form = CreateAccountForm(request.POST)
if form.is_valid():
username = form.cleaned_data['username']
password = form.cleaned_data['password']
password2 = form.cleaned_data['password2']
first_name = form.cleaned_data['first_name']
last_name = form.cleaned_data['last_name']
gender = form.cleaned_data['gender']
medication = form.cleaned_data['medication']
medical_history = form.cleaned_data['medical_history']
DOB = form.cleaned_data['DOB']
email = form.cleaned_data['email']
telephone = form.cleaned_data['telephone']
address = form.cleaned_data['address']
city = form.cleaned_data['city']
state = form.cleaned_data['state']
postcode = form.cleaned_data['postcode']
if password == password2:
if (password_verification(password)) == 3:
if (username_verification(username)) == False:
form.save()
return HttpResponseRedirect('/login')
else:
return HttpResponseRedirect('/create_account')
else:
return HttpResponseRedirect('/create_account')
else:
return HttpResponseRedirect('/create_account')
return render(request, "create_account.html", {'form': form})
In the django admin window, the user is registering to the database with the create_user fields, but none of the added custom fields are saving in the available columns. Any help would be great this is really bugging me. Below is a pic of the empty fields, cheers!
You are saving User only user.save() so other Profile ( please change your user table to something meaningful Profile.
And also you don't need password first_name last_name email in your profile as well.
And I'll suggest to use the ModelForm to save the Profile table.
user_form = CreateAccountForm(request.POST)
profile_form = ProfileForm(request.POST)
if user_form.is_valid() and profile_form.is_valid():
# save the form after your password check
user_form.save()
profile_form.save()

'SignupFormExtra' object has no attribute '_meta'

That's my first time using userena in Django. i want to override signup form of userena and now have an error.
from userena import settings as userena_settings
from userena.models import UserenaSignup
from userena.utils import get_profile_model
class SignupFormExtra(SignupForm):
"""
A form to demonstrate how to add extra fields to the signup form, in this
case adding the first and last name.
"""
cellPhone = forms.CharField(label=_(u'CellPhone'),
max_length=30,
required=False)
first_name = forms.CharField(label=_(u'First name'),
max_length=30,
required=False)
last_name = forms.CharField(label=_(u'Last name'),
max_length=30,
required=False)
def save(self):
"""
Override the save method to save the first and last name to the user
field.
"""
# First save the parent form and get the user.
new_user = super(SignupFormExtra, self).save()
# Get the profile, the `save` method above creates a profile for each
# user because it calls the manager method `create_user`.
# See: https://github.com/bread-and-pepper/django-userena/blob/master/userena/managers.py#L65
new_user = get_profile_model()
new_user.first_name = self.cleaned_data['first_name']
new_user.last_name = self.cleaned_data['last_name']
new_user.cellPhone = self.cleaned_data['cellPhone']
new_user.save(self)
# Userena expects to get the new user from this form, so return the new
# user.
return new_user
Actually i have class Profile in model.py and referred to userena like this:
class Profile(UserenaBaseProfile):
user = models.OneToOneField(User,
on_delete=models.CASCADE,
unique=True,
verbose_name=_('user'),
related_name='my_profile')
can anyone help me to fix it?
You dont need this part:
new_user = get_profile_model()
you get new_user from previous command, just save it and remove self from the new_user.save()
new_user.save(self) -> new_user.save()
def save(self):
"""
Override the save method to save the first and last name to the user
field.
"""
# First save the parent form and get the user.
new_user = super(SignupFormExtra, self).save()
new_user.first_name = self.cleaned_data['first_name']
new_user.last_name = self.cleaned_data['last_name']
new_user.cellPhone = self.cleaned_data['cellPhone']
new_user.save()
# Userena expects to get the new user from this form, so return the new
# user.
return new_user

How to populate two models using single Form

class CustomAccount(models.Model):
user = models.OneToOneField("auth.User")
role = models.CharField(max_length = 50, default = 'student', choices=APPROVAL_CHOICES)
balance = models.FloatField( default = 0 )
timezone = models.CharField(max_length = 200)
def __str__(self):
return self.user.username +" ["+ self.role + "]"
class CustomAccountForm(forms.ModelForm):
username = forms.CharField(max_length=30 )
email = forms.EmailField(max_length=255 )
password1 = forms.CharField(label= "Password",widget=forms.PasswordInput())
password2 = forms.CharField(label="Password confirmation", widget=forms.PasswordInput , help_text="Enter the same password as above, for verification.")
def save(self, commit= True):
user = User.objects.create_user(username = self.cleaned_data['username'], email = self.cleaned_data['email'] , password = self.cleaned_data['password1'])
user.save()
self.user = user
return super(CustomAccountForm, self).save(commit=commit)
def clean_username(self):
username = self.cleaned_data["username"]
try:
User.objects.get(username=username)
except User.DoesNotExist:
return username
raise forms.ValidationError("A user with that username already exists.")
def clean_password2(self):
password1 = self.cleaned_data.get("password1", "")
password2 = self.cleaned_data["password2"]
if password1 != password2:
raise forms.ValidationError("The two password fields didn't match.")
return password2
def clean_email(self):
email = self.cleaned_data["email"]
try:
User.objects.get(email=email)
except User.DoesNotExist:
return email
raise forms.ValidationError("A user with that emailaddress already exists.")
class Meta:
model = CustomAccount
exclude = ['balance','user']
I want to create Custom account in Django Admin section using single form which has fields of auth.User and CustomAccount Model. I have getting error IntegrityError at /admin/mylogin/customaccount/add/
NOT NULL constraint failed: mylogin_customaccount.user_id
Since you are getting a not null error, I think you need to specify that the field is not required. Here is an example from one of my apps. This says that the form field is not required.
class arunModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
myuser = kwargs.pop('user', None)
super(arunModelForm, self).__init__(*args, **kwargs)
self.fields['nanoadded'].required = False
self.fields['response'].required = False
I also include this in my models so that my model fields can be null, like this:
class arun(models.Model):
auser = models.CharField(max_length=50, null=True)
adate = models.DateField(null=True)
atime = models.TimeField(null=True)
If I still get not null errors after I do both of these things, I fill the field with a place holder value to allow me to further investigate the problem.
You have removed the user from the form and in the save method, you have assigned self.user in save method. So when saving the form, the user attribute is not used for CustomAccountForm and the user field for CustomAccount is None.
Your save method should look like:
def save(self, commit= True):
user = User.objects.create_user(username =self.cleaned_data['username'], email = self.cleaned_data['email'] , password = self.cleaned_data['password1'])
user.save()
obj = super(CustomAccountForm, self).save(commit=False)
obj.user = user
obj.save()