Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

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.
  • Loading branch information...
commit 028bbd15cad0e48d2b679b986e90c6af0f7e28a0 1 parent 331546f
@andrewgodwin andrewgodwin authored
View
188 django/db/models/fields/__init__.py
@@ -99,7 +99,8 @@ def __init__(self, verbose_name=None, name=None, primary_key=False,
db_tablespace=None, auto_created=False, validators=[],
error_messages=None):
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.max_length, self._unique = max_length, unique
self.blank, self.null = blank, null
@@ -128,14 +129,99 @@ def __init__(self, verbose_name=None, name=None, primary_key=False,
self.creation_counter = Field.creation_counter
Field.creation_counter += 1
+ self._validators = validators # Store for deconstruction later
self.validators = self.default_validators + validators
messages = {}
for c in reversed(self.__class__.__mro__):
messages.update(getattr(c, 'default_error_messages', {}))
messages.update(error_messages or {})
+ self._error_messages = error_messages # Store for deconstruction later
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):
# Needed for @total_ordering
if isinstance(other, Field):
@@ -584,6 +670,7 @@ def __repr__(self):
return '<%s: %s>' % (path, name)
return '<%s>' % path
+
class AutoField(Field):
description = _("Integer")
@@ -598,6 +685,12 @@ def __init__(self, *args, **kwargs):
kwargs['blank'] = True
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):
return "AutoField"
@@ -645,6 +738,11 @@ def __init__(self, *args, **kwargs):
kwargs['blank'] = True
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):
return "BooleanField"
@@ -745,6 +843,18 @@ def __init__(self, verbose_name=None, name=None, auto_now=False,
kwargs['blank'] = True
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):
return "DateField"
@@ -924,6 +1034,14 @@ def __init__(self, verbose_name=None, name=None, max_digits=None,
self.max_digits, self.decimal_places = max_digits, decimal_places
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):
return "DecimalField"
@@ -983,6 +1101,12 @@ def __init__(self, *args, **kwargs):
kwargs['max_length'] = kwargs.get('max_length', 75)
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):
# As with CharField, this will cause email validation to be performed
# twice.
@@ -1002,6 +1126,22 @@ def __init__(self, verbose_name=None, name=None, path='', match=None,
kwargs['max_length'] = kwargs.get('max_length', 100)
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):
defaults = {
'path': self.path,
@@ -1103,6 +1243,11 @@ def __init__(self, *args, **kwargs):
kwargs['max_length'] = 15
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):
return "IPAddressField"
@@ -1119,12 +1264,23 @@ class GenericIPAddressField(Field):
def __init__(self, verbose_name=None, name=None, protocol='both',
unpack_ipv4=False, *args, **kwargs):
self.unpack_ipv4 = unpack_ipv4
+ self.protocol = protocol
self.default_validators, invalid_error_message = \
validators.ip_address_validators(protocol, unpack_ipv4)
self.default_error_messages['invalid'] = invalid_error_message
kwargs['max_length'] = 39
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):
return "GenericIPAddressField"
@@ -1165,6 +1321,12 @@ def __init__(self, *args, **kwargs):
kwargs['blank'] = True
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):
return "NullBooleanField"
@@ -1238,6 +1400,16 @@ def __init__(self, *args, **kwargs):
kwargs['db_index'] = True
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):
return "SlugField"
@@ -1286,6 +1458,14 @@ def __init__(self, verbose_name=None, name=None, auto_now=False,
kwargs['blank'] = True
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):
return "TimeField"
@@ -1345,6 +1525,12 @@ def __init__(self, verbose_name=None, name=None, **kwargs):
CharField.__init__(self, verbose_name, name, **kwargs)
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):
# As with CharField, this will cause URL validation to be performed
# twice.
View
19 django/db/models/fields/files.py
@@ -227,6 +227,17 @@ def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **k
kwargs['max_length'] = kwargs.get('max_length', 100)
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):
return "FileField"
@@ -326,6 +337,14 @@ def __init__(self, verbose_name=None, name=None, width_field=None,
self.width_field, self.height_field = width_field, height_field
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):
super(ImageField, self).contribute_to_class(cls, name)
# Attach update_dimension_fields so that dimension fields declared
View
41 django/db/models/fields/related.py
@@ -1146,6 +1146,27 @@ def __init__(self, to, to_field=None, rel_class=ManyToOneRel,
)
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
def related_field(self):
return self.foreign_related_fields[0]
@@ -1263,6 +1284,12 @@ def __init__(self, to, to_field=None, **kwargs):
kwargs['unique'] = True
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):
setattr(cls, related.get_accessor_name(),
SingleRelatedObjectDescriptor(related))
@@ -1355,6 +1382,20 @@ def __init__(self, to, db_constraint=True, **kwargs):
msg = _('Hold down "Control", or "Command" on a Mac, to select more than one.')
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):
"""
Called by both direct an indirect m2m traversal.
View
0  tests/field_deconstruction/__init__.py
No changes.
View
0  tests/field_deconstruction/models.py
No changes.
View
253 tests/field_deconstruction/tests.py
@@ -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, {})
Please sign in to comment.
Something went wrong with that request. Please try again.