I am quite new to the Django testing software. Right at the moment I am trying to create a test class for validators connected with given form ( process of cleaning the input data):
forms.py:
class SignupForm(forms.Form):
email = forms.EmailField(max_length=200)
first_name = forms.CharField(max_length=200)
last_name = forms.CharField(max_length=200)
company_name = forms.CharField(max_length=200)
password1 = forms.CharField(max_length=200)
password2 = forms.CharField(max_length=200)
def clean(self):
cleaned_data = self.cleaned_data
cleaned_username_obtained = cleaned_data.get('username')
if(cleaned_username_obtained != None):
username_obtained = cleaned_username_obtained.lower()
else:
raise ValidationError('Username is empty!')
cleaned_email_address = cleaned_data.get('email')
cleaned_password1 = cleaned_data.get('password1')
cleaned_password2 = cleaned_data.get('password2')
cleaned_first_name = cleaned_data.get('first_name')
cleaned_last_name = cleaned_data.get('last_name')
cleaned_company_name = cleaned_data.get('company_name')
if username_obtained != None:
if((User.objects.filter(username=username_obtained)).count() > 0):
self._errors['username'] = [u'Username is already in use']
elif((ModelOfAdvertisementClient.objects.filter(email_address=cleaned_email_address)).count() > 0):
self._errors['email'] = [u'Email is already in use']
elif(len(cleaned_password1) == 0):
self._errors['password1'] = [u'Password input box is empty']
elif(cleaned_password2 != cleaned_password1):
self._errors['password2'] = [u'Inserted passwords are different!']
return cleaned_data
test_forms.py:
class SignupFormTest(TestCase):
#classmethod
def test_correct_values(self):
form = SignupForm({
'username': 'testUsername',
'email': 'mark12#gmail.com',
'first_name': 'Karl',
'last_name': 'Smith',
'company_name': 'HiTech Inc.',
'password1': 'test123',
'password2': 'test123'
}
)
print(str(form.is_valid()))
self.assertFalse(form.is_valid())
Unfortunately, each time I am launching written above code, it ends up with following error message:
self.assertFalse(form.is_valid())
TypeError: assertFalse() missing 1 required positional argument: 'expr'
and due to the fact, that
form.is_valid()
returns "False", I literally have no clue of what is wrong with this assert
Testing methods should not have #classmethod decorators. Those are meant for class-wide setup/teardown code. In your case because of #classmethod decorator you are silently calling an unbound assertFalse() method that expects 2 arguments: a TestCase instance an a boolean expression.
Related
I would like to make two fields (repertoire and performer) required only if the user selects and submits a type="performance". This is my model:
class Event(models.Model):
EV_TYPE = (
('performance', 'performance'),
('other', 'other'),
)
title = models.CharField(max_length=200)
type = models.CharField(max_length=15, choices=EV_TYPE, default="performance")
repertoire = models.ManyToManyField(Work, blank=True)
performer = models.ManyToManyField(Profile, limit_choices_to={'role__type__contains': 'Performer'})
My form:
class EventForm(forms.ModelForm):
class Meta:
model = Event
fields = [
'type',
'submitted_by',
'title',
'repertoire',
]
My view:
def event_create_view(request):
if request.method == 'POST':
form_event = EventForm(
request.POST,
repertoire_required=(not (active_user.id == 17)), # if the user is NMS then the repertoire field is not compulsory
initial={'submitted_by' : active_user.profile.id}
)
form_venue = AddVenueForm()
form_profile = ProfileForm()
form_work = WorkFromEventForm()
if form_event.is_valid():
this_event = form_event.save()
return redirect('event-edit', id=this_event.id)
This is what I tried. In the above 'form_event.is_valid', I did:
if form_event.is_valid():
this_event = form_event.save(commit=False)
if this_event['type'] == 'performance' and this_event['performer'] and this_event['repertoire']:
this_event.save()
return redirect('event-edit', id=this_event.id)
This doesn't work though (I still get a 'this field is required' when I submit). How can I achieve what I want to do?
Addendum
This is also not working:
if
this_event['type'] == 'performance' and not this_event['performer'] or not this_event['repertoire']:
messages.error(request, form_event.errors)
else:
this_event.save()
You do that with a clean method on the form.
For example you might do;
def clean(self):
"""
Validate the form input
"""
cleaned_data = super().clean()
type = cleaned_data.get('type')
if type == 'performance':
repertoire = cleaned_data.get('repertoire')
performer = cleaned_data.get('performer')
if not repertoire:
self.add_error('repertoire', _("This field is required"))
if not performer:
self.add_error('performer', _("This field is required"))
return cleaned_data
The docs for form validation are here; https://docs.djangoproject.com/en/3.1/ref/forms/validation/
So I have this custom register API which registers a user, but when user successfully register, I want it to have this message "You have successfully register an account!" But I tried a different method but get an error instead.
serializer.py
class UserCreate2Serializer(ModelSerializer):
email = EmailField(label='Email Address')
valid_time_formats = ['%H:%M', '%I:%M%p', '%I:%M %p']
birthTime = serializers.TimeField(format='%I:%M %p', input_formats=valid_time_formats, allow_null=True, required=False)
class Meta:
model = MyUser
fields = ['username', 'password', 'email', 'first_name', 'last_name', 'gender', 'nric', 'birthday', 'birthTime']
extra_kwargs = {"password": {"write_only": True}}
def validate(self, data): # to validate if the user have been used
email = data['email']
user_queryset = MyUser.objects.filter(email=email)
if user_queryset.exists():
raise ValidationError("This user has already registered.")
return data
def create(self, validated_data):
username = validated_data['username']
password = validated_data['password']
email = validated_data['email']
first_name = validated_data['first_name']
last_name = validated_data['last_name']
gender = validated_data['gender']
nric = validated_data['nric']
birthday = validated_data['birthday']
birthTime = validated_data['birthTime']
user_obj = MyUser(
username = username,
email = email,
first_name = first_name,
last_name = last_name,
gender = gender,
nric = nric,
birthday = birthday,
birthTime = birthTime,
)
user_obj.set_password(password)
user_obj.save()
return validated
views.py
class CreateUser2View(CreateAPIView):
permission_classes = [AllowAny]
serializer_class = UserCreate2Serializer
queryset = MyUser.objects.all()
I tried changing this into the serializer
user_obj.set_password(password)
user_obj.save()
content = {'Message': 'You have successfully register an account'}
return content
But got an error instead. I'm unsure how to do the custom response as I only know it is to be done on views.py.
But if I do this on view:
class CreateUser2View(CreateAPIView):
permission_classes = [AllowAny]
serializer_class = UserCreate2Serializer
queryset = MyUser.objects.all()
def post(self, request):
content = {'Message': 'You have successfully register'}
return Response(content, status=status.HTTP_200_OK)
It will show this even if the validation is incorrect. Please help me as I'm still inexperienced in DRF.
class CreateUser2View(CreateAPIView):
permission_classes = [AllowAny]
serializer_class = UserCreate2Serializer
queryset = MyUser.objects.all()
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response({'Message': 'You have successfully register'}, status=status.HTTP_201_CREATED, headers=headers)
from rest_framework import status
from rest_framework.views import exception_handler as base_handler
def exception_handler(exception, context):
"""
Django rest framework for custom exception handler
#exception : Exception
#context : Context
"""
response = base_handler(exception, context)
if response is not None:
response = custom_response(response)
return response
def serializer_errors(data):
"""
Django rest framework serializing the errors
#data : data is python error dictionary
"""
errors = {}
got_msg = False
message = "Bad request."
if isinstance(data, dict):
for key, value in data.items():
try:
if isinstance(value, list):
value = ", ".join(value)
except Exception:
pass
if not got_msg:
if value:
message = value
got_msg = True
errors[key] = value
if not isinstance(message, str):
message = "Bad request"
return errors, message
def error(source, detail, code):
"""
Create python dictionary of error
#source : Where coming the error
#detail : Error detail information
"""
error = {}
error["source"] = source
error["detail"] = detail
if code:
error["code"] = code
return error
def custom_response(response):
"""
Modification the response of django rest framework
#response : Return response
"""
modified_data = {}
modified_data["code"] = response.status_code
modified_data["status"] = get_status(response.status_code)
data, message = serializer_errors(response.data)
modified_data["message"] = message
modified_data["errors"] = data
response.data = modified_data
return response
def get_status(status_code):
"""
Return result base on return http status
#status_code : HTTP status code
"""
result = ""
if status_code == status.HTTP_200_OK:
result = "Success"
elif status_code == status.HTTP_201_CREATED:
result = "Instance create"
elif status_code == status.HTTP_204_NO_CONTENT:
result = "Instance deleted"
elif status_code == status.HTTP_403_FORBIDDEN:
result = "Forbidden error"
elif status_code == status.HTTP_404_NOT_FOUND:
result = "Instance not found"
elif status_code == status.HTTP_400_BAD_REQUEST:
result = "Bad request"
elif status_code == status.HTTP_401_UNAUTHORIZED:
result = "Unauthorized request"
elif status_code == status.HTTP_500_INTERNAL_SERVER_ERROR:
result = "Internal server error"
else:
result = "Unknown error"
return result
The code works well but when trying to add a new user to the site it logs in as the superuser only.
Model
class RegistrationForm(forms.Form):
username = forms.CharField(label = 'Username', max_length = 30)
first_name = forms.CharField(label = 'First name', max_length = 30,widget=forms.TextInput(attrs={'class' : 'Name'}))
last_name = forms.CharField(label = 'Last name', max_length = 30,widget=forms.TextInput(attrs={'class' : 'Name'}))
email1 = forms.EmailField(label = 'email', required = True,widget=forms.TextInput(attrs={'class' : 'email'}))
email2 = forms.EmailField(label = 'Re-enter email',widget=forms.TextInput(attrs={'class' : 'email'}))
password1 = forms.CharField(label= "Password", widget = forms.PasswordInput(attrs={'class' : 'password1'}))
birthday = forms.DateField(label = 'Birthday',widget=forms.TextInput(attrs={'class' : 'birthday'}))
class Meta:
model = User
fields = ('username', 'email', 'password1','password2')
def save(self, commit = True):
user = super(RegistrationForm, self).save(commit = False)
user.email1 = self.cleaned_data['email1']
user.email2 = self.cleaned_data['email2']
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.birthday = self.cleaned_data['birthday']
if commit:
user.save()
return user
def clean_username(self):
email1 = self.cleaned_data['email1']
if not re.search(r"(^[a-zA-Z0-9_.+-]+#[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)", email1):
raise forms.ValidationError('Use a real email address eg. something#example.com.')
try:
user.objects.get(email1 = email1)
except ObjectDoesNotExist:
return username
raise forms.ValidationError('email is already in use.')
def clean_username(self):
username = self.clean_data['username']
if not re.search(r'^\w+$', username):
raise forms.ValidationError('Username can only contain alphanumeric characters and the underscore.')
try:
user.objects.get(username = username)
except ObjectDoesNotExist:
return username
raise forms.ValidationError('Username is already taken.')
Views
def register_page(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
form.save()
user = User.objects.create_user(
username = form.clean_data['username'],
password = form.clean_data['password1'],
email = form.clean_data['email']
)
return HttpResponseRedirect('/register/success/')
else:
args = {}
args.update(csrf(request))
args['form'] = RegistrationForm()
print(args)
return render(request, 'registration/register.html', args)
This is my form:
class EventForm(forms.Form):
start_date = forms.CharField(required=True, widget=forms.TextInput(attrs={'class': 'span6 as-date'}))
end_date = forms.CharField(required=True, widget=forms.TimeInput(attrs={'class': 'span6 as-date'}))
title = forms.CharField(required=True, widget=forms.TextInput(attrs={'class': 'span8'}))
description = forms.CharField(required=True, widget=forms.Textarea(attrs={'rows': 4, 'class': 'span8'}))
total_seats = forms.IntegerField(required=True, widget=forms.TextInput(attrs={'class': 'span6'}))
buffer_seats = forms.IntegerField(required=True, widget=forms.TextInput(attrs={'class': 'span6'}))
seat_cost = forms.IntegerField(required=True, widget=forms.TextInput(attrs={'class': 'span8'}))
published = forms.BooleanField(required=False, widget=forms.CheckboxInput(attrs={'class': 'span6'}))
verified_booking = forms.BooleanField(required=False, widget=forms.CheckboxInput(attrs={'class': 'span6'}))
def clean(self):
try:
self.cleaned_data['start_date'] = datetime.strptime(self.cleaned_data['start_date'], '%Y/%m/%d %H:%M')
except ValueError:
raise forms.ValidationError('Improper start date')
try:
self.cleaned_data['end_date'] = datetime.strptime(self.cleaned_data['end_date'], '%Y/%m/%d %H:%M')
except ValueError:
raise forms.ValidationError('Improper end date')
if self.cleaned_data['buffer_seats'] >= self.cleaned_data['total_seats']:
raise forms.ValidationError('Seats buffered can not be same or greater than total seats')
if self.cleaned_data['start_date'] >= self.cleaned_data['end_date']:
raise forms.ValidationError('Event start date can not be greater than end date')
if self.cleaned_data['published'] == 'on':
self.cleaned_data['published'] = True
else:
self.cleaned_data['published'] = False
if self.cleaned_data['verified_booking'] == 'on':
self.cleaned_data['verified_booking'] = True
else:
self.cleaned_data['verified_booking'] = False
Here's the view:
event_form = EventForm(request.POST)
if event_form.is_valid():
event = events_api.save_event_from_form(request.user, address, event_form.cleaned_data)
But I'm getting event_form.cleaned_data as None. What am I missing?
form.clean() must return cleaned_data. The canonical implementation is:
class MyForm(forms.Form):
# ...
def clean(self):
cleaned_data = super(MyForm, self).clean()
# do your custom validations / transformations here
# and some more
return cleaned_data
forms.py
class UserProfileForm(forms.ModelForm):
phone = forms.CharField(max_length = 15,widget = forms.TextInput(attrs = {'placeholder':'Enter mobile no. ','class':''}))
profession = forms.CharField(max_length= 50,widget = forms.Select(choices = PROFESSION_CHOICES,attrs = {'class':''}))
#email = forms.EmailField(label='Email address',max_length = 75,widget = forms.TextInput(attrs={'placeholder':'Email address.','class':''}))
sex = forms.CharField(max_length = 20,label="I am :",widget=forms.Select(choices=SEX_CHOICES,attrs = {'class':''}))
first_name = forms.CharField(max_length = 50,widget = forms.TextInput(attrs={'placeholder':'Please enter your real name.','class':''}))
last_name = forms.CharField(max_length = 50,widget = forms.TextInput(attrs={'placeholder':'Enter last name.','class':''}))
location = forms.CharField(max_length = 50,widget = forms.TextInput(attrs={'placeholder':'Enter your current location','class':''}))
def clean_first_name(self):
first_name = self.cleaned_data['first_name']
if first_name == '':
raise forms.ValidationError("This field is required.")
return first_name
def save(self,*args,**kw):
self.instance.first_name = self.cleaned_data.get("first_name")
self.instance.last_name = self.cleaned_data.get("last_name")
self.instance.sex = self.cleaned_data.get("sex")
self.instance.location = self.cleaned_data.get("location")
self.instance.profession = self.cleaned_data.get("profession")
self.instance.phone = self.cleaned_data.get("phone")
self.instance.save()
return self.instance
class Meta:
model = User
fields = ('username','first_name','last_name','phone','sex','profession','location')
views.py
def profile(request,nav="profile",template="profile.html",context = {},extra_context = None):
if request.POST:
if 'profileFormSubmit' in request.POST:
pform = UserProfileForm(request.POST,instance = request.user)
if pform.is_valid():
try:
user = pform.save()
return redirect(profile,nav="profile")
except RuntimeError as e:
return HttpResponse(e)
error
The User could not be changed because the data didn't validate.
line
user = super(UserProfileForm,self).save(*args,**kw)
doubt
what changes am i supposed to make to get rid of this error
how am i supposed to change the , i have tried removing all the clean_field form methods , but still getting the same error , please help , thanks in advance.
You are calling save on your form before you clean. And you are calling save twice. Once at the start of the form save. And once at the end.
pform.is_valid() returns a boolean that you never check.
docs on modelforms
The form wasn't validating because I was using 'username' in my meta class of the UserProfileForm, which wasn't supposed to be there.