Django Model Form doesn't seem to validate the BooleanField - python

In my model the validation is not validating for the boolean field, only one time product_field need to be checked , if two time checked raise validation error.
product_field = models.BooleanField(default=False)
def clean(self):
if (self.product_field is not None).count() < 1:
raise ValidationError(
{
"product_field": _(" Product field can be select once")
}
)

Boolean and None are not always the same. I think that is the root of your problem. Here is None vs False:
# python3
> False is None
False
I find the code and information confusing.
I assume product_filed is a typo. Generally models.BooleanField have to possibilities True and False. If blank=True and required=False are set then None can happen (unset aka NULL).
Keep the default=False and don't check for None, you should never see None with a default on a Bool.
You mention if two time checked raise validation error - Huh? This is also confusing. What are you trying to accomplish? The logic and details do not make complete sense to me.
To check the bool you can use if not self.product_field: ... raise.
What is being counted?

Related

Django form test non-field errors

I'd like to ask for a method to check if any kind of non-field errors was raised during form validation in Django. So far I found solution to do this during view testing. Well, it wasn't what I was looking for since I'm interested in doing it at form tests.
I know the way to look for fields' errors:
class FormTest(TestCase)
def test_fields_errors(self):
"""
Suppose form has one field which has to be filled
"""
form = TestForm()
form.full_clean()
self.assertFalse(form.is_valid())
self.assertIsInstance(
form.errors.as_data()['required_field'][0],
ValidationError
)
self.assertEquals(
form.errors['deadline_datetime'][0],
'This field must not be empty'
)
Is there any way to do this when we deal with non-field errors?
I answer my own question, but I also wanna start discussion if there is any better solution
With small help of debugger, I found that non-field errors appear in form.errors as well. But this time there wasn't any dict key named like non-field errors. All I found was a bit strange key __all__, but it allowed me to access demanded errors:
class FormTest(TestCase)
def test_fields_errors(self):
"""
Suppose form has two fields and either one has to be filled
"""
form = TestForm()
form.full_clean()
self.assertFalse(form.is_valid())
self.assertIsInstance(
form.errors.as_data()['__all__'][0],
ValidationError
)
assertEquals(
errors['__all__'][0],
'Fill at least one field'
)
So, I was able to find the error I was looking for during testing non-field errors raise.

How do I make integers optional in DRF

