class SignUpForm(UserCreationForm):
class Meta:
model = models.User
fields = ["first_name", "last_name", "email"]
def clean_password1(self):
password = self.cleaned_data.get("password")
password1 = self.cleaned_data.get("password1")
print(password, password1)
if password != password1:
raise forms.ValidationError("비밀번호가 일치하지 않습니다.")
else:
return password
def clean_email(self):
email = self.cleaned_data.get("email")
try:
models.User.objects.get(username=email)
raise forms.ValidationError("이미 가입된 이메일 입니다", code="existing_user")
except models.User.DoesNotExist:
return email
def save(self, commit):
username = self.cleaned_data.get("email")
password = self.cleaned_data.get("password")
user = super().save(commit=False)
user.username = username
user.set_password(password)
user.save()
Here is my code for validation
and whenever i print password,
I really dont understand why
password1 is printed properly and password is none
i get none can anybody explain to me why this happen?
You need to indent properly.
Python is reading your code as own functions and class.
Statements that belong together must have the same indentation depth.
Related
I am a beginner and I'm still learning Django. I am writing my custom form validation for user profile update. My profile update form has -
username
first name
last name
profile image
First I have used default form validation. It is also good but I want to update the error message so I tried to create a custom validation function as I seen in a post in Stack Overflow. Please also check my username validation in forms.py
So what is the error now?
Now I am facing one more issues
If I click on update button without changing anything then it shows can't use this username. {username is set to unique=True in model fields}
If I don't update username and update just first name and last name. It doesn't allow this says same error. "Username is already taken" {because that username is validated and it already taken by current user"}
What exactly I want?
Instead of changing just error message now I want to write my own validation for this. I want if some click on update without changing any data no error message should be shown.
I can update each field independently. No error message should be shown if i don't update username.
My Views
#login_required()
def profile(request):
if request.method=='POST':
u_form = UserUpdateForm(request.POST,request.FILES,instance=request.user)
if u_form.is_valid():
u_form.save()
messages.success(request, f'Profile Updated Succesfully')
redirect('profile/')
else:
u_form = UserUpdateForm(instance=request.user)
notes = UserCreatedNote.objects.filter(user=request.user)
context = {'u_form': u_form,'notes':notes}
return render(request,'profile.html',context)
My models
from django.db import models
from django.contrib.auth.models import User
from django.contrib.auth.models import (
BaseUserManager, AbstractBaseUser
)
# ///////////////User Manager/////////////////////
# Create your models here.
# overriding the create and superuser funciton
class MyAccountManager(BaseUserManager):
def create_user(self,email,username,password=None):
if not email:
raise ValueError("Users Must Have email Address")
if not username:
raise ValueError("Users Must Have username")
user = self.model(
email=self.normalize_email(email),
username=username,
)
user.set_password(password)
user.save(using=self._db)
return user
def create_superuser(self,email,username,password):
user = self.create_user(
email = self.normalize_email(email),
username=username,
password=password,
)
user.is_admin = True
user.is_staff = True
user.is_superuser = True
user.save(using=self._db)
return user
# ////////////////////////////////////////////////////////////
def get_profile_image_filepath(self,filename):
return f'profile_images/{self.pk}/{filename}' #pk= primary key
def get_default_profile_image():
return "img/default_profile/default.png"
class KeepSafeUserModel(AbstractBaseUser):
first_name = models.CharField(verbose_name='first_name',max_length=30,default="")
last_name = models.CharField(verbose_name='last_name',max_length=30,default="")
email= models.TextField(verbose_name='email',max_length=60,unique=True,primary_key=True)
username = models.CharField(max_length=30,unique=True)
date_joined = models.DateTimeField(verbose_name="date joined",auto_now_add=True)
last_login = models.DateTimeField(verbose_name='last_login',auto_now=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
profile_image = models.ImageField(max_length=225,upload_to=get_profile_image_filepath,null=True,blank=True,default=get_default_profile_image)
#password field is buil-in
objects = MyAccountManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
def __str__(self):
return self.username
def has_perm(self,perm,obj=None):
return self.is_admin
def has_module_perms(self,app_Label):
return True
def get_profile_image_filename(self):
return str(self.profile_image)[str(self.profile_image).index('profile_images/{self.pk}/')]
forms.py
class UserUpdateForm(forms.ModelForm):
profile_image = forms.ImageField(required=False,error_messages ={'invalid':("Image files only")},widget=forms.FileInput)
class Meta:
model = KeepSafeUserModel
fields = ['username','first_name','last_name','profile_image']
labels ={
'username':'Username',
'first_name':'First Name',
'last_name':'Last Name',
'profile_image':'Profile Image'
}
def clean_username(self):
username = self.cleaned_data['username']
if not username.isdigit():
users = KeepSafeUserModel.objects.filter(username__iexact=username)
if users:
raise forms.ValidationError("Username has already taken by someone else")
else:
raise forms.ValidationError("Username can't contains only numbers")
return username
The problem is that if you update your KeepSafeUserModel, you will have a hit for KeepSafeUserModel.objects.filter(username__iexact=username): indeed, it will simply match with itself.
You can simply exclude the object with:
def clean_username(self):
username = self.cleaned_data['username']
if not username.isdigit():
users = KeepSafeUserModel.objects.exclude(pk=self.instance.pk).filter(
username__iexact=username
).exists()
if users:
raise forms.ValidationError"Username has already taken by someone else")
else:
raise forms.ValidationError("Username can't contains only numbers")
return username
I think you already pass the instance of user logged in to the form. So you need to make the clean_username(self) validation become like this.
def clean_username(self):
username = self.cleaned_data['username']
if not username.isdigit():
users = KeepSafeUserModel.objects.filter(username__iexact=username)
if users:
if users.username != self.instance.username:
raise forms.ValidationError("Username has already taken by someone else")
else:
raise forms.ValidationError("Username can't contains only numbers")
return username
It will validate the username given and stored is not same
This user keyword error is showing in my login api. I am not sure why. I am guessing user instance is not available in the login view. I am new and I dont know the reason.
This is my login view:
class LoginUserView(GenericAPIView):
permission_classes = [AllowAny]
serializer_class = UserLoginSerializer
def post(self, request, *args, **kwargs):
data = request.data
serializer = UserLoginSerializer(data=data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data["user"]
token, created = Token.objects.get_or_create(user=user)
return response.Response({"token": token.key}, status=status.HTTP_200_OK)
This is my serializers:
class UserLoginSerializer(serializers.ModelSerializer):
email = serializers.EmailField(label='Email Address')
class Meta:
model = User
fields = [
'email', 'password',
]
extra_kwargs = {"password":
{"write_only": True}}
def validate(self, data):
# user = None
email = data.get("email", None)
password = data.get("password")
if not email:
raise serializers.ValidationError("Email is required for login")
if not password:
raise serializers.ValidationError("Password is required for login")
user = authenticate(email=email, password=password)
if not user:
raise serializers.ValidationError("This email is not valid/already exists")
return data
The serializer has no user in its validated data. Note that serializer.validated_data['user'] makes not much sense, since you never passed this to the validator.
You should add it to the data with:
def validate(self, data):
# user = None
email = data.get('email', None)
password = data.get('password')
if not email:
raise serializers.ValidationError('Email is required for login')
if not password:
raise serializers.ValidationError('Password is required for login')
user = authenticate(email=email, password=password)
if not user:
raise serializers.ValidationError('This email is not valid/already exists')
data['user'] = user
return data
I try to make a login using a LoginView class. I just can't login. It always says that my password is wrong but it isn't (I checked it with check_password()). I already set AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.ModelBackend'] in my settings.py.
Here's my clean function in my custom AuthenticationForm:
def clean(self):
cleaned_data = self.cleaned_data
username = cleaned_data.get("username")
password = cleaned_data.get("password")
user = authenticate(username=username, password=password)
print(user)
print(username)
print(password)
try:
us = User.objects.get(username=username)
print(us)
print(us.check_password(password)) # This returns True!!!
except ObjectDoesNotExist:
pass
if user is not None:
if not user.is_active:
raise forms.ValidationError("Dieser Account ist deaktiviert. Du kannst ihn über deine E-Mail aktivieren.")
else:
raise forms.ValidationError("Falsches Passwort")
return cleaned_data
The ModelBackend authenticate method does this:
def authenticate(self, request, username=None, password=None, **kwargs):
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
try:
user = UserModel._default_manager.get_by_natural_key(username)
except UserModel.DoesNotExist:
# Run the default password hasher once to reduce the timing
# difference between an existing and a nonexistent user (#20760).
UserModel().set_password(password)
else:
if user.check_password(password) and self.user_can_authenticate(user):
return user
and user_can_authenticate is:
def user_can_authenticate(self, user):
"""
Reject users with is_active=False. Custom user models that don't have
that attribute are allowed.
"""
is_active = getattr(user, 'is_active', None)
return is_active or is_active is None
Does your user have is_active set to True?
I'm trying to validate age for user during creation by rest-auth. I managed to add field and save it during registration, but now I'm having hard time to validate if age is < 18.
Someone could point me at the way I should do it?
I have tried with validation through my AbstractUser model, with #property method, and it was raising ValidationError during registration, but the User account was saving anyway, and i couldn't access to user detail view because of the ValidationError, so I came to the conclusion that I would just prefer to prevent registration through validation, but it isn't working in my case.
class RegisterSerializer(serializers.Serializer):
username = serializers.CharField(
max_length=get_username_max_length(),
min_length=allauth_settings.USERNAME_MIN_LENGTH,
required=allauth_settings.USERNAME_REQUIRED
)
email = serializers.EmailField(required=allauth_settings.EMAIL_REQUIRED)
date_of_birthday = serializers.DateField() ### ADDED BY ME
password1 = serializers.CharField(write_only=True)
password2 = serializers.CharField(write_only=True)
def validate_username(self, username):
username = get_adapter().clean_username(username)
return username
def validate_email(self, email):
email = get_adapter().clean_email(email)
if allauth_settings.UNIQUE_EMAIL:
if email and email_address_exists(email):
raise serializers.ValidationError(
_("A user is already registered with this e-mail address."))
return email
def validate_age(self, date_of_birthday): ### ADDED BY ME
age = relativedelta(datetime.now(), date_of_birthday).years
if age < 18:
raise serializers.ValidationError('Must be at least 18 years old to register.')
else:
return age
def validate_password1(self, password):
return get_adapter().clean_password(password)
def validate(self, data):
if data['password1'] != data['password2']:
raise serializers.ValidationError(_("The two password fields didn't match."))
return data
def custom_signup(self, request, user):
pass
def get_cleaned_data(self):
return {
'username': self.validated_data.get('username', ''),
'date_of_birthday': self.validated_data.get('date_of_birthday', ''), ### ADDED BY ME
'password1': self.validated_data.get('password1', ''),
'email': self.validated_data.get('email', '')
}
def save(self, request):
adapter = get_adapter()
user = adapter.new_user(request)
self.cleaned_data = self.get_cleaned_data()
adapter.save_user(request, user, self)
self.custom_signup(request, user)
setup_user_email(request, user, [])
user.date_of_birth = self.cleaned_data.get('date_of_birthday') ### ADDED BY ME
user.save() ### ADDED BY ME
return user
You can validated serializer fields by defining methods prefixing validate_ to the field name. In your case, replace validate_age with validate_date_of_birthday
def validate_date_of_birthday(self, date_of_birthday):
age = relativedelta(datetime.now(), date_of_birthday).years
if age < 18:
raise serializers.ValidationError('Must be at least 18 years old to register.')
else:
return date_of_birthday
I'm having trouble overriding clean method of a built-in Django form (django.contrib.auth.SetPasswordForm). This form has two fields: new_password1 and new_password2.
so in my views.py I call the customized form (MySetPasswordForm):
def reset_confirm(request, uidb64=None, token=None):
return password_reset_confirm_delegate(request,
template_name='app/reset_confirm.html',
set_password_form = MySetPasswordForm, uidb64=uidb64,
token=token, post_reset_redirect=reverse('main_page'))
In my forms.py: I want to define my own clean method to show my customized error messages. here's how I wrote MySetPasswordForm:
from django.contrib.auth.forms import SetPasswordForm
class MySetPasswordForm(SetPasswordForm):
error_messages = { 'password_mismatch': _("Missmatch!"), }
def clean(self):
password1 = self.cleaned_data.get('new_password1', '')
password2 = self.cleaned_data.get('new_password2', '')
print password1 #prints user's entered value
print password2 #prints nothing!!
print self.data['new_password2'] #prints user's entered value
if password1 == '':
self._errors["new_password1"] = ErrorList([u"enter pass1!"])
if password2 == '':
self._errors["new_password2"] = ErrorList([u"enter pass2"])
elif password1 != password2:
raise forms.ValidationError(
self.error_messages['password_mismatch'],
code='password_mismatch',
)
return self.cleaned_data
The problem is that when the user enter the repeat password wrong, instead of getting "Missmatch" error, it gives "enter pass2"! Also print password2 doesn't print user's entered value for password2.
What am I doing wrong in this code?! and what is the best way to do customized error messages?
p.s. using the original SetPasswordForm in the view works fine.
The SetPasswordForm checks that new_password1 and new_password2 match in the clean_new_password2 method.
When the passwords do not match, new_password2 is not included in self.cleaned_data, so you can't access it in the clean method.
If you want to override the error message for mismatched passwords, then setting it in the error_messages dict is the correct approach. I would then remove the clean method from your form.
If you need a different required error message for each field, you could set it in the __init__ method.
class MySetPasswordForm(SetPasswordForm):
error_messages = {
'password_mismatch': _("Missmatch!"),
'required': _("Please enter a password"), # If you do not require the fieldname in the error message
}
def __init__(self, *args, **kwargs):
super(MySetPasswordForm, self).__init__(*args, **kwargs)
self.fields['new_password1'].error_messages['required'] = _("enter pass1!")
When you call the clean method of form super method def clean_new_password2(self) all ready is called, so self.cleaned_data['new_password2'] is empty You need override the clean_new_password2 in your form, look for source auth forms
class MySetPasswordForm(SetPasswordForm):
def clean_new_password2(self):
password1 = self.cleaned_data.get('new_password1')
password2 = self.cleaned_data.get('new_password2')
if password1 and password2:
if password1 != password2:
raise forms.ValidationError(
self.error_messages['password_mismatch'],
code='password_mismatch',
)
return password2