Fixed #9649 -- Better error handling in model creation.
Previously, you could explicitly assign None to a non-null ForeignKey (or other) field when creating the model (Child(parent=None), etc). We now throw an exception when you do that, which matches the behaviour when you assign None to the attribute after creation. Thanks to ales.zoulek@gmail.com and ondrej.kohout@gmail.com for some analysis of this problem. git-svn-id: http://code.djangoproject.com/svn/django/trunk@9983 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
b5d4a8ae1b
commit
53da1e4794
|
@ -224,12 +224,13 @@ class Model(object):
|
||||||
# keywords, or default.
|
# keywords, or default.
|
||||||
|
|
||||||
for field in fields_iter:
|
for field in fields_iter:
|
||||||
rel_obj = None
|
is_related_object = False
|
||||||
if kwargs:
|
if kwargs:
|
||||||
if isinstance(field.rel, ManyToOneRel):
|
if isinstance(field.rel, ManyToOneRel):
|
||||||
try:
|
try:
|
||||||
# Assume object instance was passed in.
|
# Assume object instance was passed in.
|
||||||
rel_obj = kwargs.pop(field.name)
|
rel_obj = kwargs.pop(field.name)
|
||||||
|
is_related_object = True
|
||||||
except KeyError:
|
except KeyError:
|
||||||
try:
|
try:
|
||||||
# Object instance wasn't passed in -- must be an ID.
|
# Object instance wasn't passed in -- must be an ID.
|
||||||
|
@ -245,11 +246,11 @@ class Model(object):
|
||||||
val = kwargs.pop(field.attname, field.get_default())
|
val = kwargs.pop(field.attname, field.get_default())
|
||||||
else:
|
else:
|
||||||
val = field.get_default()
|
val = field.get_default()
|
||||||
# If we got passed a related instance, set it using the field.name
|
if is_related_object:
|
||||||
# instead of field.attname (e.g. "user" instead of "user_id") so
|
# If we are passed a related instance, set it using the
|
||||||
# that the object gets properly cached (and type checked) by the
|
# field.name instead of field.attname (e.g. "user" instead of
|
||||||
# RelatedObjectDescriptor.
|
# "user_id") so that the object gets properly cached (and type
|
||||||
if rel_obj:
|
# checked) by the RelatedObjectDescriptor.
|
||||||
setattr(self, field.name, rel_obj)
|
setattr(self, field.name, rel_obj)
|
||||||
else:
|
else:
|
||||||
setattr(self, field.attname, val)
|
setattr(self, field.attname, val)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
"""
|
"""
|
||||||
Regression tests for a few FK bugs: #1578, #6886
|
Regression tests for a few ForeignKey bugs.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
@ -106,6 +106,17 @@ Traceback (most recent call last):
|
||||||
...
|
...
|
||||||
ValueError: Cannot assign "<First: First object>": "Child.parent" must be a "Parent" instance.
|
ValueError: Cannot assign "<First: First object>": "Child.parent" must be a "Parent" instance.
|
||||||
|
|
||||||
|
# Nor can you explicitly assign None to Child.parent during object creation
|
||||||
|
# (regression for #9649).
|
||||||
|
>>> Child(name='xyzzy', parent=None)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValueError: Cannot assign None: "Child.parent" does not allow null values.
|
||||||
|
>>> Child.objects.create(name='xyzzy', parent=None)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
ValueError: Cannot assign None: "Child.parent" does not allow null values.
|
||||||
|
|
||||||
# Creation using keyword argument should cache the related object.
|
# Creation using keyword argument should cache the related object.
|
||||||
>>> p = Parent.objects.get(name="Parent")
|
>>> p = Parent.objects.get(name="Parent")
|
||||||
>>> c = Child(parent=p)
|
>>> c = Child(parent=p)
|
||||||
|
|
Loading…
Reference in New Issue