I have a serializer with an integer field
foo = serializers.IntegerField()
and I'd like that field to be optional. It seems obvious to me that
foo = serializers.IntegerField(required=False)
should work, but it doesn't, I get the error message:
{"error":{"foo":["A valid integer is required."]
I though I said that it wasn't required. I also tried adding a default,
serializers.IntegerField(required=False, default=42)
Am I missing something? Is this even possible?
While using 'required = False'
Normally an error will be raised if a field is not supplied during
deserialization.
Setting this to False also allows the object attribute or dictionary key
to be omitted from output when serializing the instance.
you should try setting: 'null=True'
Normally an error will be raised if None is passed to a serializer field. Set this keyword argument to True if None should be considered a valid value.
Defaults to False
for further reading DRF docs

Django Datetime Issue

What I am trying isn't difficult, however it really isn't working. I cannot see where an error is. In my abstract user model I have an is_donator method that just does not work.
#property
def is_donator(self):
if(self.donator >= datetime.now()):
return True
else:
return False
Some reason it just does not return anything, it all looks alright to me though, any ideas?
You have two related issues. The first is you are using the wrong comparison.
if(self.donator >= datetime.now()):
This means that the donor must become a donor at some point in the future.
Change it to
if(self.donator <= datetime.now()):
This will ensure they became a donor in the past.
The other issue you have is that you are using auto_now:
Automatically set the field to now every time the object is saved.
Useful for “last-modified” timestamps. Note that the current date is
always used; it’s not just a default value that you can override.
This, then, relates to your first issue. Every time the user field is updated - if you don't explicitly set your field - it defaults to now.
Update based on comments: This is checking if your donator is not null and also ensure that it exists. If it doesn't exist, it is up to you to determine your logic, though if it doesn't exist, your user isn't a donator. You can probably just return False in your except block. This block is pretty verbose, but it shows you explicitly what is happening.
def is_donator(self):
try:
if self.donator: # This is checking that self.donator is not null
if(self.donator >= datetime.now()):
return True
else:
return False
else:
# return false? # This runs if self.donator is null
except NameError:
# do something (or just 'pass', because your self.donator value doesn't exist)

Django - NullBooleanField won't retain False value from custom save() method

Newcomer to Django, so forgive me if I'm missing something obvious or doing something stupid here...
I have a Django model with a custom save() extension. Trimming the unrelated fields and methods, it looks like this:
class Content(models.Model):
url = models.URLField(max_length=1000)
image = models.URLField(blank=True, max_length=1000)
image_type = models.NullBooleanField(default=None)
def save(self, *args, **kwargs):
if self.url:
img, img_type = image_extractor(self.url)
print 'extractor returned: ', img_type
print 'before set: ', self.image_type
setattr(self, 'image', img)
setattr(self, 'image_type', img_type)
print 'after set: ', self.image_type
super(Content, self).save(*args, **kwargs)
print 'from query: ', Content.objects.get(url=self.url).image_type
The image_extractor function returns a url string and a boolean value representing the image type: True for images larger than a certain width, False for smaller images, or None if no suitable image was found. The problem is, when I query Content.objects and check image_type, all objects return either True or None. Hence the print statements. The images with image_type None or True work as expected, but when image_type is returned False, this is what prints:
extractor returned: False
before set: None
after set: False
from query: True
Somehow the call to super().save always reverts image_type to True if I try to set False (but not if I set None). I admit, I don't understand super() very well, but based on the documentation it seems to be necessary when extending the model function. Can anyone explain what's going on here, and how I can fix it?
Edit: I notice that I can change the value to False through the admin page and it stays False. But I still need to fix the save() method. For now, I have just replaced the boolean field with an integer field and saved values as 0 or 1, which does work. But I'd still like to know why the more appropriate field doesn't.
Since writing this question, I had moved ahead with a temporary solution of using an IntegerField to store the boolean as 0 or 1. Upon deciding to revisit the problem tonight and switching back to the NullBooleanField, suddenly everything works as intended. So it seems danodonovan was correct: the code above is correct, and unfortunately I still have no idea what was causing the error. Something else I changed while using the IntegerField must have resolved the issue. Thanks to those of you who have taken a look and offered some opinions, and I'm sorry to have wasted your time.
the way you overload save method looks good.
I think you are not loading the good object from database.
Use the primary key will probably works better.
In your case just replace that:
Content.objects.get(url=self.url).image_type
by that:
Content.objects.get(pk=self.pk).image_type
Looks at the documentation: https://docs.djangoproject.com/en/dev/ref/models/instances/#the-pk-property
Extract from the django documention:
Once the object has been saved, you must reload the object in order to
access the actual value that was applied to the updated field:
product = Products.objects.get(pk=product.pk)
print(product.number_sold)

0 value in Django PositiveIntegerField?

Can a field of type models.PositiveIntegerField contain a 0 value? I'm doing something like:
points = models.PositiveIntegerField()
Thanks,
I know I should try it myself, but I haven't a Django environment here.
Yes, it can. It is debatable whether it should--there is a longstanding bug report: http://code.djangoproject.com/ticket/7609
For those looking to exclude the 0, it's easy to write a validator for that.
def validate_nonzero(value):
if value == 0:
raise ValidationError(
_('Quantity %(value)s is not allowed'),
params={'value': value},
)
and then use it as such
fill_qty = models.PositiveIntegerField(
default=0,
validators=[MaxValueValidator(1000000), validate_nonzero]
)
Yes.
The model field reference says so. For completeness, also quoted here:
PositiveIntegerField
class PositiveIntegerField([**options])
Like an IntegerField, but must be either positive or zero (0). The value 0 is accepted for backward compatibility reasons.
Well by the definition of a Positive Integer, it shouldn't accept a zero value, but django actually considers it in the sense of none-negative number which is zero inclusive. So, Yes it can accept a zero value
Yes, "PositiveIntegerField" can contain "0" value.
For example, I defined the model "Point" as shown below:
# "myapp/models.py"
from django.db import models
class Point(models.Model):
points = models.PositiveIntegerField()
Then, run this command below:
python manage.py makemigrations && python manage.py migrate
Now, I opened "db.sqlite3" then as you can see, "CHECK("points" >= 0)" is set to "points" field which means "PositiveIntegerField" can contain "0" value:

Categories