I got confused, does django automatically calls clean_botcatcher(self): . Does it acts as listener which got triggered when bot makes changes to values?
from django import forms
class FormName(forms.Form):
name = forms.CharField()
email = forms.EmailField()
text = forms.CharField(widget=forms.Textarea)
botcatcher = forms.CharField(required=False,widget =forms.HiddenInput)
def clean_botcatcher(self):
botcatcher = self.cleaned_data['botcatcher']
if len(botcatcher) > 0:
raise forms.ValidationError("Gotcha BOT")
return botcatcher
The clean_() method is called on a form subclass – where is replaced with the name of the form field attribute. This method does any cleaning that is specific to that particular attribute, unrelated to the type of field that it is. This method is not passed any parameters. You will need to look up the value of the field in self.cleaned_data and remember that it will be a Python object at this point, not the original string submitted in the form (it will be in cleaned_data because the general field clean() method, above, has already cleaned the data once).
For example, if you wanted to validate that the contents of a CharField called serialnumber was unique, clean_serialnumber() would be the right place to do this. You don’t need a specific field (it’s a CharField), but you want a formfield-specific piece of validation and, possibly, cleaning/normalizing the data.
The return value of this method replaces the existing value in cleaned_data, so it must be the field’s value from cleaned_data (even if this method didn’t change it) or a new cleaned value.
reference https://docs.djangoproject.com/en/4.0/ref/forms/validation/
I'm assuming you're going through Jose Portilla's course on udemy? Because I just went over this exact section. I believe the def function inside the class FormName runs automatically. He was saying that if the value in the form has been altered by the bot, then this function will catch the bot and give you the validation error.
Related
I try override clean method for model form with foreign key.
Model:
class Doc(Model):
name = CharField()
doc_type = ForeignKey(DictDocType)
Form:
class DocForm(ModelForm):
class Meta:
model = Doc
fields = '__all__'
def clean_doc_type(self)
doc_type_name = self.cleaned_data['doc_type']
try:
DictDocType.objects.get(name=doc_type_name)
except DictDocType.DoesNotExist:
msg = '{0} does not exist in dictdoc {1}.'.format(
doc_type_name, self.cleaned_data['name'])
raise ValidationError(msg)
return name
In the test I get an error:
KeyError: 'name'.
If I remove self.cleaned_data['name'] from msg - I do not get self.cleaned_data['doc_type'].
Where I'm wrong?
You can't cross reference other fields in clean_foo methods, because not all fields' clean_foo methods are called when you are in one of them. There might be some values of the form that are not populated yet, so clean_name() is not yet called when you call clean_doc_type(), thus you don't have self.cleaned_data['name'].
This should be done in clean method. Django doc very explicitly documented this:
By the time the form’s clean() method is called, all the individual
field clean methods will have been run (the previous two sections), so
self.cleaned_data will be populated with any data that has survived so
far. So you also need to remember to allow for the fact that the
fields you are wanting to validate might not have survived the initial
individual field checks.
Also, your clean method doesn't make much sense and not necessary at all. You wouldn't able to choose a foreignkey that doesn't exist in ModelForm. Even if you force the front end to do so, the field would auto fail the validation and give error:
Select a valid choice. foo is not one of the available choices.
i am having a unique kind of problem.
i am putting "$90" value in my floatField.
i assume it should be cleaned and stored as "90.0" in the model.
but when i input "$90" and call clean_filed method, it does not call that clean method.
but when i apply the breakpoint on clean(self) mehthod and try to get its value, its says None.
please help me out. here is my code.
Model
class Project(models.Model):
foo = models.FloatField("foo",
null=True, blank=True
)
forms
widgets = {
'foo': forms.TextInput(attrs={'currency': 'true'})}
def clean_foo(self):
value = self.cleaned_data.get('foo')
print value # i get NONE here.............
return value
The clean_<field> hooks in the forms API are for doing additional validation. It is called after the field calls its clean method. If the field does not consider the input valid then the clean_<field> is not called. This is noted in the custom validation docs https://docs.djangoproject.com/en/stable/ref/forms/validation/
For any field, if the Field.clean() method raises a ValidationError, any field-specific cleaning method is not called. However, the cleaning methods for all remaining fields are still executed.
In your case because $90 does not validate as a float it will not be called. Instead what you should do is create a subclass of the FloatField which cleans the input (removing $, etc) prior to validating the input as a float.
I have the following model in Django 1.5:
class Person(models.Model):
name = models.CharField(max_length=50)
Note that according to https://docs.djangoproject.com/en/dev/ref/models/fields/
name.blank is by default False which means it must be specified.
However, I could successfully create a Person object as follows:
Person.objects.create()
Notice the name is not specified. What is going on?
Ok, the answer from the docs is :
Note that this is different than null. null is purely database-related, whereas blank is validation-related. If a field has blank=True, form validation will allow entry of an empty value. If a field has blank=False, the field will be required.
Another catch:
Note that validators will not be run automatically when you save a model, but if you are using a ModelForm, it will run your validators on any fields that are included in your form.
It's your responsibility to call the clean methods before saving if you're not using a form.
blank only applies to form field validation as in the admin, django forms, etc.
null on the other hand is a database level nullable column.
As for why blank results in a default '', I had really just accepted it as "that's the way it works" but here's where it appears to be in django.db.models.Field
def get_default(self):
"""
Returns the default value for this field.
"""
if self.has_default():
if callable(self.default):
return self.default()
return force_unicode(self.default, strings_only=True)
if (not self.empty_strings_allowed or (self.null and
not connection.features.interprets_empty_strings_as_nulls)):
return None
return ""
# ^ this
Django creates your user with an empty string. You can actually run Person.objects.all() and it will give you a list, if you save that to a variable called user_list and do something like user_list[0], it will return a user object with an empty string. I do not know how or why it does this.
I'm trying to do this in Django:
When saving an object in the Admin I want to save also another object of a different type based on one of the fields in my fist object.
In order to do this I must check if that second object already exists and return an validation error only for the particular field in the first object if it does.
My problem is that I want the validation error to appear in the field only if the operation is insert.
How do I display a validation error for a particular admin form field based on knowing if the operation is update or insert?
P.S. I know that for a model validation this is impossible since the validator only takes the value parameter, but I think it should be possible for form validation.
This ca be done by writing a clean_[name_of_field] method in a Django Admin Form. The insert or update operation can be checked by testing self.instance.pk.
class EntityAdminForm(forms.ModelForm):
def clean_field(self):
field = self.cleaned_data['field']
insert = self.instance.pk == None
if insert:
raise forms.ValidationError('Some error message!')
else:
pass
return field
class EntityAdmin(admin.ModelAdmin):
form = EntityAdminForm
You have to use then the EntityAdmin class when registering the Entity model with the Django admin:
admin.site.register(Entity, EntityAdmin)
You can write your custom validation at the model level:
#inside your class model ...
def clean(self):
is_insert = self.pk is None
from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
#do your business rules
if is_insert:
...
if __some_condition__ :
raise ValidationError('Dups.')
Create a model form for your model. In the clean method, you can set errors for specific fields.
See the docs for cleaning and validating fields that depend on each other for more information.
That is (probably) not an exact answer, but i guess it might help.
Django Admin offers you to override save method with ModelAdmin.save_model method (doc is here)
Also Django api have a get_or_create method (Doc is here). It returns two values, first is the object and second one is a boolean value that represents whether object is created or not (updated an existing record).
Let me say you have FirstObject and SecondObject
In your related admin.py file:
class FirstObjectAdmin(admin.ModelAdmin):
...
...
def save_model(self, request, obj, form, change):
s_obj, s_created = SecondObject.objects.get_or_create(..., defaults={...})
if not s_created:
# second object already exists... We will raise validation error for our first object
...
For the rest, I do not have a clear idea about how to handle it. Since you have the form object at hand, you can call form.fields{'somefield'].validate(value) and write a custom validation for admin. You will probably override clean method and try to trigger a raise ValidationError from ModelAdmin.save_model method. you can call validate and pass a value from there...
You may dig django source to see how django handles this, and try to define some custom validaton steps.
I have a ModelForm field that is based on the following Model:
class Phrase(models.Model):
subject = models.ForeignKey(Entity) # Entity is unique on a per Entity.name basis
object = models.ForeignKey(Entity) # Entity is unique on a per Entity.name basis
The modelform (PhraseForm) has a field 'subject' that is a CharField. I want users to be able to enter a string. When the modelform is saved, and the string does not match an existing Entity, a new Entity is created.
This is why I had to overwrite the "subject" field of the Modelform, as I cannot use the automatically generated "subject" field of the Modelform (I hope I'm making myself clear here).
Now, all tests run fine when creating a new Phrase through the modelform. But, when modifying a Phrase:
p = Phrase.objects.latest()
pf = PhraseForm({'subject': 'anewsubject'}, instance=p).
pf.is_valid() returns False. The error I get is that "object" cannot be None. This makes sense, as indeed, the object field was not filled in.
What would be the best way to handle this? I could of course check if an instance is provided in the init() function of the PhraseForm, and then assign the missing field values from the instance passed. This doesn't feel as if it's the right way though, so, is there a less cumbersome way of making sure the instance's data is passed on through the ModelForm?
Now that I'm typing this, I guess there isn't, as the underlying model fields are being overwritten, meaning the form field values need to be filled in again in order for everything to work fine. Which makes me rephrase my question: is the way I've handled allowing users to enter free text and linking this to either a new or existing Entity the correct way of doing this?
Thanks in advance!
Why are you modifying using the form.
p = Phrase.objects.latest()
p.subject = Entity.objects.get_or_create(name='anewsubject')[0]
docs for get_or_create
If you are actually using the form it should work fine:
def mod_phrase(request, phrase_id=None):
phrase = get_object_or_404(Phrase, pk=phrase_id)
if request.method == 'POST':
form = PhraseForm(request.POST, instance=phrase)
if form.is_valid():
form.save()
return HttpResponse("Success")
else:
form = PhraseForm(instance=phrase)
context = { 'form': form }
return render_to_response('modify-phrase.html', context,
context_instance=RequestContext(request))
Setting the instance for the ModelForm sets initial data, and also lets the form know which object the form is working with. The way you are trying to use the form, you are passing an invalid data dictionary (lacks object), which the form is correctly telling you isn't valid. When you set the data to request.POST in the example above, the request.POST includes the initial data which allows the form to validate.