Anyone who can explain me, why my ValidationError in my form doesn't work? I can see "TEXT" in my terminal, but the ValidationError doesn't show.
Forms.py
def clean(self):
cleaned_data = super(CheckInForm, self).clean()
new_room = cleaned_data.get('room')
new_name = cleaned_data.get('name')
if Student.objects.filter(room=new_room).count() > 3:
if not Student.objects.filter(room=new_room, name__icontains=new_name):
print('TEXT')
raise ValidationError('The room is full')
It’s also worth noting that a similar def clean_room(self): function works fine in my code.
In this function, raise ValidationError works correctly.
def clean_room(self):
new_room = self.cleaned_data['room']
if new_room == '':
raise ValidationError('This field cannot be empty')
return new_room
Full length code:
class CheckInForm(forms.ModelForm):
class Meta:
model = Student
fields = ['room', 'name', 'faculty', 'place_status', 'form_studies',
'group', 'sex', 'mobile_number', 'fluorography', 'pediculosis',
'contract_number', 'agreement_date', 'registration', 'citizenship',
'date_of_birthday', 'place_of_birthday', 'document_number', 'authority',
'date_of_issue', 'notation'
]
widgets = {'room': forms.TextInput(attrs={'class': 'form-control'}),
'name': forms.TextInput(attrs={'class': 'form-control'}),
'faculty': forms.TextInput(attrs={'class': 'form-control'}),
}
def clean(self):
cleaned_data = super(CheckInForm, self).clean()
new_room = cleaned_data.get('room')
new_name = cleaned_data.get('name')
if Student.objects.filter(room=new_room).count() > 3:
if not Student.objects.filter(room=new_room, name__icontains=new_name):
print('TEXT')
raise ValidationError('The room is full')
def clean_room(self):
new_room = self.cleaned_data['room']
if new_room == '':
raise ValidationError('This field cannot be empty!')
return new_room
{% extends 'hostel/base_home.html' %}
{% block check_in %}
<form action="{% url 'check_in_update_url' id=student.id %}" method="post">
{% csrf_token %}
{% for field in bound_form %}
<div class="from-group">
{{ field.label }}
{{ field }}
{% for error in form.non_field_errors %}
{{error}}
{% endfor %}
{% if field.errors %}
<div class="alert alert-danger">
{{ field.errors }}
</div>
{% endif %}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">Update</button>
</form>
{% endblock %}
Related
I've got some forms I'm trying to customize.
I render the fields manually - and it all works fine until get to a particular field (which is an InlineFormset itself). I'm trying to customize those options but can't seem to figure out how to do so.
my forms.py looks like this:
class SummativeScoreForm(forms.ModelForm):
subdomain_proficiency_level = forms.ModelChoiceField(
empty_label="Undecided",
queryset=SubdomainProficiencyLevel.objects.none(),
widget=forms.RadioSelect,
required=False,
)
def __init__(self, request, *args, **kwargs):
super(SummativeScoreForm, self).__init__(*args, **kwargs)
if self.instance:
if request.user == self.instance.summative.employee:
self.fields["subdomain_proficiency_level"].disabled = True
self.fields[
"subdomain_proficiency_level"
].queryset = SubdomainProficiencyLevel.objects.filter(
subdomain=self.instance.subdomain
)
self.fields[
"subdomain_proficiency_level"
].label = f"""
{self.instance.subdomain.character_code}:
{self.instance.subdomain.short_description}
"""
class Meta:
model = SummativeScore
fields = "__all__"
SummativeScoreInlineFormset = inlineformset_factory(
Summative,
SummativeScore,
fields=("subdomain_proficiency_level",),
can_delete=False,
extra=0,
form=SummativeScoreForm,
)
My template for summative_score_form looks like this:
<form method="post" novalidate>
{% csrf_token %}
{% include "myapp/includes/summative_score_response_formset_snippet.html" with formset=form %}
<button type="submit" class="btn btn-primary"><i class="fal fa-clipboard-check"></i> Submit Updated Scores</button>
</form>
The summative_score_response_formset_snippet looks like this:
{{ formset.management_form }}
{% for formset_form in formset.forms %}
{% if formset_form.non_field_errors %}
<ul>
{% for error in formset_form.non_field_errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
{% for hidden_field in formset_form.hidden_fields %}
{% if hidden_field.errors %}
<ul>
{% for error in hidden_field.errors %}
<li>
(Hidden field {{ hidden_field.name }}) {{ error }}
</li>
{% endfor %}
</ul>
{% endif %}
{{ hidden_field }}
{% endfor %}
{% for field in formset_form.visible_fields %}
{% if field.name == 'subdomain_proficiency_level' %}
<label class="form-check-label" for="{{ field.id_for_label }}">
{{ field.label }}
</label>
<ul id="{{ field.auto_id }}" class="form-check mt-2">
{% for choice in formset_form.subdomain_proficiency_level %}
<div class="form-check">
<!--
THIS IS THE PART I WOULD LIKE TO CUSTOMIZE:
Unsatisfactory (name) Lorum Ipsum (description)
Satisfactory (name) Lorum Ipsum (description)
Excellent (name) Lorum Ipsum (description)
CURRENTLY IT ONLY SHOWS THE NAME
-->
{{ choice }}
</div>
{% endfor %}
</ul>
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
{% else %}
{{ field }}
{% endif %}
{% endfor %}
{% endfor %}
My models look like this:
class SubdomainProficiencyLevel(CreateUpdateMixin):
"THIS IS THE 'UNSATISFACTORY' (name) 'LORUM IPSUM' (description)"
name = models.CharField(max_length=75)
description = models.TextField()
sequence = models.IntegerField()
class Meta:
ordering = ["sequence"]
verbose_name = "Subdomain Rank"
verbose_name_plural = "Subdomain Ranks"
def __str__(self):
"""
THIS IS WHAT IS 'CHOICE' IN THE FORM
I'm trying to edit this to add styles to the self.description on the form
"""
return f"{self.name}"
class SummativeScore(CreateUpdateMixin, CreateUpdateUserMixin):
summative = models.ForeignKey(Summative, on_delete=models.PROTECT)
subdomain = models.ForeignKey(Subdomain, on_delete=models.PROTECT)
subdomain_proficiency_level = models.ForeignKey(
SubdomainProficiencyLevel,
on_delete=models.PROTECT,
null=True,
blank=True,
)
class Meta:
ordering = ["subdomain__character_code"]
verbose_name = "SummativeScore"
verbose_name_plural = "SummativeScores"
def __str__(self):
"""Unicode representation of SummativeScore."""
return f"{self.subdomain_proficiency_level}"
The view is a Class Based FormView
class SummativeScoreFormView(
LoginRequiredMixin,
UserIsObserverOrObserveeMixin,
SingleObjectMixin,
FormView,
):
model = Summative
template_name = "myapp/summative_score_form.html"
pk_url_kwarg = "summative_id"
def get(self, request, *args, **kwargs):
summative_id = kwargs.pop("summative_id")
self.object = self.get_object(
queryset=Summative.objects.filter(id=summative_id)
)
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
summative_id = kwargs.pop("summative_id")
self.object = self.get_object(
queryset=Summative.objects.filter(id=summative_id)
)
return super().post(request, *args, **kwargs)
def get_form(self, form_class=None):
formset = SummativeScoreInlineFormset(
**self.get_form_kwargs(), instance=self.object
)
return formset
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["form_kwargs"] = {"request": self.request}
return kwargs
def form_valid(self, form):
form.save()
messages.success(self.request, "Changes were saved!")
return super().form_valid(form)
def form_invalid(self, form):
return super().form_invalid(form)
def get_success_url(self):
user_id = self.kwargs["user_id"]
summative_id = self.kwargs["summative_id"]
return reverse(
"myapp:summative_detail",
kwargs={
"user_id": user_id,
"summative_id": summative_id,
},
)
As you can see in the template - I render the SubdomainProficiencyLevel objects with the template variable {{ choice }}
I have tried doing {{ choice.description }} or {{ choice.name }} <span class="bold">{{ choice.description }}</span> but then nothing displays.
I have also tried adjusting the __str__ method on the model - which changes there work, but do not render as HTML (just as a string as expected).
What is the best way to customize that in the HTML?
I ended up creating a custom radio button class (similar to the documentation)
class CustomRadioSelect(forms.RadioSelect):
def create_option(
self, name, value, label, selected, index, subindex=None, attrs=None
):
option = super().create_option(
name, value, label, selected, index, subindex, attrs
)
if value:
option["attrs"]["description"] = value.instance.description
return option
Using that in the form:
subdomain_proficiency_level = forms.ModelChoiceField(
empty_label="Undecided",
queryset=SubdomainProficiencyLevel.objects.none(),
widget=CustomRadioSelect(),
required=False,
)
Then I could access it like this in the template:
{{ choice.data.attrs.description }}
I am not that experienced writing Python/Back-end, but trying to improve. In development/localserver I am trying to create a password reset form... but I got the following error when accessing the link from the forgot password email - and before that the password was not saving:
get_context_data() missing 1 required positional argument: 'user'
forms.py (almost copy/paste from Django's form; minor changes)
class ResetPasswordForm(SetPasswordForm):
error_messages = {
'password_mismatch': static_textLanguage['page_user_passwordReset_alert_passwordNotMatch'],
'password_empty': static_textLanguage['global_alert_mandatoryField'],
'minimum_length': static_textLanguage['global_alert_minCharacters_password'],
}
new_password1 = forms.CharField(
required=False,
widget=forms.PasswordInput(attrs={
'id': 'page_userPasswordReset_content_form_input_passwordA',
'maxlength': '25',
'class': 'global_component_input_box'
}),
)
new_password2 = forms.CharField(
required = False,
widget=forms.PasswordInput(attrs={
'id': 'page_userPasswordReset_content_form_input_passwordB',
'maxlength': '25',
'class': 'global_component_input_box'
}),
)
def __init__(self, user, *args, **kwargs):
self.user = user
super(ResetPasswordForm, self).__init__(user, *args, **kwargs)
def clean_new_password1(self):
password1 = self.cleaned_data.get('new_password1')
if password1 == '' or password1 is None:
raise forms.ValidationError(self.error_messages['password_empty'], code='password_field_empty')
elif len(password1) < 8:
raise forms.ValidationError(self.error_messages['minimum_length'], code='password_too_short')
return password1
def clean_new_password2(self):
password1 = self.cleaned_data.get('new_password1')
password2 = self.cleaned_data.get('new_password2')
if password2 == '' or password2 is None:
raise forms.ValidationError(self.error_messages['password_empty'], code='password_field_empty')
if password1 and password2:
if password1 != password2:
raise ValidationError(self.error_messages['password_mismatch'], code='password_mismatch')
password_validation.validate_password(password2, self.user)
return password2
def save(self, commit=True):
password = self.cleaned_data["new_password1"]
self.user.set_password(password)
if commit:
self.user.save()
return self.user
views.py
class UserPasswordResetView(auth_views.PasswordResetConfirmView):
template_name = '../frontend/templates/frontend/templates.user/template.page_passwordReset.html'
form_class = ResetPasswordForm
post_reset_login = True
success_url = reverse_lazy('page_userLoginPrivate')
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.user
return kwargs
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.validlink:
context.update({
'validlink': True,
'form': self.form_class(),
'static_json_text': static_textLanguage,
'static_json_textGlobal': static_textGlobal
})
else:
context.update({
'validlink': False,
'form': None,
'title': _('Password reset unsuccessful'),
'static_json_text': static_textLanguage,
'static_json_textGlobal': static_textGlobal
})
return context
template.html (for purpose of this exercise .html was simplified)
<div class="wrapper_page_userPasswordReset">
{% if validlink %}
<form id="page_userPasswordReset_content_form" class="page_userPasswordReset_content_form" method="post" novalidate>
{% csrf_token %}
<div class="global_component_input_box_container">
{{ form.new_password1}}
</div>
<div id="page_userPasswordReset_input_passwordA_alert_errors" class="alert_message_input_danger_format">
{% if form.errors %}
{% for key, value in form.errors.items %}
{% if key == 'new_password1' %}
{{ value }}
{% endif %}
{% endfor %}
{% endif %}
</div>
<div class="global_component_input_box_container">
{{ form.new_password2}}
</div>
<div id="page_userPasswordReset_input_passwordB_alert_errors" class="alert_message_input_danger_format">
{% if form.errors %}
{% for key, value in form.errors.items %}
{% if key == 'new_password1' %}
{{ value }}
{% endif %}
{% endfor %}
{% endif %}
</div>
<button type="submit" id="page_userPasswordReset_content_form_button_submit" class="global_component_button button_background_green">{{ static_json_text.global_button_submit }}</button>
</form>
{% else %}
<button id="page_userPasswordReset_button_forgotPassword" class="global_component_button button_background_green">{{ static_json_text.page_user_passwordReset_notValidLink_button }}</button>
{% endif %}
</div>
Update:
The issue seemed to be in the coding for Django-formset. I was processing it as an inline formset and not a model formset. The answer below was also correct. Thanks!
I am working with a model formset for an intermediate model. I am using django-formset js to add additional formset fields on the template. Most everything works OK except that when I go to save the formset only the first entry is being saved to the DB. The first entry is saved and assigned correctly but any after than just disappear. It is not throwing any errors so I am not sure what is going wrong. Thanks!
The Model
class StaffAssignment(models.Model):
study = models.ForeignKey(Study, related_name='study_set', null=True, on_delete=models.CASCADE)
staff = models.ForeignKey('account.UserProfile', related_name='assigned_to_set', null=True, on_delete=models.CASCADE)
role = models.CharField(max_length=100, null=True)
assigned_on = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('-role',)
def __str__(self):
return '{} is assigned to {}'.format(self.staff, self.study)
The Form:
class AddStaff(forms.ModelForm):
model = StaffAssignment
fields = ('staff',)
def __init__(self, *args, **kwargs):
super(AddStaff, self).__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].widget.attrs.update({'class': 'form-control'})
The View:
def add_staff(request, study_slug):
study = get_object_or_404(Study, slug=study_slug)
staff_formset = modelformset_factory(StaffAssignment, form=AddStaff, fields=('staff',), can_delete=True)
if request.method == 'POST':
staffList = staff_formset(request.POST, request.FILES)
if staffList.is_valid():
for assignment in staffList:
assigned = assignment.save(commit=False)
assigned.study = study
assigned.role = assigned.staff.job_title
assigned.save()
return HttpResponseRedirect(reverse('studies:studydashboard'))
else:
HttpResponse('Something is messed up')
else:
staffList = staff_formset(queryset=StaffAssignment.objects.none())
return render(request, 'studies/addstaff.html', {'staffList': staffList, 'study': study})
The Template:
<form action="{% url 'studies:addstaff' study.slug %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="box-body">
{% for list in staffList %}
<div class="form-group" id="formset">
{% if list.instance.pk %}{{ list.DELETE }}{% endif %}
{{ list.staff }}
{% if list.staff.errors %}
{% for error in list.staff.errors %}
{{ error|escape }}
{% endfor %}
{% endif %}
</div>
{% endfor %}
{{ staffList.management_form }}
</div>
<div class="box-footer">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</form>
You are not including the primary key field in the template, as required by the docs. Add
{% for list in staffList %}
{{ list.pk }}
...
{% endfor %}
I am trying to use FormWizard to make a registration for new users. Because in some steps I would need to use js,formsets, and photos, I decided to make a different template for each step. But then I got an error :
ValidationError at /registration_steps
[u'\u0414\u0430\u043d\u043d\u044b\u0435 ManagementForm \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0442 \u0438\u043b\u0438 \u0431\u044b\u043b\u0438 \u0438\u0441\u043a\u0430\u0436\u0435\u043d\u044b.']
forms.py
class ApplicantForm1(forms.ModelForm):
password1 = forms.CharField(
label='Password',
widget=forms.PasswordInput
)
password2 = forms.CharField(
label='confirm',
widget=forms.PasswordInput
)
travel_passport = forms.ChoiceField(
choices=((False, 'Нет'), (True, 'Да')),
widget=forms.Select
)
def clean_password2(self):
password1 = self.cleaned_data.get('password1')
password2 = self.cleaned_data.get('password2')
if password1 and password2 and password1 != password2:
raise forms.ValidationError('Passwords dont match')
return password2
def save(self, commit=True):
user = super(ApplCreaForm, self).save(commit=False)
user.set_password(self.cleaned_data['password1'])
user.isempl=0
if commit:
user.save()
return user
class Meta:
model = ExtUser
fields = ['email','firstname', 'lastname', 'middlename', 'date_of_birth', 'gender', 'family_position',
'driver_license', 'driver_license_category',]
class ApplicantForm2(forms.ModelForm):
class Meta:
model = ExtUser
fields = ['country','region','city', 'nearcity', 'travel_passport', 'travel_passport_end_date','metro', 'mobile_sms', 'mobile_double', 'email_double',
'web', 'vkontakte', 'facebook','odnoklasniki','skype','whatsapp','viber','ready_to_move','ready_to_move_where',]
views.py
FORMS_REG = [("reg_step1", ApplicantForm1),
("reg_step2", ApplicantForm2),
# ("ApplicantForm3", ApplicantForm3),
# ("ApplicantForm4", ApplicantForm4),
# ("ApplicantForm5", ApplicantForm5),
# ("ApplicantForm6", ApplicantForm6),
# ("ApplicantForm7", ApplicantForm7),
]
TEMPLATES_REG = {"reg_step1": "registration_steps/reg_step1.html",
"reg_step2": "registration_steps/reg_step2.html",
#"reg_step3": "registration_steps/reg_step3.html",
#"reg_step4": "registration_steps/reg_step4.html",
#"reg_step5": "registration_steps/reg_step5.html",
#"reg_step6": "registration_steps/reg_step6.html",
#"reg_step7": "registration_steps/reg_step7.html",
}
class ApplicantWizard(SessionWizardView):
instance = None
def get_template_names(self):
return [TEMPLATES_REG[self.steps.current]]
def get_form_instance( self, step ):
if self.instance is None:
self.instance = ExtUser()
return self.instance
def done(self, form_list, **kwargs):
self.instance.save()
return render_to_response('app/successpage.html', {
'title':"Registration complited" ,
})
urls.py
url(r'^registration_steps$', ApplicantWizard.as_view(FORMS_REG)),
templates reg_step1.html and reg_step2.html (for now they are same)
{% extends "layout/layout_main.html" %}
{% block content %}
<p> Step {{ wizard.steps.step1 }} of {{ wizard.steps.count }}</p>
{% for field in form %}
{{field.error}}
{% endfor %}
<form action="" method="post">{% csrf_token %}
<table>
{{wizard.managment_form}}
{{ wizard.form }}
</table>
{% if wizard.steps.prev %}
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.first }}">"first step"</button>
<button name="wizard_goto_step" type="submit" value="{{ wizard.steps.prev }}">"prev step"</button>
{% endif %}
<input type="submit" value="Submit" />
</form>
{% endblock %}
As form wizard complains about the missing management form, the problem seems to be related to nested formsets. Make your relevant template section look like:
<table>
{{ wizard.management_form }}
{% if wizard.form.forms %}
{{ wizard.form.management_form }}
{% for form in wizard.form.forms %}
{{ form }}
{% endfor %}
{% else %}
{{ wizard.form }}
{% endif %}
</table>
I recently added crispy_forms to my django project and it caused me to get the 'unicode' object has no attribute 'field' error. Can't figure out why.
models.py
class Trip(models.Model):
location_name = models.CharField(max_length=60)
trip_date = models.DateField()
trip_rating = models.IntegerField(validators=[MinValueValidator(1),MaxValueValidator(5)])
fishing_vehicle = models.ForeignKey(FishingVehicle)
water_body = models.ForeignKey(WaterBody)
user = models.ForeignKey(User)
def __unicode__(self):
return self.location_name
views.py
#login_required
def logtrip(request):
if request.method == 'POST':
form = forms.TripForm(request.POST)
if form.is_valid():
trip = form.save(commit=False)
trip.user = request.user
trip.save()
return redirect('home')
else:
print form.errors
else:
form = forms.TripForm()
return render_to_response('logtrip.html', {'form': form}, context_instance=RequestContext(request))
forms.py
class TripForm(ModelForm):
CHOICES = (('1', 'None',), ('2', 'Below Average Amount',), ('3', 'Average Amount',), ('4', 'Above Average Amount',), ('5', 'A Lot/Limited Out',))
trip_rating = forms.ChoiceField(widget=forms.RadioSelect, choices=CHOICES, label='Fish Caught')
class Meta:
model = Trip
exclude = ['user']
widgets = {'trip_date': forms.DateInput(attrs={'class':'datepicker'})}
logtrip.html
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<!-- Example row of columns -->
<div class="row">
<div class="col-md-4">
<form action="/logtrip/" method="post">
{% csrf_token %}
{{ form.as_p|crispy }}
<input type="submit" value="Submit" />
</form>
</div>
</div>
</div>
{% endblock %}
Everything works correctly when I remove the |crispy filter.
For anyone else getting this error, my problem was solved by changing {{ form.as_p|crispy }} to {{ form|crispy }}.