I'm new to Django, and I've been fighting with this form for over a week now. I started this first because it is the core of my project. What I ultimately want is a single form that has a condition that the user can set, to add data to a second form when they check a radio box (will do that in jquery). I want to give admins the ability to register users, but I have a special subclass of users called operators that need additional information in a separate model. I almost have it working right now, but all users get added to the special subclass. Please help!
EDIT: What's not working is that I want to have the Admins register a user on a single form that can create users and if they check a button, then fill out the rest of the form for operators. I have the second part working (they can create an operator), but I also want them be able to create a regular user with the same form (that doesn't use the operator model). Can this be done?
Here is my code. NOTE: I messed up the password registration in this code, but I'll fix that later. Just working on this core functionality right now.
Models
class UserProfile(AbstractUser):
bio = models.TextField(max_length=500, blank=True)
birth_date = models.DateField(null=True, blank=True)
profile_pic = models.ImageField(null=True, blank=True)
notes = models.TextField(null=True, blank=True)
def __str__(self):
return self.first_name + ' ' + self.last_name
class OperatorProfile(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
cdl = models.ManyToManyField('CDL', blank=True)
endorsement = models.ManyToManyField('Endorsement', blank=True)
cdl_expiration = models.DateField(blank=True, null=True)
def __str__(self):
return str(self.user)
Views
class OperatorCreateView(CreateView):
model = OperatorProfile
template_name = 'pages/operatorprofile_form.html'
form_class = UserCreationMultiForm
success_url = reverse_lazy('index')
def form_valid(self, form):
# Save the user first, because the profile needs a user before it
# can be saved.
user = form['user'].save()
user.groups.add(Group.objects.get(name='Operators'))
profile = form['profile'].save(commit=False)
profile.user = user
profile.save()
form['profile'].save_m2m()
return redirect(reverse_lazy('index'))
Forms
# Operator Creation Form
class OperatorProfileForm(forms.ModelForm):
class Meta:
model = OperatorProfile
exclude = ['user']
class UserProfileForm(forms.ModelForm):
first_name = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control'}))
last_name = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control'}))
username = forms.CharField(widget=forms.TextInput(attrs={'class': 'form-control'}))
password = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'form-control'}))
password_confirm = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'form-control'}))
email = forms.CharField(widget=forms.EmailInput(attrs={'class': 'form-control'}))
bio = forms.CharField(widget=forms.Textarea(attrs={'class': 'form-control'}))
def clean_password2(self):
password = self.cleaned_data.get('password1')
password_confirm = self.cleaned_data.get('password2')
if not password_confirm:
raise forms.ValidationError("You must confirm your password")
if password != password_confirm:
raise forms.ValidationError("Your passwords do not match")
return password_confirm
class Meta:
model = UserProfile
fields = ['username', 'password', 'password_confirm', 'first_name', 'last_name', 'bio']
class UserCreationMultiForm(MultiModelForm):
form_classes = {
'user': UserProfileForm,
'profile': OperatorProfileForm,
}
Related
i created a custom form from the Django-allauth package adding a first name, Lastname and is_staff. This was after i had extended the user model using abstart user. i think it has something to do with is_staff being a boolean value. All suggestions are welcome. thank you.
My user model looks like this
from django.contrib.auth.models import AbstractUser, UserManager
class CustomUserManager(UserManager):
def get_by_natural_key(self, username):
case_insensitive_username_field = '{}__iexact'.format(self.model.USERNAME_FIELD)
return self.get(**{case_insensitive_username_field: username})
class User(AbstractUser):
is_student = models.BooleanField('student status', default=False)
location = models.CharField(max_length=30, blank=True)
# first_name= models.CharField( blank=False, max_length=30, verbose_name='first name')
email = models.EmailField(unique=True)
objects = CustomUserManager()
My custom form is as shown below
from django.contrib.auth.models import User, Group
class CustomSignupForm(SignupForm):
first_name = forms.CharField(max_length=30, label='First Name')
last_name = forms.CharField(max_length=30, label='Last Name')
is_staff = forms.BooleanField(required=True ,label = 'Are you been assigned as a staff?')
def signup(self, request, user):
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
return user
Update your signup method and add the following line before save,
user.is_staff = self.cleaned_data['is_staff']
In my project I have two 'types' of users: Customers and Businesses. They are an extension of the django base User from django.contrib.auth.models.User.
I have in my models.py:
class Customer(models.Model):
user = models.OneToOneField(User, related_name='user', on_delete=models.CASCADE)
birth_date = models.DateField(blank=True, null=True)
phone = PhoneNumberField(unique=True)
def __str__(self):
return self.user.username
class Business(models.Model):
user = models.OneToOneField(User, related_name='business', on_delete=models.CASCADE)
cf = models.CharField(max_length=16, validators=[ssn_validation])
birth_date = models.DateField(null=True)
city = models.CharField(max_length=50, blank=False)
address = models.CharField(max_length=150, blank=False)
Ok, then I have two different registration, one for Customers and one for Businesses.
A problem is that, to validate the password, sent from a REST API, I need to compare password with password2, create a User (django base), and pass it to my Customer.objects.create, like:
I have in my serializers.py:
class CustomerRegistationSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='user.username',
validators=[UniqueValidator(queryset=User.objects.all())])
email = serializers.CharField(source='user.email',
validators=[UniqueValidator(queryset=User.objects.all())])
first_name = serializers.CharField(source='user.first_name')
last_name = serializers.CharField(source='user.last_name')
password = serializers.CharField(source='user.password', write_only=True)
password2 = serializers.CharField(style={'input_style': 'password'}, write_only=True)
birth_date = serializers.CharField(required=False)
class Meta:
model = Customer
fields = ['id', 'username', 'email', 'password', 'password2', 'first_name', 'last_name',
'birth_date', 'phone']
def save(self):
username = self.validated_data['user']['username']
password = self.validated_data['user']['password']
password2 = self.validated_data['password2']
email = self.validated_data['user']['email']
first_name = self.validated_data['user']['first_name']
last_name = self.validated_data['user']['last_name']
phone = self.validated_data['phone']
try:
birth_date = self.validated_data['birth_date']
except KeyError:
birth_date = None
if password != password2:
raise serializers.ValidationError({'password': 'Passwords must match!'})
user = User.objects.create(username=username, email=email, first_name=first_name, last_name=last_name)
user.set_password(password)
user.is_active = False
user.save()
customer = Customer.objects.create(user=user,
birth_date=birth_date,
phone=phone)
return customer
That's actually working, but in case of errors can happen that a User is created, but a Customer not.
Is there a cleaner way to make Customers registration, always checking for password == password2?
EDIT: I found a more elegant way to handle this:
#transaction.atomic
def save(self):
password = self.validated_data['user']['password']
password2 = self.validated_data['password2']
user = User.objects.create(**self.validated_data['user'])
if password != password2:
raise serializers.ValidationError({'password': 'Passwords must match!'})
user.set_password(password)
user.is_active = False
user.save()
update_last_login(None, user)
del self.validated_data['user']
del self.validated_data['password2']
customer = Customer.objects.create(user=user, **self.validated_data)
return customer
If you want to require that all of the DB transactions you are making during save() method are successful to effectively write it on DB, and not to write anything if there is an error at any point in the process, you are typically asking for atomicity (one of the four ACID capabilities of a Database)
Use this Django decorator, typically made for this:
from django.db import transaction
#transaction.atomic
def save(self):
<...>
I am facing one issue with django forms
Here is my model :
class User(models.Model):
email = models.EmailField(unique=True)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class UserProfile(AuditFields):
user = models.ForeignKey(User, on_delete=models.CASCADE)
designation = models.CharField(max_length=200, blank=True)
contact_number = models.CharField(max_length=20, blank=True)
team = models.CharField(max_length=200, blank=True)
manager = models.CharField(max_length=200, blank=True)
joining_date = models.DateField(default=datetime.now)
I need to create a form for editing profile details of the current user
This is my form. But it is a model Form so only getting the detauls from the User Profile table only
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
exclude = ['user']
How can I get first_name , last_name from User table and save it
Just add the fields as a CharField in form, and use cleaned_data attribute to fetch the data and save it:
class UserProfileForm(forms.ModelForm):
first_name = forms.CharField(max_length=30,required=True)
last_name = forms.CharField(max_length=30,required=True)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance:
self.fields['first_name'].initial = self.instance.user.first_name
self.fields['last_name'].initial = self.instance.user.last_name
class Meta:
model = UserProfile
exclude = ['user']
def save(self, commit=False):
instance = super().save(commit=True)
user = instance.user
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
return instance
I have the following model
class Organizer(models.Model):
name = models.CharField(max_length=100)
phone = models.CharField(max_length=20, default="", blank=True)
created_at = models.DateTimeField(default=timezone.now)
created_by = models.ForeignKey(User, on_delete=models.CASCADE, blank=True)
I have a field created_by of all the users. I don't want to show on the front end in admin form during adding form. how to do that. and save the current login user id
You need to add an admin form in admin.py and register that form to the model:
admin.py:
class OrganizerAdmin(admin.ModelAdmin):
fields = ('name', 'phone', 'created_at')
admin.site.register(Organizer, OrganizerAdmin)
you can read more about fields in admin page here
for hiding created_by
class OrganizerAdmin(admin.ModelAdmin):
// show fileds on form
fields = ('name', 'phone', 'created_at')
admin.site.register(Organizer, OrganizerAdmin)
and for save login user
def save_model(self, request, instance, form, change):
user = request.user
instance = form.save(commit=False)
if not change or not instance.created_by:
instance.created_by = user
instance.modified_by = user
instance.save()
form.save_m2m()
return instance
I have a model field called nearbyzips1, and I have a list of integers called in zip_codes. I am trying to pass the value of zip_codes[1] to nearbyzips1, but for some reason it won't save. When I fill out the form and hit submit, I can see all the values from each field except for nearbyzips1 which returns None.
if form.is_valid():
cleanzipcode= form.cleaned_data['zipcode']
nearestzips = PostalCode.objects.distance(PostalCode.objects.get(code=cleanzipcode).location).order_by('distance')[:8]
zip_codes = list(nearestzips.values_list('code', flat=True))
//print zip_codes
form.cleaned_data['nearbyzips1'] = zip_codes[1]
//print form.cleaned_data['nearbyzips1']
profile = form.save()
I added some print statements to troubleshoot, and zip_codes prints a short list of integers as expected. form.cleaned_data['nearbyzips'] returns one of those integers from the list. So then why is it not saving? I am very confused.
I am suspecting the issue is in the code above, but just in case I have included some of my other code from forms.py and models.py in case it helps.
models.py
class MyProfile(UserenaBaseProfile):
user = models.OneToOneField(User,
unique=True,
verbose_name=_('user'),
related_name='my_profile')
streetaddress=models.CharField(null=True, blank=True, max_length=30)
city = models.CharField(null=True, blank=True, max_length=20)
state = models.CharField(null=True, blank=True, max_length=20)
zipcode = models.IntegerField(_('zipcode'),
max_length=5, null=True, blank=True)
nearbyzips1=models.IntegerField(null=True, blank=True, max_length=10)
forms.py
class EditProfileForm(forms.ModelForm):
""" Base form used for fields that are always required """
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 __init__(self, *args, **kw):
super(EditProfileForm, self).__init__(*args, **kw)
# Put the first and last name at the top
new_order = self.fields.keyOrder[:-2]
new_order.insert(0, 'first_name')
new_order.insert(1, 'last_name')
self.fields.keyOrder = new_order
class Meta:
model = get_profile_model()
exclude = ['user', 'privacy']
widgets = {
'deliveryoption': forms.RadioSelect(choices=[
(True, ' Yes'),
(False, ' No')
])}
def save(self, force_insert=False, force_update=False, commit=True):
profile = super(EditProfileForm, self).save(commit=commit)
# Save first and last name
user = profile.user
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.save()
return profile
Should I maybe try pass the value to nearbyzips1 from inside the forms.py rather than the views.py?
You can try this instead:
# --- Replace this ---
# form.cleaned_data['nearbyzips1'] = zip_codes[1]
# print form.cleaned_data['nearbyzips1']
# profile = form.save()
# --- whith this ---
profile = form.save(commit=False)
profile.nearbyzips1 = zip_codes[1]
profile.save()
I really don't like to place business logic into ModelForm.save, prefer to place all business logic into the view.