I am trying to change the required of a form field to 'False' depending on the input of another field.
form:
class MyNewForm(forms.Form):
def __init__(self, *args, **kwargs):
self.users = kwargs.pop('users', None)
super(MyNewForm, self).__init__(*args, **kwargs)
self.fields['name'] = forms.CharField(
max_length=60,
required=True,
label="Name *:",
widget=forms.TextInput(
attrs={'class' : 'form-control', 'placeholder': 'Name'})
)
def clean_this_other_field(self):
# change required field
name = self.fields['name']
print name.required
name.required=False
print name.required
results of print:
True
False
So the field required is changing to 'False' but when the form page reloads it reverts back to 'True'
Any suggestions?
EDIT - Problem solved
For anyone landing on this with a similar issue:
def clean_remove(self):
cleaned_data = super(MyNewForm, self).clean()
remove = cleaned_data.get('remove', None)
if remove:
self.fields['name'].required=False
Remove is the first field in the form so adding this clean on the remove field resolves the issue.
Can you give more of your code when this problem appers?
If u use ModelForm you can do something like:
class YourForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(YourForm, self).__init__(*args, **kwargs)
self.fields['name'].required = True
class Meta:
model = YouModel
fields = (...)
Django Model Forms - Setting a required field
U can add required using widget too:
email = forms.EmailField(
max_length=100,
required=True,
widget=forms.TextInput(attrs={ 'required': 'true' }),
)
Related
I have the following situation with Django that i cannot resolve. How to filter/sort field 'training' to represent only the trainings that are owned by the user that is logged in. Thank you in advance!
class Exercise(mоdels.Model):
MAX_LENGTH = 25
name = mоdels.CharField(
mаx_length=MAX_LENGTH
)
weight = mоdels.IntegerField()
series = mоdels.IntegerField()
reps = mоdels.IntegerField()
training = models.FоreignKey('Workout', оn_delete=mоdels.CASCADE)
class CreateExerciseFоrm(forms.ModelFоrm):
class Meta:
model = SingleExercisе
fields = ('name', 'weight', 'series', 'reps', 'training', )
You can override the __init__ method of your form so that you can pass an additional argument and modify the queryset of your field based on that argument. To accept a user keyword argument:
class CreateExerciseFоrm(forms.ModelFоrm):
class Meta:
model = SingleExercisе
fields = ('name', 'weight', 'series', 'reps', 'training')
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
if user:
self.fields['training'].queryset = Workout.objects.filter(user=user)
Then in your view you pass the logged in user to your form
form = CreateExerciseFоrm(user=request.user)
And when you pass POST data
form = CreateExerciseFоrm(request.POST, user=request.user)
the form does not pass validation and throws an error, although the correct data is entered, what is the problem?
I enter in the input field +79211234569 and gives an error in html Select the correct option. Your option is not among the valid values.
form data: <'form': <RelatedAddForm bound=True, valid=False, fields=(name;phone)>
forms
class ListTextWidget(forms.Select):
template_name = 'include/_forms_clients_datalist.html'
def format_value(self, value):
# Copied from forms.Input - makes sure value is rendered properly
if value == '' or value is None:
print('ListTextWidget None')
return ''
if self.is_localized:
print('ListTextWidget local')
return formats.localize_input(value)
return str(value)
class ChoiceTxtField(forms.ModelChoiceField):
widget=ListTextWidget()
class RelatedAddForm(forms.ModelForm):
phone = ChoiceTxtField(queryset=Clients.objects.order_by('-phone'))
class Meta:
model = Clients
fields = ['name', 'phone']
widgets = {
'name': forms.TextInput(attrs={'class': 'form-control', 'autocomplete': 'off'}),
}
models
class Clients(models.Model):
name = models.CharField(max_length=150, blank=True, verbose_name='Имя')
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed.")
phone = models.CharField(validators=[phone_regex], unique=True, max_length=17, verbose_name='Телефон')
def get_absolute_url(self):
return reverse('view_clients', kwargs={'pk': self.pk})
def __str__(self):
return self.phone
UPD:
request_post <QueryDict: {'csrfmiddlewaretoken': ['m9mKTv4kLWSFmW6Jj39OUAZ0zINBoFjvphjYADWvY97lk1oKAB3LAHhxOpmXnKbo'], 'cli ents-name': ['test'], 'clients-phone': ['+79121234566']}> passes the name variable and the phone variable in the correct format
Most likely the problem is that the check of the phone field is related to the ChoiceTxtField?
ModelChoiceField assumes selection of a previously saved model only (specified in the queryset parameter). There is no need to inherit ModelChoiceField, changes are made at the widget level. Therefore, you need to replace it with CharField.
class ListTextWidget(forms.Select):
template_name = 'include/_forms_clients_datalist.html'
class PhoneInputField(forms.CharField):
widget=ListTextWidget()
class RelatedAddForm(forms.ModelForm):
phone = PhoneInputField()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['phone'].choices = Clients.objects.order_by('-phone').values_list('phone')
class Meta:
model = Clients
fields = ['name', 'phone']
widgets = {
'name': forms.TextInput(attrs={'class': 'form-control', 'autocomplete': 'off'}),
}
I have a customuser model with
class customuser(AbstractUser):
# additional fields
def __str__(self):
return self.username
I have another model, that becomes the foreign key for this model
class bfs_support_ticket_model(models.Model):
ticket_created_by = models.ForeignKey(customuser, on_delete = models.CASCADE)
Why doesn't Django renders username for the form, but render or returns username everywhere correctly
class ticket_edit_form(ticket_create_form):
# ticket_created_by = forms.CharField(widget=forms.TextInput(attrs={'class' : 'form-control', 'readonly' : True})) # does not work in this way
def __init__(self, *args, **kwargs):
super(ticket_edit_form,self).__init__(*args, **kwargs)
# self.fields['ticket_created_by'].disabled = True
self.fields['ticket_created_by'].widget = forms.TextInput(attrs={'class' : 'form-control', 'readonly' : True}) # doesnot work in this way too
class Meta:
model=bfs_support_ticket_model
exclude=['ticket_last_updated_by']
When the form is rendered it just prints the customuser.id instead of customuser.username
But when no form initialization is made, it return the customuser.username correctly
i.e. when
class ticket_edit_form(ticket_create_form):
def __init__(self, *args, **kwargs):
super(ticket_edit_form,self).__init__(*args, **kwargs)
self.fields['ticket_created_by'].disabled = True # only this line present it renders customuser.username
class Meta:
model=bfs_support_ticket_model
exclude=['ticket_last_updated_by']
Please help me, where I am going wrong
Edit:
Why does
self.fields['ticket_created_by'].disabled = True # prints username
while
self.fields['ticket_created_by'].widget = forms.TextInput(attrs={'class' : 'form-control', 'readonly' : True}) # this doesn't
class bfs_support_ticket_model(models.Model):
ticket_created_by = models.ForeignKey(customuser, on_delete = models.CASCADE)
def __str__(self):
return self.ticket_created_by.customuser.username
Observations:
Since the field is a Foreignkey, it gets rendered as a modelchoicefield, hence initializing the field as charfield, only prints the exact value present in the db (in this case customuser.id)
To make it render properly, I had to initialize it as a modelchoicefield, like
ticket_created_by = forms.ModelChoiceField(queryset = customuser.objects.all(), widget = forms.Select(attrs={'class' : 'custom-select custom-select-sm mb-3', 'disabled' : True}))
Please advice, if this is a correct solution
I have a form that has two fields (mail and status). I want to have the status field hidden only when the user has a profile not equal to "tech" so that the user cannot change its value in that case.
What I was trying to do, but I still didn't get it to work since it throws me TypeError error: __init __ () got an unexpected keyword argument 'user', is to overwrite the __init __ () method of RequestForm in forms.py and on the other hand, overwrite the get_form_kwargs () method to pass the user to the form. I post the code that I understand relevant:
views.py:
...
class RequestUpdate(UpdateView):
model = Request
form_class = RequestForm
template_name = 'request/request_form.html'
success_url = reverse_lazy('request:request_list')
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
forms.py:
class RequestForm(forms.ModelForm):
class Meta:
model = Request
fields = [
'mail',
'status',
]
labels = {
'mail': 'E-Mail (including #domain.example)',
'status': "Request's status:"
}
widgets = {
'mail': forms.EmailInput(attrs={'class': 'form-control'}),
}
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
if self.user.is_tech == False:
self.fields.pop('status')
models.py
...
class Request(models.Model):
mail = models.CharField(max_length=50)
states = [('1','Pending'), ('2','In process'), ('3','Done')]
status = models.CharField(max_length=50, choices=states, default='1')
user = models.ManyToManyField(Requester, blank=True)
Why does the * unexpected keyword argument 'user' * error occur? What suggestions could you make to hide that field according to the user's profile?
I would suggest to only override get_form method as follow in RequestUpdate:
def get_form(self, *args, **kwargs):
form = super().get_form(self.form_class)
if not self.request.user.status.is_tech:
form.fields.pop('status')
return form
I don't know why your code fails, but anyway you can tweak your form in view class so no need to do more work.
Django password field input showing up as plaintext despite widget=forms.PasswordInput declaration:
forms.py
from django.contrib.auth.models import User
class LoginForm(forms.ModelForm):
# specify password type so that passwords show up as *******, not plaintext
# but this doesn't work if placeholder = ''
password = forms.CharField(widget=forms.PasswordInput)
class Meta:
model = User
fields = ["username", "password"]
def __init__(self, *args, **kwargs):
# first call the 'real' __init__()
super(LoginForm, self).__init__(*args, **kwargs)
# then do extra stuff:
self.fields['username'].help_text = ''
self.fields['password'].widget = forms.TextInput(attrs={'placeholder': ''})
self.fields['password'].widget.attrs['class'] = 'form-control'
So interestingly, when I surface this form in a template, the password value shows up as plaintext instead of '******' text. But only if I add the 'placeholder': '' line. I inspected the form element and figured out that when I added the 'placeholder': '' line, type='password' was being changed to type='text' in the <input type='FOO'></input>element in the rendered HTML.
--> How do I keep this from happening, so passwords continue to show up as plaintext, without removing my 'placeholder': '' line?
You should not be using forms.TextInput for your password field. Django provides a PasswordInput widget that is more appropriate. Try this:
class Meta:
model = User
fields = ["username", "password"]
def __init__(self, *args, **kwargs):
# first call the 'real' __init__()
super(LoginForm, self).__init__(*args, **kwargs)
# then do extra stuff:
self.fields['username'].help_text = ''
self.fields['password'].widget = forms.PasswordInput(attrs={'placeholder': ''})
self.fields['password'].widget.attrs['class'] = 'form-control'
While you can edit the type of the field manually, it's better convention to use the widget.