Django. Error saving empty string to DateTimeField(blank=True) - django

I have a model with a DateTimeField (blank=True, null=True). However, when I try to save an empty string in it, I get the following error:
>>> from courses.models import Event
>>> x = Event.objects.last()
>>> x.date_start = ""
>>> x.save()
django.core.exceptions.ValidationError: ['“” value has an invalid format. It must be in YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] format.']
I have never seen this, any ideas? All my migrations are up to date.

blank=True [Django-doc] has impact on modelforms, not on the model itself. It does not mean that the element accepts an empty string. blank=True simply means that the field is not required. In case the value is not specified, it will use the default value.
But at the model layer, nothing changes. Since your field has null=True [Django-doc], the default in case you do not fill in the value will be None, so you can use:
>>> from courses.models import Event
>>> x = Event.objects.last()
>>> x.date_start = None
>>> x.save()

Related

DRF - Serializer fields "source" argument unclear behaviour

So, consider the following:
>>> d = {'macAddress': '00:00:00:00:00:00'}
>>> s = DeviceSerializer(data=d)
>>> s
DeviceSerializer(data={'macAddress':'00:00:00:00:00:00'}):
mac_address = CharField(max_length=20, source='macAddress')
>>> s.is_valid()
False
>>> s.errors
{'mac_address': [ErrorDetail(string='This field is required.', code='required')]}
Based on the simple above example and my current understanding of the source field argument I would expect the mac_address field to be automatically mapped to the macAddress in the input data and the serializer to be valid.
Why this is not the case?
Thanks to anyone willing to help out :)
It is just the other way around. source is what is on the python side and the field name on the external/API side.
data = {'mac_address':'00:00:00:00:00:00'}
would lead to:
validated_data == {'macAddress':'00:00:00:00:00:00'}

Django 1.10 UUIDField returns either string or UUID

While upgrading from Django 1.9.13 to Django 1.10.7 I encountered a weird issue with Django's native UUIDField.
We use this UUIDField on our custom User model like:
username = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
In 1.9 this always returns a UUID instance.
In 1.10 this returns a string when creating a new model instance.
Compare the following test examples:
1.9.13
>>> u = User.objects.last()
>>> u2 = UserFactory()
>>> u3 = User.objects.create()
>>> u.pk
UUID('e7e0f87d-1ed4-4293-829f-b0b745ccd676')
>>> u2.pk
UUID('f8e9a4a9-2265-4cd7-9813-00ffe7fd922a')
>>> u3.pk
UUID('0cb736d7-f8a0-4057-9c89-44fa114f4f82')
1.10.7
>>> u = User.objects.last()
>>> u2 = UserFactory()
>>> u3 = User.objects.create()
>>> u.pk
UUID('e7e0f87d-1ed4-4293-829f-b0b745ccd676')
>>> u2.pk
'f8e9a4a9-2265-4cd7-9813-00ffe7fd922a'
>>> u3.pk
'0cb736d7-f8a0-4057-9c89-44fa114f4f82'
This difference gives issues with various unittests. I can work around it by forcing both to string, but I wish to understand why UUIDField behaves the way it does as it feels inconsistent.
The problem was caused by changed behaviour in Django's AbstractBaseUser class.
The class received a clean method which I called on save. Inside the new clean method a normalize_username method was called which forced the username to be text.
By avoiding the super call to AbstractBaseUser we no longer normalize the username which isn't something we wanted anyways as our username field is a UUID.

Why are my model restrictions ignored?

here is my model.py :
from django.db import models
class Code(models.Model):
code = models.CharField(max_length=10)
def __str__(self):
return self.code
However when I enter the shell, this returns no mistake :
In [1]: from mini_url.models import Code
In [2]: co = Code()
In [3]: co.save()
In [4]: co = Code("1234567891011")
In [5]: co.save()
Whereas I expect two errors :
1/ from the fact that co is saved without any field, which is supposed to be impossible since there is no null = True
2/ from the fact that I save a Code whosecode field is longer than 10!
Any Idea?
The empty value of a CharField is the empty string ''.
You do not actually set the code field. What your are actually doing is setting the id.
>>> co = Code("1234567891011")
>>> co.id
"1234567891011"
Try:
co = Code(code="1234567891011")
co.save()
or, if you really do not want to use keyword arguments:
co = Code(None, "1234567891011")
It will raise an exception if you use a database that enforces length constraints (some databases, e.g. SQLite, don't).

Django get_or_create method doesn't return exact object

Here is my models.py:
class Foo(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=30)
In django manage.py shell:
>>> a,b = Foo.objects.get_or_create(name='hi')
>>> b
True
>>> vars(a)
{'_state': <django.db.models.base.ModelState object at 0x02761390>, 'id': None,
'name': 'hi'}
The problem here is the customized id attribute has no value. And if I continue to do the following:
>>> c = foo.objects.get(name='hi')
>>> c.id
1
>>> a is c
False
So that newly created object (denoted by c) already exists now, but b is not an exact reference to it like c is.
Is this a bug? My django version is 1.6.5.
By default, if you have not specified the id field for you model, Django will create AutoField:
id = models.AutoField(primary_key=True)
In your case you've created just IntegerField. I suppose this is the point. In the database, it will still be incremented, but django won't assign the id value from database to the object, returned by get_or_create
By the way, in your question:
>>> b is c
False
b - is a bool value. I think you were trying to do
>>> a is c
But also this is not a good idea. If you want to compare django model objects, use ==, not is:
>>> a == c
You will get False anyway, as a has its id set to None.
You have declared your id field as an IntegerField, so Django does not know it needs to autoincrement. You could define it as an AutoField, but actually there is no reason to declare it at all: Django will automatically define it for you if you leave it out.
Note this has nothing to do with get_or_create: you would have exactly the same problem with a normal create/save.

Custom validation of a ModelField in Django

my model looks like this:
class MyModel(models.Model):
field1 = models.FloatField(default=0)
field2 = models.FloatField(default=0)
This is how it behaves:
>>> m = MyModel()
>>> m.full_clean()
>>> m = MyModel(field1=8.9)
>>> m.full_clean()
>>> m = MyModel(field1='')
>>> m.full_clean()
ValidationError: {'field1': [u'This value must be a float.'], ...
I want it to accept blank strings and have it use 0. I also want it to accept values such as "5:56" and have it use "5.93333". (I already have a function that can do this)
Currently I have this working with a custom form field (and it's ugly as sin), but I want to move it all to use model validation. Whats the best way to go about this?
Make a clean_field1 function in your form and verify it in there. Throw a validation error if it's improper, and reformat and return the "proper" value if it is. Example:
http://docs.djangoproject.com/en/dev/ref/forms/validation/#cleaning-a-specific-field-attribute
Update:
Question clarified: user wants a Model Field that does this.
Override FloatField and the get_prep_value and to_python routines within:
class MagicalFloatField(FloatField):
def to_python( self, value_from_db ):
...
return what_should_go_into_model_field_value
def get_prep_value( self, value_from_field ):
...
return what_should_go_into_db
So you can do the "5:26" <--> "5.26" there. Then use your field in your model:
class MagicalModel(Model):
foo = MagicalFloatField()
Reference:
http://docs.djangoproject.com/en/dev/howto/custom-model-fields/#django.db.models.to_python
Also, for an example of what it expects and how to raise validation errors, look at what you're subclassing -- look up FloatField in site-packages/django/db/models/fields/__init__.py