I have a forms.py as following. I am using htmx for validation. And I would like to get whole form for use form.is_valid() function. But in my case I am getting no valid form.
Is there any way to get all form with HTMX?
forms.py
class UniversityForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.form_id = 'university-form'
self.helper.attrs = {
'hx-post': reverse_lazy('index'),
'hx-target': '#university-form',
'hx-swap': 'outerHTML'
}
self.helper.add_input(Submit('submit', 'Submit'))
subject = forms.ChoiceField(
choices=User.Subjects.choices,
widget=forms.Select(attrs={
'hx-get': reverse_lazy('check-subject'),
'hx-target': '#div_id_subject',
'hx-trigger': 'change'
})
)
date_of_birth = forms.DateField(widget=forms.DateInput(attrs={'type': 'date', 'max': datetime.now().date()}))
class Meta:
model = User
fields = ('username', 'password', 'date_of_birth', 'subject')
widgets = {
'password': forms.PasswordInput(),
'username': forms.TextInput(attrs={
'hx-get': reverse_lazy('check-username'),
'hx-target': '#div_id_username',
'hx-trigger': 'keyup[target.value.length > 3]'
})
}
def clean_username(self):
username = self.cleaned_data['username']
if len(username) <= 3:
raise forms.ValidationError("Username is too short")
return username
def clean_subject(self):
subject = self.cleaned_data['subject']
if User.objects.filter(subject=subject).count() > 3:
raise forms.ValidationError("There are no spaces on this course")
return subject
def save(self, commit=True):
""" Hash user's password on save """
user = super().save(commit=False)
user.set_password(self.cleaned_data['password'])
if commit:
user.save()
return user
views.py
def check_username(request):
form = UniversityForm(request.GET)
if form.is_valid():
print('Valid')
else:
print('No Valid')
print(form.errors)
print(form['date_of_birth'])
context = {
'field': as_crispy_field(form['username']),
'valid': not form['username'].errors
}
return render(request, 'partials/field.html', context)
Error: Even all fields are filled; my output of >print('No Valid') >print(form.errors) >print(form['date_of_birth']) are as following...
No Valid
<ul ><li>password<ul ><li>This field is required.</li></ul></li><li>date_of_birth<ul ><li>This field is required.</li></ul></li><li>subject<ul ><li>This field is required.</li></ul></li></ul>
<input type="date" name="date_of_birth" max="2022-03-15" required id="id_date_of_birth">
CodePudding user response:
The username checking HTMX GET request is activated only on the username
field. By default HTMX will not include any other parent element in the request, this is the reason of the missing fields error message.
To include other elements in the request, you can use the hx-include
attribute, where you list the additional fields' CSS query selector:
class Meta:
model = User
fields = ('username', 'password', 'date_of_birth', 'subject')
widgets = {
'password': forms.PasswordInput(),
'username': forms.TextInput(attrs={
'hx-get': reverse_lazy('check-username'),
'hx-target': '#div_id_username',
'hx-trigger': 'keyup[target.value.length > 3]',
'hx-include': '[name="password"],[name="date_of_birth"],[name="subject"]'
})
}
Note: in the POST request you don't have to use this, because you put the HTMX attribute on the outermost form element, so by default HTMX will include each field (child elements of the form) in the request.