664 lines
24 KiB
Python
664 lines
24 KiB
Python
"""
|
|
Forms for core app.
|
|
"""
|
|
|
|
from django import forms
|
|
from django.contrib.auth.forms import UserCreationForm as BaseUserCreationForm, PasswordChangeForm
|
|
from django.utils.translation import gettext_lazy as _
|
|
from .models import TenantSetting, SettingTemplate, Patient, Consent, ConsentTemplate, User
|
|
|
|
|
|
class PatientForm(forms.ModelForm):
|
|
"""Form for creating and updating patients."""
|
|
|
|
class Meta:
|
|
model = Patient
|
|
fields = [
|
|
'first_name_en', 'father_name_en', 'grandfather_name_en', 'last_name_en',
|
|
'first_name_ar', 'father_name_ar', 'grandfather_name_ar', 'last_name_ar',
|
|
'national_id', 'date_of_birth', 'sex',
|
|
'phone', 'email', 'address', 'city', 'postal_code',
|
|
'caregiver_name', 'caregiver_phone', 'caregiver_relationship',
|
|
'emergency_contact'
|
|
]
|
|
labels = {
|
|
'first_name_en': _('First Name (English)'),
|
|
'father_name_en': _("Father's Name (English)"),
|
|
'grandfather_name_en': _("Grandfather's Name (English)"),
|
|
'last_name_en': _('Last Name (English)'),
|
|
'first_name_ar': _('First Name (Arabic)'),
|
|
'father_name_ar': _("Father's Name (Arabic)"),
|
|
'grandfather_name_ar': _("Grandfather's Name (Arabic)"),
|
|
'last_name_ar': _('Last Name (Arabic)'),
|
|
'national_id': _('National ID'),
|
|
'date_of_birth': _('Date of Birth'),
|
|
'sex': _('Gender'),
|
|
'phone': _('Phone Number'),
|
|
'email': _('Email Address'),
|
|
'address': _('Address'),
|
|
'city': _('City'),
|
|
'postal_code': _('Postal Code'),
|
|
'caregiver_name': _('Caregiver Name'),
|
|
'caregiver_phone': _('Caregiver Phone'),
|
|
'caregiver_relationship': _('Relationship to Patient'),
|
|
'emergency_contact': _('Emergency Contact Information'),
|
|
}
|
|
widgets = {
|
|
'date_of_birth': forms.DateInput(attrs={'type': 'date', 'class': 'form-control'}),
|
|
'first_name_en': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'father_name_en': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'grandfather_name_en': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'last_name_en': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'first_name_ar': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'father_name_ar': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'grandfather_name_ar': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'last_name_ar': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'national_id': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'sex': forms.Select(attrs={'class': 'form-select'}),
|
|
'phone': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'email': forms.EmailInput(attrs={'class': 'form-control'}),
|
|
'address': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
|
|
'city': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'postal_code': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'caregiver_name': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'caregiver_phone': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'caregiver_relationship': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'emergency_contact': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
|
|
}
|
|
|
|
|
|
class ConsentForm(forms.ModelForm):
|
|
"""Form for creating consents with digital signature support."""
|
|
|
|
# Additional field for signature data (base64)
|
|
signature_data = forms.CharField(
|
|
required=False,
|
|
widget=forms.HiddenInput()
|
|
)
|
|
|
|
class Meta:
|
|
model = Consent
|
|
fields = ['patient', 'consent_type', 'content_text', 'signed_by_name',
|
|
'signed_by_relationship', 'signature_method']
|
|
widgets = {
|
|
'patient': forms.Select(attrs={'class': 'form-select select2'}),
|
|
'consent_type': forms.Select(attrs={'class': 'form-select'}),
|
|
'content_text': forms.HiddenInput(), # Hidden, populated via JS
|
|
'signed_by_name': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'signed_by_relationship': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'signature_method': forms.HiddenInput(), # Hidden, set to DRAWN
|
|
}
|
|
|
|
def clean_signature_data(self):
|
|
"""Validate signature data."""
|
|
signature_data = self.cleaned_data.get('signature_data')
|
|
|
|
if not signature_data:
|
|
raise forms.ValidationError('Signature is required.')
|
|
|
|
# Validate base64 format
|
|
if not signature_data.startswith('data:image/png;base64,'):
|
|
raise forms.ValidationError('Invalid signature format.')
|
|
|
|
return signature_data
|
|
|
|
def save(self, commit=True):
|
|
"""Save consent and process signature image."""
|
|
import base64
|
|
from django.core.files.base import ContentFile
|
|
from django.utils import timezone
|
|
import hashlib
|
|
|
|
instance = super().save(commit=False)
|
|
|
|
# Process signature data
|
|
signature_data = self.cleaned_data.get('signature_data')
|
|
if signature_data:
|
|
# Extract base64 data
|
|
format, imgstr = signature_data.split(';base64,')
|
|
ext = format.split('/')[-1]
|
|
|
|
# Decode base64 to binary
|
|
signature_binary = base64.b64decode(imgstr)
|
|
|
|
# Generate filename
|
|
timestamp = timezone.now().strftime('%Y%m%d_%H%M%S')
|
|
filename = f'signature_{instance.patient.mrn}_{timestamp}.{ext}'
|
|
|
|
# Save to signature_image field
|
|
instance.signature_image.save(
|
|
filename,
|
|
ContentFile(signature_binary),
|
|
save=False
|
|
)
|
|
|
|
# Calculate signature hash for verification
|
|
instance.signature_hash = hashlib.sha256(signature_binary).hexdigest()
|
|
|
|
# Set signed_at timestamp
|
|
if not instance.signed_at:
|
|
instance.signed_at = timezone.now()
|
|
|
|
if commit:
|
|
instance.save()
|
|
|
|
return instance
|
|
|
|
|
|
class PatientSearchForm(forms.Form):
|
|
"""Form for searching patients."""
|
|
|
|
search = forms.CharField(
|
|
required=False,
|
|
widget=forms.TextInput(attrs={
|
|
'class': 'form-control',
|
|
'placeholder': _('Search by name, MRN, phone, or national ID...')
|
|
})
|
|
)
|
|
status = forms.ChoiceField(
|
|
required=False,
|
|
choices=[('', _('All')), ('active', _('Active')), ('inactive', _('Inactive'))],
|
|
widget=forms.Select(attrs={'class': 'form-select'})
|
|
)
|
|
gender = forms.ChoiceField(
|
|
required=False,
|
|
choices=[('', _('All'))] + list(Patient.Sex.choices),
|
|
widget=forms.Select(attrs={'class': 'form-select'})
|
|
)
|
|
|
|
|
|
class TenantSettingsForm(forms.Form):
|
|
"""
|
|
Dynamic form for tenant settings based on templates.
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.tenant = kwargs.pop('tenant', None)
|
|
self.category = kwargs.pop('category', None)
|
|
self.user = kwargs.pop('user', None)
|
|
super().__init__(*args, **kwargs)
|
|
|
|
# Get templates for this category
|
|
templates = SettingTemplate.objects.filter(
|
|
category=self.category,
|
|
is_active=True
|
|
).order_by('order')
|
|
|
|
# Create form fields dynamically
|
|
for template in templates:
|
|
field = self._create_field_for_template(template)
|
|
self.fields[template.key] = field
|
|
|
|
# Set initial value if exists
|
|
try:
|
|
tenant_setting = TenantSetting.objects.get(
|
|
tenant=self.tenant,
|
|
template=template
|
|
)
|
|
if template.data_type == SettingTemplate.DataType.FILE:
|
|
self.initial[template.key] = tenant_setting.file_value
|
|
elif template.data_type == SettingTemplate.DataType.ENCRYPTED:
|
|
# Show placeholder for encrypted values
|
|
if tenant_setting.encrypted_value:
|
|
self.initial[template.key] = '***ENCRYPTED***'
|
|
elif template.data_type == SettingTemplate.DataType.BOOLEAN:
|
|
self.initial[template.key] = tenant_setting.value.lower() in ('true', '1', 'yes')
|
|
else:
|
|
self.initial[template.key] = tenant_setting.value
|
|
except TenantSetting.DoesNotExist:
|
|
if template.default_value:
|
|
self.initial[template.key] = template.default_value
|
|
|
|
def _create_field_for_template(self, template):
|
|
"""Create appropriate form field based on template data type."""
|
|
label = template.label_en
|
|
required = template.is_required
|
|
help_text = template.help_text_en
|
|
|
|
if template.data_type == SettingTemplate.DataType.STRING:
|
|
return forms.CharField(
|
|
label=label,
|
|
required=required,
|
|
help_text=help_text,
|
|
max_length=500,
|
|
widget=forms.TextInput(attrs={'class': 'form-control'})
|
|
)
|
|
|
|
elif template.data_type == SettingTemplate.DataType.TEXT:
|
|
return forms.CharField(
|
|
label=label,
|
|
required=required,
|
|
help_text=help_text,
|
|
widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 3})
|
|
)
|
|
|
|
elif template.data_type == SettingTemplate.DataType.INTEGER:
|
|
return forms.IntegerField(
|
|
label=label,
|
|
required=required,
|
|
help_text=help_text,
|
|
widget=forms.NumberInput(attrs={'class': 'form-control'})
|
|
)
|
|
|
|
elif template.data_type == SettingTemplate.DataType.BOOLEAN:
|
|
return forms.BooleanField(
|
|
label=label,
|
|
required=False,
|
|
help_text=help_text,
|
|
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
|
|
)
|
|
|
|
elif template.data_type == SettingTemplate.DataType.CHOICE:
|
|
choices = [(choice['value'], choice['label']) for choice in template.choices]
|
|
return forms.ChoiceField(
|
|
label=label,
|
|
required=required,
|
|
help_text=help_text,
|
|
choices=choices,
|
|
widget=forms.Select(attrs={'class': 'form-select'})
|
|
)
|
|
|
|
elif template.data_type == SettingTemplate.DataType.FILE:
|
|
return forms.FileField(
|
|
label=label,
|
|
required=False,
|
|
help_text=help_text,
|
|
widget=forms.FileInput(attrs={'class': 'form-control'})
|
|
)
|
|
|
|
elif template.data_type == SettingTemplate.DataType.ENCRYPTED:
|
|
return forms.CharField(
|
|
label=label,
|
|
required=False,
|
|
help_text=help_text + ' (Leave blank to keep current value)',
|
|
widget=forms.PasswordInput(attrs={'class': 'form-control'})
|
|
)
|
|
|
|
elif template.data_type == SettingTemplate.DataType.EMAIL:
|
|
return forms.EmailField(
|
|
label=label,
|
|
required=required,
|
|
help_text=help_text,
|
|
widget=forms.EmailInput(attrs={'class': 'form-control'})
|
|
)
|
|
|
|
elif template.data_type == SettingTemplate.DataType.URL:
|
|
return forms.URLField(
|
|
label=label,
|
|
required=required,
|
|
help_text=help_text,
|
|
widget=forms.URLInput(attrs={'class': 'form-control'})
|
|
)
|
|
|
|
elif template.data_type == SettingTemplate.DataType.PHONE:
|
|
return forms.CharField(
|
|
label=label,
|
|
required=required,
|
|
help_text=help_text,
|
|
widget=forms.TextInput(attrs={'class': 'form-control', 'type': 'tel'})
|
|
)
|
|
|
|
elif template.data_type == SettingTemplate.DataType.COLOR:
|
|
return forms.CharField(
|
|
label=label,
|
|
required=required,
|
|
help_text=help_text,
|
|
widget=forms.TextInput(attrs={'class': 'form-control', 'type': 'color'})
|
|
)
|
|
|
|
else:
|
|
return forms.CharField(
|
|
label=label,
|
|
required=required,
|
|
help_text=help_text,
|
|
widget=forms.TextInput(attrs={'class': 'form-control'})
|
|
)
|
|
|
|
def save(self):
|
|
"""Save form data to tenant settings."""
|
|
from .settings_service import get_tenant_settings_service
|
|
|
|
service = get_tenant_settings_service(self.tenant)
|
|
saved_count = 0
|
|
|
|
for key, value in self.cleaned_data.items():
|
|
# Skip encrypted fields with placeholder value
|
|
if value == '***ENCRYPTED***':
|
|
continue
|
|
|
|
# Skip empty encrypted fields (keep existing value)
|
|
template = SettingTemplate.objects.get(key=key)
|
|
if template.data_type == SettingTemplate.DataType.ENCRYPTED and not value:
|
|
continue
|
|
|
|
try:
|
|
service.set_setting(key, value, self.user)
|
|
saved_count += 1
|
|
except Exception as e:
|
|
# Log error but continue
|
|
print(f"Error saving setting {key}: {e}")
|
|
|
|
return saved_count
|
|
|
|
|
|
# ============================================================================
|
|
# USER PROFILE FORMS
|
|
# ============================================================================
|
|
|
|
class UserProfileForm(forms.ModelForm):
|
|
"""
|
|
Form for users to edit their own profile.
|
|
"""
|
|
|
|
class Meta:
|
|
model = User
|
|
fields = [
|
|
'first_name', 'last_name', 'email', 'phone_number',
|
|
'profile_picture', 'bio', 'timezone'
|
|
]
|
|
widgets = {
|
|
'first_name': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'last_name': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'email': forms.EmailInput(attrs={'class': 'form-control'}),
|
|
'phone_number': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'profile_picture': forms.FileInput(attrs={'class': 'form-control'}),
|
|
'bio': forms.Textarea(attrs={'class': 'form-control', 'rows': 4}),
|
|
'timezone': forms.Select(attrs={'class': 'form-select'}),
|
|
}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
|
|
# Add timezone choices
|
|
self.fields['timezone'].widget.choices = [
|
|
('Asia/Riyadh', _('Riyadh (GMT+3)')),
|
|
('Asia/Dubai', _('Dubai (GMT+4)')),
|
|
('Asia/Kuwait', _('Kuwait (GMT+3)')),
|
|
('Asia/Bahrain', _('Bahrain (GMT+3)')),
|
|
('Asia/Qatar', _('Qatar (GMT+3)')),
|
|
('UTC', _('UTC (GMT+0)')),
|
|
]
|
|
|
|
|
|
class UserPreferencesForm(forms.Form):
|
|
"""
|
|
Form for user preferences (stored in JSON field).
|
|
"""
|
|
|
|
language = forms.ChoiceField(
|
|
label=_("Preferred Language"),
|
|
choices=[('en', _('English')), ('ar', _('Arabic'))],
|
|
widget=forms.Select(attrs={'class': 'form-select'}),
|
|
required=False
|
|
)
|
|
|
|
email_notifications = forms.BooleanField(
|
|
label=_("Email Notifications"),
|
|
required=False,
|
|
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
|
|
)
|
|
|
|
sms_notifications = forms.BooleanField(
|
|
label=_("SMS Notifications"),
|
|
required=False,
|
|
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
|
|
)
|
|
|
|
appointment_reminders = forms.BooleanField(
|
|
label=_("Appointment Reminders"),
|
|
required=False,
|
|
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
|
|
)
|
|
|
|
dashboard_layout = forms.ChoiceField(
|
|
label=_("Dashboard Layout"),
|
|
choices=[
|
|
('default', _('Default')),
|
|
('compact', _('Compact')),
|
|
('detailed', _('Detailed'))
|
|
],
|
|
widget=forms.Select(attrs={'class': 'form-select'}),
|
|
required=False
|
|
)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.user = kwargs.pop('user', None)
|
|
super().__init__(*args, **kwargs)
|
|
|
|
# Load current preferences
|
|
if self.user and self.user.preferences:
|
|
for field_name in self.fields:
|
|
if field_name in self.user.preferences:
|
|
self.initial[field_name] = self.user.preferences[field_name]
|
|
|
|
def save(self):
|
|
"""Save preferences to user's preferences JSON field."""
|
|
if not self.user:
|
|
return
|
|
|
|
# Update preferences
|
|
if not self.user.preferences:
|
|
self.user.preferences = {}
|
|
|
|
for field_name, value in self.cleaned_data.items():
|
|
self.user.preferences[field_name] = value
|
|
|
|
self.user.save(update_fields=['preferences'])
|
|
|
|
|
|
class UserPasswordChangeForm(PasswordChangeForm):
|
|
"""
|
|
Custom password change form with Bootstrap styling.
|
|
"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
|
|
# Add Bootstrap classes
|
|
for field_name in self.fields:
|
|
self.fields[field_name].widget.attrs['class'] = 'form-control'
|
|
|
|
|
|
class UserAdminForm(forms.ModelForm):
|
|
"""
|
|
Form for admins to create/edit staff members.
|
|
"""
|
|
|
|
class Meta:
|
|
model = User
|
|
fields = [
|
|
'username', 'first_name', 'last_name', 'email',
|
|
'phone_number', 'employee_id', 'role',
|
|
'is_active', 'is_staff', 'profile_picture', 'bio'
|
|
]
|
|
widgets = {
|
|
'username': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'first_name': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'last_name': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'email': forms.EmailInput(attrs={'class': 'form-control'}),
|
|
'phone_number': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'employee_id': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'role': forms.Select(attrs={'class': 'form-select'}),
|
|
'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
|
'is_staff': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
|
'profile_picture': forms.FileInput(attrs={'class': 'form-control'}),
|
|
'bio': forms.Textarea(attrs={'class': 'form-control', 'rows': 4}),
|
|
}
|
|
|
|
password1 = forms.CharField(
|
|
label=_("Password"),
|
|
required=False,
|
|
widget=forms.PasswordInput(attrs={'class': 'form-control'}),
|
|
help_text=_("Leave blank to keep current password (for updates)")
|
|
)
|
|
|
|
password2 = forms.CharField(
|
|
label=_("Password Confirmation"),
|
|
required=False,
|
|
widget=forms.PasswordInput(attrs={'class': 'form-control'}),
|
|
help_text=_("Enter the same password as before, for verification")
|
|
)
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
|
|
# Make password required for new users
|
|
if not self.instance.pk:
|
|
self.fields['password1'].required = True
|
|
self.fields['password2'].required = True
|
|
|
|
def clean_password2(self):
|
|
"""Validate that passwords match."""
|
|
password1 = self.cleaned_data.get('password1')
|
|
password2 = self.cleaned_data.get('password2')
|
|
|
|
if password1 and password2 and password1 != password2:
|
|
raise forms.ValidationError(_("Passwords don't match"))
|
|
|
|
return password2
|
|
|
|
def save(self, commit=True):
|
|
"""Save user with password if provided."""
|
|
user = super().save(commit=False)
|
|
|
|
# Set password if provided
|
|
password = self.cleaned_data.get('password1')
|
|
if password:
|
|
user.set_password(password)
|
|
|
|
if commit:
|
|
user.save()
|
|
|
|
return user
|
|
|
|
|
|
class UserSearchForm(forms.Form):
|
|
"""
|
|
Form for searching staff members.
|
|
"""
|
|
|
|
search = forms.CharField(
|
|
required=False,
|
|
widget=forms.TextInput(attrs={
|
|
'class': 'form-control',
|
|
'placeholder': _('Search by name, email, or employee ID...')
|
|
})
|
|
)
|
|
|
|
role = forms.ChoiceField(
|
|
required=False,
|
|
choices=[('', _('All Roles'))] + list(User.Role.choices),
|
|
widget=forms.Select(attrs={'class': 'form-select'})
|
|
)
|
|
|
|
status = forms.ChoiceField(
|
|
required=False,
|
|
choices=[
|
|
('', _('All')),
|
|
('active', _('Active')),
|
|
('inactive', _('Inactive'))
|
|
],
|
|
widget=forms.Select(attrs={'class': 'form-select'})
|
|
)
|
|
|
|
|
|
# ============================================================================
|
|
# USER SIGNUP FORM
|
|
# ============================================================================
|
|
|
|
class ConsentTemplateForm(forms.ModelForm):
|
|
"""Form for creating and updating consent templates."""
|
|
|
|
class Meta:
|
|
model = ConsentTemplate
|
|
fields = [
|
|
'consent_type', 'title_en', 'title_ar',
|
|
'content_en', 'content_ar', 'is_active', 'version'
|
|
]
|
|
widgets = {
|
|
'consent_type': forms.Select(attrs={'class': 'form-select'}),
|
|
'title_en': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'title_ar': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'content_en': forms.Textarea(attrs={
|
|
'class': 'form-control',
|
|
'rows': 10,
|
|
'placeholder': _('Use placeholders: {patient_name}, {patient_mrn}, {date}, {patient_dob}, {patient_age}')
|
|
}),
|
|
'content_ar': forms.Textarea(attrs={
|
|
'class': 'form-control',
|
|
'rows': 10,
|
|
'placeholder': _('Use placeholders: {patient_name}, {patient_mrn}, {date}, {patient_dob}, {patient_age}')
|
|
}),
|
|
'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
|
'version': forms.NumberInput(attrs={'class': 'form-control'}),
|
|
}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
|
|
# Make version read-only for updates
|
|
if self.instance.pk:
|
|
self.fields['version'].disabled = True
|
|
self.fields['version'].help_text = _('Version is auto-incremented on save')
|
|
|
|
|
|
class UserSignupForm(BaseUserCreationForm):
|
|
"""
|
|
Custom user signup form with additional fields.
|
|
"""
|
|
|
|
email = forms.EmailField(
|
|
label=_("Email Address"),
|
|
required=True,
|
|
widget=forms.EmailInput(attrs={'class': 'form-control fs-13px h-45px'})
|
|
)
|
|
|
|
first_name = forms.CharField(
|
|
label=_("First Name"),
|
|
required=False,
|
|
max_length=150,
|
|
widget=forms.TextInput(attrs={'class': 'form-control fs-13px h-45px'})
|
|
)
|
|
|
|
last_name = forms.CharField(
|
|
label=_("Last Name"),
|
|
required=False,
|
|
max_length=150,
|
|
widget=forms.TextInput(attrs={'class': 'form-control fs-13px h-45px'})
|
|
)
|
|
|
|
class Meta:
|
|
model = User
|
|
fields = ('username', 'email', 'first_name', 'last_name', 'password1', 'password2')
|
|
widgets = {
|
|
'username': forms.TextInput(attrs={'class': 'form-control fs-13px h-45px'}),
|
|
}
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
|
|
# Add Bootstrap classes to password fields
|
|
self.fields['password1'].widget.attrs['class'] = 'form-control fs-13px h-45px'
|
|
self.fields['password2'].widget.attrs['class'] = 'form-control fs-13px h-45px'
|
|
|
|
def clean_email(self):
|
|
"""Validate that email is unique."""
|
|
email = self.cleaned_data.get('email')
|
|
|
|
if User.objects.filter(email=email).exists():
|
|
raise forms.ValidationError(_("A user with this email already exists."))
|
|
|
|
return email
|
|
|
|
def save(self, commit=True):
|
|
"""Save user with email."""
|
|
user = super().save(commit=False)
|
|
user.email = self.cleaned_data['email']
|
|
user.first_name = self.cleaned_data.get('first_name', '')
|
|
user.last_name = self.cleaned_data.get('last_name', '')
|
|
|
|
if commit:
|
|
user.save()
|
|
|
|
return user
|