I have a model with two fields:
class Books(model.Models)
status = models.IntegerField(
null = False, blank = False, default = None,
choices = [(1,'in stock'),(2,'out of stock'),(3,('other, specify below')]
)
other = models.TextField(
blank = True, null = True,
)
I then use a RadioSelect in my form widget to render status and all is well with the world.
However, I want to make other required if option 3 in status is selected. How can I do this server side via form validation?
E.g. the form should fail validation if choice 3:'other' is selected and other remains blank or null.
If you want to validate your data in the form, you should override the clean method and add your conditions there:
class YourForm(forms.Form): # or a ModelForm
#...
def clean(self):
cleaned_data = super().clean()
status = cleaned_data.get("status")
other = cleaned_data.get("other")
if status == 3 and (not other):
raise forms.ValidationError('Provide other field')
return data
Related
I can't figure out how to populate a django ChoiceField with initial data. Preferrably want to do it in the view, as it will change depending on the parameters I pass to the view.
views.py
def index(request):
init_ingredients = [{'food':'Candy','amt':12,'units':'cup'},{'food':'Bacon','amt':9,'units':'cup'}]
IngredientsFormSet = formset_factory(IngredientLineForm, can_delete=True)
if request.method == 'POST':
formset = IngredientsFormSet(request.POST, request.FILES)
...
else:
formset = IngredientsFormSet(initial=init_ingredients)
the 'food' field and the 'amt' field populate, but the 'units' field - which is an html Select input does not populate with initial value. Do I need to define choices too? and have the initial value be one of them?
forms.py
class IngredientLineForm(forms.Form):
food = forms.CharField(widget=forms.TextInput(attrs={'class':'foods form-control'})) #class = food
units = forms.ChoiceField(widget=forms.Select(attrs={'class':'units form-control'}))
amt = forms.CharField(widget=forms.NumberInput(attrs={'class':'amt form-control'}))
I use:
class netadminGlobalFormView(LoginRequiredMixin, FormView):
model = netInfo
form_class = netInfoForm
def get_initial(self):
initial = super(netadminGlobalFormView, self).get_initial()
initial['eth0_ip'] = self.model_instance.get_eth0_ip_stdout
initial['config_type'] = 'DHCP'
return initial
Note that here:
initial['config_type'] = 'DHCP'
I set a value from selection:
# value displayed value
config_types=(
('DHCP', 'Automatic (DHCP)'),
('MANUAL', 'Static (manual)')
)
and form definition includes the following:
class netInfoForm(ModelForm):
eth0_ip=forms.GenericIPAddressField(protocol='IPv4',
widget=forms.TextInput(
attrs={'placeholder': 'xxx.xxx.xxx.xxx'}),
max_length=IPv4_addr_chars,
label=IPv4_addr_html_label,
help_text='required: i.e. 192.168.111.12 ',
required=True
# ,error_messages={'required': 'Please enter IPv4 address, i.e. 192.168.111.12'}
)
config_type = forms.ChoiceField(choices=config_types, widget=forms.RadioSelect())
#,initial='MANUAL')
and in model:
class netInfo(models.Model):
eth0_ip = models.CharField(max_length = IPv4_addr_chars, blank=True, null=False, default=get_eth0_ip_stdout)
config_type = models.CharField(max_length=6, blank=False, null=False, default="DHCP")
W/o using initial value 'DHCP' or 'MANUAL' in sample above the choice starts unselected. Also note that initial could be set in form class (commented above).
So, exactly to your questions:
1> Do I need to define choices too?
Yes, choices should be defined in model.
2> and have the initial value be one of them?
Yes, initial values for choices must match your choices definition for form and model.
At least that's so in django 2.0 .
As about question 1) - I can't claim there is no ability to init choices other way, but for my sample answer for question 2) is exactly that - non-matching values ignored (didn't raise exception).
I want to filter by "status" which is an added field but it does not work:
serial_number_list = TestSerialNumber.objects.filter(test_pool = test_pool, status='rejected')
I get this error:
Cannot resolve keyword 'status' into field. Choices are: id, serial_number, test_pool, testresult, testrun
This is my model class:
class TestSerialNumber(models.Model):
serial_number = models.ForeignKey("core.SerialNumber", on_delete=models.PROTECT)
test_pool = models.ForeignKey("TestPool", blank=True, null=True)
def __unicode__(self):
return self.serial_number.serial_number
def panel_code(self):
return self.serial_number.panel.panel_code
def status(self):
try:
test_result = self.testresult_set.latest('report')
except TestResult.DoesNotExist:
return 'unknown'
else:
return test_result.test_status.name
Also, when exporting to json, i get just the fields: serial_number and test_pool, just the ids and not the rest.
Thanks in advance!
If you want to do it you have to create a field with name "status"
STATUS_CHOICES = (
(1, 'rejected'),
(2, 'blabla'),
)
status = models.IntegerField(choices=STATUS_CHOICES)
By the structure of your code that you have provided, status is not a field its a method. So if you want to get the status for and object you need to filter it out first and then you can access the method that will return its status for that object.
serial_number_list = TestSerialNumber.objects.filter(test_pool = test_pool)
print serial_number_list[0].status # as filter return a list
this will give you the status for that object.
The error Cannot resolve keyword 'status' into field. Choices are:id, serial_number, test_pool, testresult, testrun occurs when you try to access the model with a invalid field, by invalid I mean the field that doesnot exist in the model or the database
I have a Model where one boolean ModelField is dependent upon another. The Model is configured like so:
class Situation(models.Model):
ctcs = models.BooleanField(verbose_name="Cross-Technology Critical Situation", blank=True)
has_been_ctcs = models.BooleanField(editable=False, default=False)
The ctcs field is rendered as a checkbox in the ModelForm for this model. What I want to do is, if the ctcs field has been checked, I also want to set has_been_ctcs to True. So what I am doing is setting cleaned_data['has_been_ctcs'] = True on the ModelForm. I've tried doing this both in my view that handles the POST request, as well as within the ModelForm clean function like so:
class SituationForm(forms.ModelForm):
def clean(self):
cleaned_data = super(SituationForm, self).clean()
ctcs = cleaned_data.get("ctcs")
if ctcs:
self.cleaned_data['has_been_ctcs'] = True
return cleaned_data
And here is the snippet from the view that handles creation of a new Situation model:
sit_form = SituationForm(request.POST)
if sit_form.is_valid():
print sit_form.cleaned_data['ctcs'] # Prints True
if sit_form.cleaned_data['ctcs']:
print "Checking form has_been_ctcs"
# Have also tried setting sit_form.cleaned_data['has_been_ctcs'] here, no difference from doing it in `def clean()`
print sit_form.cleaned_data['has_been_ctcs'] # Prints True
sit = sit_form.save()
print sit.has_been_ctcs # Prints False
I cannot seem to get the has_been_ctcs value of True to propagate to the Situation model. How can I go about doing this?
cleaned_data only works for fields that are included in the form. What you want to do is:
sit_form = SituationForm(request.POST)
if sit_form.is_valid():
if sit_form.cleand_data['ctcs']:
sit_form.instance.has_been_ctcs = True
sit = sit_form.save()
I want my ChoiceField in ModelForm to have a blank option (------) but it's required.
I need to have blank option to prevent user from accidentally skipping the field thus select the wrong option.
This works for at least 1.4 and later:
CHOICES = (
('', '-----------'),
('foo', 'Foo')
)
class FooForm(forms.Form):
foo = forms.ChoiceField(choices=CHOICES)
Since ChoiceField is required (by default), it will complain about being empty when first choice is selected and wouldn't if second.
It's better to do it like this than the way Yuji Tomita showed, because this way you use Django's localized validation messages.
You could validate the field with clean_FOO
CHOICES = (
('------------','-----------'), # first field is invalid.
('Foo', 'Foo')
)
class FooForm(forms.Form):
foo = forms.ChoiceField(choices=CHOICES)
def clean_foo(self):
data = self.cleaned_data.get('foo')
if data == self.fields['foo'].choices[0][0]:
raise forms.ValidationError('This field is required')
return data
If it's a ModelChoiceField, you can supply the empty_label argument.
foo = forms.ModelChoiceField(queryset=Foo.objects.all(),
empty_label="-------------")
This will keep the form required, and if ----- is selected, will throw a validation error.
You can also override form's __init__() method and modify the choices field attribute, reasigning a new list of tuples. (This may be useful for dynamic changes):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['my_field'].choices = [('', '---------')] + self.fields['my_field'].choices
in argument add null = True
like this
gender = models.CharField(max_length=1, null = True)
http://docs.djangoproject.com/en/dev/ref/models/fields/
for your comment
THEME_CHOICES = (
('--', '-----'),
('DR', 'Domain_registery'),
)
theme = models.CharField(max_length=2, choices=THEME_CHOICES)
I have a form which has two fields for Integers:
class DemoForm(forms.Form):
b_one = forms.IntegerField(
error_messages={
'required':'Please enter a valid number.'
},
label = 'NumberOne',
required = True,
help_text = 'e.g. 266492'
)
b_two = forms.IntegerField(
error_messages={
'required':'Please enter a valid number.'
},
label = 'NumberTwo',
required = True,
help_text = 'e.g. 262865',
)
and I am validating these fields as
def clean_b_one(self):
self.validate_form(self.cleaned_data['b_one'])
def clean_b_two(self):
self.validate_form(self.cleaned_data['b_two'])
Now what I want to do is in validate_form I check, if these numbers exists in database, else raising forms.ValidationError
But what I also want to do some other validations when these form fields are valid, basically some check on the form based on input and raise some custom errors, where can I add logic? or what is the best way of doing it?
You can do individual field verifying in clean_b_one like you have, and raise ValidationErrors if something doesn't fit. Or override the clean method to do cross-field checking. General documentation to be found here.