Add a deconstruct() method to Fields.
This allows the field's initial argument to be obtained so it can be serialised to, and re-created from, a textual format.
This commit is contained in:
parent
331546f6ee
commit
028bbd15ca
|
@ -99,7 +99,8 @@ class Field(object):
|
||||||
db_tablespace=None, auto_created=False, validators=[],
|
db_tablespace=None, auto_created=False, validators=[],
|
||||||
error_messages=None):
|
error_messages=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.verbose_name = verbose_name
|
self.verbose_name = verbose_name # May be set by set_attributes_from_name
|
||||||
|
self._verbose_name = verbose_name # Store original for deconstruction
|
||||||
self.primary_key = primary_key
|
self.primary_key = primary_key
|
||||||
self.max_length, self._unique = max_length, unique
|
self.max_length, self._unique = max_length, unique
|
||||||
self.blank, self.null = blank, null
|
self.blank, self.null = blank, null
|
||||||
|
@ -128,14 +129,99 @@ class Field(object):
|
||||||
self.creation_counter = Field.creation_counter
|
self.creation_counter = Field.creation_counter
|
||||||
Field.creation_counter += 1
|
Field.creation_counter += 1
|
||||||
|
|
||||||
|
self._validators = validators # Store for deconstruction later
|
||||||
self.validators = self.default_validators + validators
|
self.validators = self.default_validators + validators
|
||||||
|
|
||||||
messages = {}
|
messages = {}
|
||||||
for c in reversed(self.__class__.__mro__):
|
for c in reversed(self.__class__.__mro__):
|
||||||
messages.update(getattr(c, 'default_error_messages', {}))
|
messages.update(getattr(c, 'default_error_messages', {}))
|
||||||
messages.update(error_messages or {})
|
messages.update(error_messages or {})
|
||||||
|
self._error_messages = error_messages # Store for deconstruction later
|
||||||
self.error_messages = messages
|
self.error_messages = messages
|
||||||
|
|
||||||
|
def deconstruct(self):
|
||||||
|
"""
|
||||||
|
Returns enough information to recreate the field as a 4-tuple:
|
||||||
|
|
||||||
|
* The name of the field on the model, if contribute_to_class has been run
|
||||||
|
* The import path of the field, including the class: django.db.models.IntegerField
|
||||||
|
This should be the most portable version, so less specific may be better.
|
||||||
|
* A list of positional arguments
|
||||||
|
* A dict of keyword arguments
|
||||||
|
|
||||||
|
Note that the positional or keyword arguments must contain values of the
|
||||||
|
following types (including inner values of collection types):
|
||||||
|
|
||||||
|
* None, bool, str, unicode, int, long, float, complex, set, frozenset, list, tuple, dict
|
||||||
|
* UUID
|
||||||
|
* datetime.datetime (naive), datetime.date
|
||||||
|
* top-level classes, top-level functions - will be referenced by their full import path
|
||||||
|
* Storage instances - these have their own deconstruct() method
|
||||||
|
|
||||||
|
This is because the values here must be serialised into a text format
|
||||||
|
(possibly new Python code, possibly JSON) and these are the only types
|
||||||
|
with encoding handlers defined.
|
||||||
|
|
||||||
|
There's no need to return the exact way the field was instantiated this time,
|
||||||
|
just ensure that the resulting field is the same - prefer keyword arguments
|
||||||
|
over positional ones, and omit parameters with their default values.
|
||||||
|
"""
|
||||||
|
# Short-form way of fetching all the default parameters
|
||||||
|
keywords = {}
|
||||||
|
possibles = {
|
||||||
|
"verbose_name": None,
|
||||||
|
"primary_key": False,
|
||||||
|
"max_length": None,
|
||||||
|
"unique": False,
|
||||||
|
"blank": False,
|
||||||
|
"null": False,
|
||||||
|
"db_index": False,
|
||||||
|
"default": NOT_PROVIDED,
|
||||||
|
"editable": True,
|
||||||
|
"serialize": True,
|
||||||
|
"unique_for_date": None,
|
||||||
|
"unique_for_month": None,
|
||||||
|
"unique_for_year": None,
|
||||||
|
"choices": [],
|
||||||
|
"help_text": '',
|
||||||
|
"db_column": None,
|
||||||
|
"db_tablespace": settings.DEFAULT_INDEX_TABLESPACE,
|
||||||
|
"auto_created": False,
|
||||||
|
"validators": [],
|
||||||
|
"error_messages": None,
|
||||||
|
}
|
||||||
|
attr_overrides = {
|
||||||
|
"unique": "_unique",
|
||||||
|
"choices": "_choices",
|
||||||
|
"error_messages": "_error_messages",
|
||||||
|
"validators": "_validators",
|
||||||
|
"verbose_name": "_verbose_name",
|
||||||
|
}
|
||||||
|
equals_comparison = set(["choices", "validators", "db_tablespace"])
|
||||||
|
for name, default in possibles.items():
|
||||||
|
value = getattr(self, attr_overrides.get(name, name))
|
||||||
|
if name in equals_comparison:
|
||||||
|
if value != default:
|
||||||
|
keywords[name] = value
|
||||||
|
else:
|
||||||
|
if value is not default:
|
||||||
|
keywords[name] = value
|
||||||
|
# Work out path - we shorten it for known Django core fields
|
||||||
|
path = "%s.%s" % (self.__class__.__module__, self.__class__.__name__)
|
||||||
|
if path.startswith("django.db.models.fields.related"):
|
||||||
|
path = path.replace("django.db.models.fields.related", "django.db.models")
|
||||||
|
if path.startswith("django.db.models.fields.files"):
|
||||||
|
path = path.replace("django.db.models.fields.files", "django.db.models")
|
||||||
|
if path.startswith("django.db.models.fields"):
|
||||||
|
path = path.replace("django.db.models.fields", "django.db.models")
|
||||||
|
# Return basic info - other fields should override this.
|
||||||
|
return (
|
||||||
|
self.name,
|
||||||
|
path,
|
||||||
|
[],
|
||||||
|
keywords,
|
||||||
|
)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
# Needed for @total_ordering
|
# Needed for @total_ordering
|
||||||
if isinstance(other, Field):
|
if isinstance(other, Field):
|
||||||
|
@ -584,6 +670,7 @@ class Field(object):
|
||||||
return '<%s: %s>' % (path, name)
|
return '<%s: %s>' % (path, name)
|
||||||
return '<%s>' % path
|
return '<%s>' % path
|
||||||
|
|
||||||
|
|
||||||
class AutoField(Field):
|
class AutoField(Field):
|
||||||
description = _("Integer")
|
description = _("Integer")
|
||||||
|
|
||||||
|
@ -598,6 +685,12 @@ class AutoField(Field):
|
||||||
kwargs['blank'] = True
|
kwargs['blank'] = True
|
||||||
Field.__init__(self, *args, **kwargs)
|
Field.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
def deconstruct(self):
|
||||||
|
name, path, args, kwargs = super(AutoField, self).deconstruct()
|
||||||
|
del kwargs['blank']
|
||||||
|
kwargs['primary_key'] = True
|
||||||
|
return name, path, args, kwargs
|
||||||
|
|
||||||
def get_internal_type(self):
|
def get_internal_type(self):
|
||||||
return "AutoField"
|
return "AutoField"
|
||||||
|
|
||||||
|
@ -645,6 +738,11 @@ class BooleanField(Field):
|
||||||
kwargs['blank'] = True
|
kwargs['blank'] = True
|
||||||
Field.__init__(self, *args, **kwargs)
|
Field.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
def deconstruct(self):
|
||||||
|
name, path, args, kwargs = super(BooleanField, self).deconstruct()
|
||||||
|
del kwargs['blank']
|
||||||
|
return name, path, args, kwargs
|
||||||
|
|
||||||
def get_internal_type(self):
|
def get_internal_type(self):
|
||||||
return "BooleanField"
|
return "BooleanField"
|
||||||
|
|
||||||
|
@ -745,6 +843,18 @@ class DateField(Field):
|
||||||
kwargs['blank'] = True
|
kwargs['blank'] = True
|
||||||
Field.__init__(self, verbose_name, name, **kwargs)
|
Field.__init__(self, verbose_name, name, **kwargs)
|
||||||
|
|
||||||
|
def deconstruct(self):
|
||||||
|
name, path, args, kwargs = super(DateField, self).deconstruct()
|
||||||
|
if self.auto_now:
|
||||||
|
kwargs['auto_now'] = True
|
||||||
|
del kwargs['editable']
|
||||||
|
del kwargs['blank']
|
||||||
|
if self.auto_now_add:
|
||||||
|
kwargs['auto_now_add'] = True
|
||||||
|
del kwargs['editable']
|
||||||
|
del kwargs['blank']
|
||||||
|
return name, path, args, kwargs
|
||||||
|
|
||||||
def get_internal_type(self):
|
def get_internal_type(self):
|
||||||
return "DateField"
|
return "DateField"
|
||||||
|
|
||||||
|
@ -924,6 +1034,14 @@ class DecimalField(Field):
|
||||||
self.max_digits, self.decimal_places = max_digits, decimal_places
|
self.max_digits, self.decimal_places = max_digits, decimal_places
|
||||||
Field.__init__(self, verbose_name, name, **kwargs)
|
Field.__init__(self, verbose_name, name, **kwargs)
|
||||||
|
|
||||||
|
def deconstruct(self):
|
||||||
|
name, path, args, kwargs = super(DecimalField, self).deconstruct()
|
||||||
|
if self.max_digits:
|
||||||
|
kwargs['max_digits'] = self.max_digits
|
||||||
|
if self.decimal_places:
|
||||||
|
kwargs['decimal_places'] = self.decimal_places
|
||||||
|
return name, path, args, kwargs
|
||||||
|
|
||||||
def get_internal_type(self):
|
def get_internal_type(self):
|
||||||
return "DecimalField"
|
return "DecimalField"
|
||||||
|
|
||||||
|
@ -983,6 +1101,12 @@ class EmailField(CharField):
|
||||||
kwargs['max_length'] = kwargs.get('max_length', 75)
|
kwargs['max_length'] = kwargs.get('max_length', 75)
|
||||||
CharField.__init__(self, *args, **kwargs)
|
CharField.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
def deconstruct(self):
|
||||||
|
name, path, args, kwargs = super(EmailField, self).deconstruct()
|
||||||
|
if kwargs.get("max_length", None) == 75:
|
||||||
|
del kwargs['max_length']
|
||||||
|
return name, path, args, kwargs
|
||||||
|
|
||||||
def formfield(self, **kwargs):
|
def formfield(self, **kwargs):
|
||||||
# As with CharField, this will cause email validation to be performed
|
# As with CharField, this will cause email validation to be performed
|
||||||
# twice.
|
# twice.
|
||||||
|
@ -1002,6 +1126,22 @@ class FilePathField(Field):
|
||||||
kwargs['max_length'] = kwargs.get('max_length', 100)
|
kwargs['max_length'] = kwargs.get('max_length', 100)
|
||||||
Field.__init__(self, verbose_name, name, **kwargs)
|
Field.__init__(self, verbose_name, name, **kwargs)
|
||||||
|
|
||||||
|
def deconstruct(self):
|
||||||
|
name, path, args, kwargs = super(FilePathField, self).deconstruct()
|
||||||
|
if self.path != '':
|
||||||
|
kwargs['path'] = self.path
|
||||||
|
if self.match is not None:
|
||||||
|
kwargs['match'] = self.match
|
||||||
|
if self.recursive is not False:
|
||||||
|
kwargs['recursive'] = self.recursive
|
||||||
|
if self.allow_files is not True:
|
||||||
|
kwargs['allow_files'] = self.allow_files
|
||||||
|
if self.allow_folders is not False:
|
||||||
|
kwargs['allow_folders'] = self.allow_folders
|
||||||
|
if kwargs.get("max_length", None) == 100:
|
||||||
|
del kwargs["max_length"]
|
||||||
|
return name, path, args, kwargs
|
||||||
|
|
||||||
def formfield(self, **kwargs):
|
def formfield(self, **kwargs):
|
||||||
defaults = {
|
defaults = {
|
||||||
'path': self.path,
|
'path': self.path,
|
||||||
|
@ -1103,6 +1243,11 @@ class IPAddressField(Field):
|
||||||
kwargs['max_length'] = 15
|
kwargs['max_length'] = 15
|
||||||
Field.__init__(self, *args, **kwargs)
|
Field.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
def deconstruct(self):
|
||||||
|
name, path, args, kwargs = super(IPAddressField, self).deconstruct()
|
||||||
|
del kwargs['max_length']
|
||||||
|
return name, path, args, kwargs
|
||||||
|
|
||||||
def get_internal_type(self):
|
def get_internal_type(self):
|
||||||
return "IPAddressField"
|
return "IPAddressField"
|
||||||
|
|
||||||
|
@ -1119,12 +1264,23 @@ class GenericIPAddressField(Field):
|
||||||
def __init__(self, verbose_name=None, name=None, protocol='both',
|
def __init__(self, verbose_name=None, name=None, protocol='both',
|
||||||
unpack_ipv4=False, *args, **kwargs):
|
unpack_ipv4=False, *args, **kwargs):
|
||||||
self.unpack_ipv4 = unpack_ipv4
|
self.unpack_ipv4 = unpack_ipv4
|
||||||
|
self.protocol = protocol
|
||||||
self.default_validators, invalid_error_message = \
|
self.default_validators, invalid_error_message = \
|
||||||
validators.ip_address_validators(protocol, unpack_ipv4)
|
validators.ip_address_validators(protocol, unpack_ipv4)
|
||||||
self.default_error_messages['invalid'] = invalid_error_message
|
self.default_error_messages['invalid'] = invalid_error_message
|
||||||
kwargs['max_length'] = 39
|
kwargs['max_length'] = 39
|
||||||
Field.__init__(self, verbose_name, name, *args, **kwargs)
|
Field.__init__(self, verbose_name, name, *args, **kwargs)
|
||||||
|
|
||||||
|
def deconstruct(self):
|
||||||
|
name, path, args, kwargs = super(GenericIPAddressField, self).deconstruct()
|
||||||
|
if self.unpack_ipv4 is not False:
|
||||||
|
kwargs['unpack_ipv4'] = self.unpack_ipv4
|
||||||
|
if self.protocol != "both":
|
||||||
|
kwargs['protocol'] = self.protocol
|
||||||
|
if kwargs.get("max_length", None) == 39:
|
||||||
|
del kwargs['max_length']
|
||||||
|
return name, path, args, kwargs
|
||||||
|
|
||||||
def get_internal_type(self):
|
def get_internal_type(self):
|
||||||
return "GenericIPAddressField"
|
return "GenericIPAddressField"
|
||||||
|
|
||||||
|
@ -1165,6 +1321,12 @@ class NullBooleanField(Field):
|
||||||
kwargs['blank'] = True
|
kwargs['blank'] = True
|
||||||
Field.__init__(self, *args, **kwargs)
|
Field.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
def deconstruct(self):
|
||||||
|
name, path, args, kwargs = super(NullBooleanField, self).deconstruct()
|
||||||
|
del kwargs['null']
|
||||||
|
del kwargs['blank']
|
||||||
|
return name, path, args, kwargs
|
||||||
|
|
||||||
def get_internal_type(self):
|
def get_internal_type(self):
|
||||||
return "NullBooleanField"
|
return "NullBooleanField"
|
||||||
|
|
||||||
|
@ -1238,6 +1400,16 @@ class SlugField(CharField):
|
||||||
kwargs['db_index'] = True
|
kwargs['db_index'] = True
|
||||||
super(SlugField, self).__init__(*args, **kwargs)
|
super(SlugField, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
def deconstruct(self):
|
||||||
|
name, path, args, kwargs = super(SlugField, self).deconstruct()
|
||||||
|
if kwargs.get("max_length", None) == 50:
|
||||||
|
del kwargs['max_length']
|
||||||
|
if self.db_index is False:
|
||||||
|
kwargs['db_index'] = False
|
||||||
|
else:
|
||||||
|
del kwargs['db_index']
|
||||||
|
return name, path, args, kwargs
|
||||||
|
|
||||||
def get_internal_type(self):
|
def get_internal_type(self):
|
||||||
return "SlugField"
|
return "SlugField"
|
||||||
|
|
||||||
|
@ -1286,6 +1458,14 @@ class TimeField(Field):
|
||||||
kwargs['blank'] = True
|
kwargs['blank'] = True
|
||||||
Field.__init__(self, verbose_name, name, **kwargs)
|
Field.__init__(self, verbose_name, name, **kwargs)
|
||||||
|
|
||||||
|
def deconstruct(self):
|
||||||
|
name, path, args, kwargs = super(TimeField, self).deconstruct()
|
||||||
|
if self.auto_now is not False:
|
||||||
|
kwargs["auto_now"] = self.auto_now
|
||||||
|
if self.auto_now_add is not False:
|
||||||
|
kwargs["auto_now_add"] = self.auto_now_add
|
||||||
|
return name, path, args, kwargs
|
||||||
|
|
||||||
def get_internal_type(self):
|
def get_internal_type(self):
|
||||||
return "TimeField"
|
return "TimeField"
|
||||||
|
|
||||||
|
@ -1345,6 +1525,12 @@ class URLField(CharField):
|
||||||
CharField.__init__(self, verbose_name, name, **kwargs)
|
CharField.__init__(self, verbose_name, name, **kwargs)
|
||||||
self.validators.append(validators.URLValidator())
|
self.validators.append(validators.URLValidator())
|
||||||
|
|
||||||
|
def deconstruct(self):
|
||||||
|
name, path, args, kwargs = super(URLField, self).deconstruct()
|
||||||
|
if kwargs.get("max_length", None) == 200:
|
||||||
|
del kwargs['max_length']
|
||||||
|
return name, path, args, kwargs
|
||||||
|
|
||||||
def formfield(self, **kwargs):
|
def formfield(self, **kwargs):
|
||||||
# As with CharField, this will cause URL validation to be performed
|
# As with CharField, this will cause URL validation to be performed
|
||||||
# twice.
|
# twice.
|
||||||
|
|
|
@ -227,6 +227,17 @@ class FileField(Field):
|
||||||
kwargs['max_length'] = kwargs.get('max_length', 100)
|
kwargs['max_length'] = kwargs.get('max_length', 100)
|
||||||
super(FileField, self).__init__(verbose_name, name, **kwargs)
|
super(FileField, self).__init__(verbose_name, name, **kwargs)
|
||||||
|
|
||||||
|
def deconstruct(self):
|
||||||
|
name, path, args, kwargs = super(FileField, self).deconstruct()
|
||||||
|
if kwargs.get("max_length", None) != 100:
|
||||||
|
kwargs["max_length"] = 100
|
||||||
|
else:
|
||||||
|
del kwargs["max_length"]
|
||||||
|
kwargs['upload_to'] = self.upload_to
|
||||||
|
if self.storage is not default_storage:
|
||||||
|
kwargs['storage'] = self.storage
|
||||||
|
return name, path, args, kwargs
|
||||||
|
|
||||||
def get_internal_type(self):
|
def get_internal_type(self):
|
||||||
return "FileField"
|
return "FileField"
|
||||||
|
|
||||||
|
@ -326,6 +337,14 @@ class ImageField(FileField):
|
||||||
self.width_field, self.height_field = width_field, height_field
|
self.width_field, self.height_field = width_field, height_field
|
||||||
super(ImageField, self).__init__(verbose_name, name, **kwargs)
|
super(ImageField, self).__init__(verbose_name, name, **kwargs)
|
||||||
|
|
||||||
|
def deconstruct(self):
|
||||||
|
name, path, args, kwargs = super(ImageField, self).deconstruct()
|
||||||
|
if self.width_field:
|
||||||
|
kwargs['width_field'] = self.width_field
|
||||||
|
if self.height_field:
|
||||||
|
kwargs['height_field'] = self.height_field
|
||||||
|
return name, path, args, kwargs
|
||||||
|
|
||||||
def contribute_to_class(self, cls, name):
|
def contribute_to_class(self, cls, name):
|
||||||
super(ImageField, self).contribute_to_class(cls, name)
|
super(ImageField, self).contribute_to_class(cls, name)
|
||||||
# Attach update_dimension_fields so that dimension fields declared
|
# Attach update_dimension_fields so that dimension fields declared
|
||||||
|
|
|
@ -1146,6 +1146,27 @@ class ForeignKey(ForeignObject):
|
||||||
)
|
)
|
||||||
super(ForeignKey, self).__init__(to, ['self'], [to_field], **kwargs)
|
super(ForeignKey, self).__init__(to, ['self'], [to_field], **kwargs)
|
||||||
|
|
||||||
|
def deconstruct(self):
|
||||||
|
name, path, args, kwargs = super(ForeignKey, self).deconstruct()
|
||||||
|
# Handle the simpler arguments
|
||||||
|
if self.db_index:
|
||||||
|
del kwargs['db_index']
|
||||||
|
else:
|
||||||
|
kwargs['db_index'] = False
|
||||||
|
if self.db_constraint is not True:
|
||||||
|
kwargs['db_constraint'] = self.db_constraint
|
||||||
|
if self.rel.on_delete is not CASCADE:
|
||||||
|
kwargs['on_delete'] = self.rel.on_delete
|
||||||
|
# Rel needs more work.
|
||||||
|
rel = self.rel
|
||||||
|
if self.rel.field_name:
|
||||||
|
kwargs['to_field'] = self.rel.field_name
|
||||||
|
if isinstance(self.rel.to, basestring):
|
||||||
|
kwargs['to'] = self.rel.to
|
||||||
|
else:
|
||||||
|
kwargs['to'] = "%s.%s" % (self.rel.to._meta.app_label, self.rel.to._meta.object_name)
|
||||||
|
return name, path, args, kwargs
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def related_field(self):
|
def related_field(self):
|
||||||
return self.foreign_related_fields[0]
|
return self.foreign_related_fields[0]
|
||||||
|
@ -1263,6 +1284,12 @@ class OneToOneField(ForeignKey):
|
||||||
kwargs['unique'] = True
|
kwargs['unique'] = True
|
||||||
super(OneToOneField, self).__init__(to, to_field, OneToOneRel, **kwargs)
|
super(OneToOneField, self).__init__(to, to_field, OneToOneRel, **kwargs)
|
||||||
|
|
||||||
|
def deconstruct(self):
|
||||||
|
name, path, args, kwargs = super(OneToOneField, self).deconstruct()
|
||||||
|
if "unique" in kwargs:
|
||||||
|
del kwargs['unique']
|
||||||
|
return name, path, args, kwargs
|
||||||
|
|
||||||
def contribute_to_related_class(self, cls, related):
|
def contribute_to_related_class(self, cls, related):
|
||||||
setattr(cls, related.get_accessor_name(),
|
setattr(cls, related.get_accessor_name(),
|
||||||
SingleRelatedObjectDescriptor(related))
|
SingleRelatedObjectDescriptor(related))
|
||||||
|
@ -1355,6 +1382,20 @@ class ManyToManyField(RelatedField):
|
||||||
msg = _('Hold down "Control", or "Command" on a Mac, to select more than one.')
|
msg = _('Hold down "Control", or "Command" on a Mac, to select more than one.')
|
||||||
self.help_text = string_concat(self.help_text, ' ', msg)
|
self.help_text = string_concat(self.help_text, ' ', msg)
|
||||||
|
|
||||||
|
def deconstruct(self):
|
||||||
|
name, path, args, kwargs = super(ManyToManyField, self).deconstruct()
|
||||||
|
# Handle the simpler arguments
|
||||||
|
if self.rel.db_constraint is not True:
|
||||||
|
kwargs['db_constraint'] = self.db_constraint
|
||||||
|
del kwargs['help_text']
|
||||||
|
# Rel needs more work.
|
||||||
|
rel = self.rel
|
||||||
|
if isinstance(self.rel.to, basestring):
|
||||||
|
kwargs['to'] = self.rel.to
|
||||||
|
else:
|
||||||
|
kwargs['to'] = "%s.%s" % (self.rel.to._meta.app_label, self.rel.to._meta.object_name)
|
||||||
|
return name, path, args, kwargs
|
||||||
|
|
||||||
def _get_path_info(self, direct=False):
|
def _get_path_info(self, direct=False):
|
||||||
"""
|
"""
|
||||||
Called by both direct an indirect m2m traversal.
|
Called by both direct an indirect m2m traversal.
|
||||||
|
|
|
@ -0,0 +1,253 @@
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class FieldDeconstructionTests(TestCase):
|
||||||
|
"""
|
||||||
|
Tests the deconstruct() method on all core fields.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def test_name(self):
|
||||||
|
"""
|
||||||
|
Tests the outputting of the correct name if assigned one.
|
||||||
|
"""
|
||||||
|
# First try using a "normal" field
|
||||||
|
field = models.CharField(max_length=65)
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(name, None)
|
||||||
|
field.set_attributes_from_name("is_awesome_test")
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(name, "is_awesome_test")
|
||||||
|
# Now try with a ForeignKey
|
||||||
|
field = models.ForeignKey("some_fake.ModelName")
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(name, None)
|
||||||
|
field.set_attributes_from_name("author")
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(name, "author")
|
||||||
|
|
||||||
|
def test_auto_field(self):
|
||||||
|
field = models.AutoField(primary_key=True)
|
||||||
|
field.set_attributes_from_name("id")
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.AutoField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {"primary_key": True})
|
||||||
|
|
||||||
|
def test_big_integer_field(self):
|
||||||
|
field = models.BigIntegerField()
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.BigIntegerField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {})
|
||||||
|
|
||||||
|
def test_boolean_field(self):
|
||||||
|
field = models.BooleanField()
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.BooleanField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {})
|
||||||
|
field = models.BooleanField(default=True)
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.BooleanField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {"default": True})
|
||||||
|
|
||||||
|
def test_char_field(self):
|
||||||
|
field = models.CharField(max_length=65)
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.CharField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {"max_length": 65})
|
||||||
|
field = models.CharField(max_length=65, null=True, blank=True)
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.CharField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {"max_length": 65, "null": True, "blank": True})
|
||||||
|
|
||||||
|
def test_csi_field(self):
|
||||||
|
field = models.CommaSeparatedIntegerField(max_length=100)
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.CommaSeparatedIntegerField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {"max_length": 100})
|
||||||
|
|
||||||
|
def test_date_field(self):
|
||||||
|
field = models.DateField()
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.DateField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {})
|
||||||
|
field = models.DateField(auto_now=True)
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.DateField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {"auto_now": True})
|
||||||
|
|
||||||
|
def test_datetime_field(self):
|
||||||
|
field = models.DateTimeField()
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.DateTimeField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {})
|
||||||
|
field = models.DateTimeField(auto_now_add=True)
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.DateTimeField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {"auto_now_add": True})
|
||||||
|
|
||||||
|
def test_decimal_field(self):
|
||||||
|
field = models.DecimalField(max_digits=5, decimal_places=2)
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.DecimalField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {"max_digits": 5, "decimal_places": 2})
|
||||||
|
|
||||||
|
def test_email_field(self):
|
||||||
|
field = models.EmailField()
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.EmailField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {})
|
||||||
|
field = models.EmailField(max_length=255)
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.EmailField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {"max_length": 255})
|
||||||
|
|
||||||
|
def test_file_field(self):
|
||||||
|
field = models.FileField(upload_to="foo/bar")
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.FileField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {"upload_to": "foo/bar"})
|
||||||
|
|
||||||
|
def test_file_path_field(self):
|
||||||
|
field = models.FilePathField(match=".*\.txt$")
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.FilePathField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {"match": ".*\.txt$"})
|
||||||
|
field = models.FilePathField(recursive=True, allow_folders=True)
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.FilePathField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {"recursive": True, "allow_folders": True})
|
||||||
|
|
||||||
|
def test_float_field(self):
|
||||||
|
field = models.FloatField()
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.FloatField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {})
|
||||||
|
|
||||||
|
def test_foreign_key(self):
|
||||||
|
field = models.ForeignKey("auth.User")
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.ForeignKey")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {"to": "auth.User"})
|
||||||
|
field = models.ForeignKey("something.Else")
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.ForeignKey")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {"to": "something.Else"})
|
||||||
|
field = models.ForeignKey("auth.User", on_delete=models.SET_NULL)
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.ForeignKey")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {"to": "auth.User", "on_delete": models.SET_NULL})
|
||||||
|
|
||||||
|
def test_image_field(self):
|
||||||
|
field = models.ImageField(upload_to="foo/barness", width_field="width", height_field="height")
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.ImageField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {"upload_to": "foo/barness", "width_field": "width", "height_field": "height"})
|
||||||
|
|
||||||
|
def test_integer_field(self):
|
||||||
|
field = models.IntegerField()
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.IntegerField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {})
|
||||||
|
|
||||||
|
def test_ip_address_field(self):
|
||||||
|
field = models.IPAddressField()
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.IPAddressField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {})
|
||||||
|
|
||||||
|
def test_generic_ip_address_field(self):
|
||||||
|
field = models.GenericIPAddressField()
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.GenericIPAddressField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {})
|
||||||
|
field = models.GenericIPAddressField(protocol="IPv6")
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.GenericIPAddressField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {"protocol": "IPv6"})
|
||||||
|
|
||||||
|
def test_many_to_many_field(self):
|
||||||
|
field = models.ManyToManyField("auth.User")
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.ManyToManyField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {"to": "auth.User"})
|
||||||
|
|
||||||
|
def test_null_boolean_field(self):
|
||||||
|
field = models.NullBooleanField()
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.NullBooleanField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {})
|
||||||
|
|
||||||
|
def test_positive_integer_field(self):
|
||||||
|
field = models.PositiveIntegerField()
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.PositiveIntegerField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {})
|
||||||
|
|
||||||
|
def test_positive_small_integer_field(self):
|
||||||
|
field = models.PositiveSmallIntegerField()
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.PositiveSmallIntegerField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {})
|
||||||
|
|
||||||
|
def test_slug_field(self):
|
||||||
|
field = models.SlugField()
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.SlugField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {})
|
||||||
|
field = models.SlugField(db_index=False)
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.SlugField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {"db_index": False})
|
||||||
|
|
||||||
|
def test_small_integer_field(self):
|
||||||
|
field = models.SmallIntegerField()
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.SmallIntegerField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {})
|
||||||
|
|
||||||
|
def test_text_field(self):
|
||||||
|
field = models.TextField()
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.TextField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {})
|
||||||
|
|
||||||
|
def test_url_field(self):
|
||||||
|
field = models.URLField()
|
||||||
|
name, path, args, kwargs = field.deconstruct()
|
||||||
|
self.assertEqual(path, "django.db.models.URLField")
|
||||||
|
self.assertEqual(args, [])
|
||||||
|
self.assertEqual(kwargs, {})
|
Loading…
Reference in New Issue