mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Added a db_type() method to the database Field class. This is a hook for calculating the database column type for a given Field. Also converted all management.py CREATE TABLE statements to use db_type(), which made that code cleaner. The Field.get_internal_type() hook still exists, but we should consider removing it at some point, because db_type() is more general. Also added docs -- the beginnings of docs on how to create custom database Field classes. This is backwards-compatible.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5725 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
		| @@ -95,19 +95,12 @@ def _get_sequence_list(): | |||||||
|  |  | ||||||
|     return sequence_list |     return sequence_list | ||||||
|  |  | ||||||
| # If the foreign key points to an AutoField, a PositiveIntegerField or a |  | ||||||
| # PositiveSmallIntegerField, the foreign key should be an IntegerField, not the |  | ||||||
| # referred field type. Otherwise, the foreign key should be the same type of |  | ||||||
| # field as the field to which it points. |  | ||||||
| get_rel_data_type = lambda f: (f.get_internal_type() in ('AutoField', 'PositiveIntegerField', 'PositiveSmallIntegerField')) and 'IntegerField' or f.get_internal_type() |  | ||||||
|  |  | ||||||
| def get_sql_create(app): | def get_sql_create(app): | ||||||
|     "Returns a list of the CREATE TABLE SQL statements for the given app." |     "Returns a list of the CREATE TABLE SQL statements for the given app." | ||||||
|     from django.db import get_creation_module, models |     from django.db import models | ||||||
|  |     from django.conf import settings | ||||||
|  |  | ||||||
|     data_types = get_creation_module().DATA_TYPES |     if settings.DATABASE_ENGINE == 'dummy': | ||||||
|  |  | ||||||
|     if not data_types: |  | ||||||
|         # This must be the "dummy" database backend, which means the user |         # This must be the "dummy" database backend, which means the user | ||||||
|         # hasn't set DATABASE_ENGINE. |         # hasn't set DATABASE_ENGINE. | ||||||
|         sys.stderr.write(style.ERROR("Error: Django doesn't know which syntax to use for your SQL statements,\n" + |         sys.stderr.write(style.ERROR("Error: Django doesn't know which syntax to use for your SQL statements,\n" + | ||||||
| @@ -159,28 +152,19 @@ def _get_sql_model_create(model, known_models=set()): | |||||||
|  |  | ||||||
|     Returns list_of_sql, pending_references_dict |     Returns list_of_sql, pending_references_dict | ||||||
|     """ |     """ | ||||||
|     from django.db import backend, get_creation_module, models |     from django.db import backend, models | ||||||
|     data_types = get_creation_module().DATA_TYPES |  | ||||||
|  |  | ||||||
|     opts = model._meta |     opts = model._meta | ||||||
|     final_output = [] |     final_output = [] | ||||||
|     table_output = [] |     table_output = [] | ||||||
|     pending_references = {} |     pending_references = {} | ||||||
|     for f in opts.fields: |     for f in opts.fields: | ||||||
|         if isinstance(f, (models.ForeignKey, models.OneToOneField)): |         col_type = f.db_type() | ||||||
|             rel_field = f.rel.get_related_field() |  | ||||||
|             while isinstance(rel_field, (models.ForeignKey, models.OneToOneField)): |  | ||||||
|                 rel_field = rel_field.rel.get_related_field() |  | ||||||
|             data_type = get_rel_data_type(rel_field) |  | ||||||
|         else: |  | ||||||
|             rel_field = f |  | ||||||
|             data_type = f.get_internal_type() |  | ||||||
|         col_type = data_types[data_type] |  | ||||||
|         tablespace = f.db_tablespace or opts.db_tablespace |         tablespace = f.db_tablespace or opts.db_tablespace | ||||||
|         if col_type is not None: |         if col_type is not None: | ||||||
|             # Make the definition (e.g. 'foo VARCHAR(30)') for this field. |             # Make the definition (e.g. 'foo VARCHAR(30)') for this field. | ||||||
|             field_output = [style.SQL_FIELD(backend.quote_name(f.column)), |             field_output = [style.SQL_FIELD(backend.quote_name(f.column)), | ||||||
|                 style.SQL_COLTYPE(col_type % rel_field.__dict__)] |                 style.SQL_COLTYPE(col_type)] | ||||||
|             field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or ''))) |             field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or ''))) | ||||||
|             if f.unique and (not f.primary_key or backend.allows_unique_and_pk): |             if f.unique and (not f.primary_key or backend.allows_unique_and_pk): | ||||||
|                 field_output.append(style.SQL_KEYWORD('UNIQUE')) |                 field_output.append(style.SQL_KEYWORD('UNIQUE')) | ||||||
| @@ -204,7 +188,7 @@ def _get_sql_model_create(model, known_models=set()): | |||||||
|             table_output.append(' '.join(field_output)) |             table_output.append(' '.join(field_output)) | ||||||
|     if opts.order_with_respect_to: |     if opts.order_with_respect_to: | ||||||
|         table_output.append(style.SQL_FIELD(backend.quote_name('_order')) + ' ' + \ |         table_output.append(style.SQL_FIELD(backend.quote_name('_order')) + ' ' + \ | ||||||
|             style.SQL_COLTYPE(data_types['IntegerField']) + ' ' + \ |             style.SQL_COLTYPE(models.IntegerField().db_type()) + ' ' + \ | ||||||
|             style.SQL_KEYWORD('NULL')) |             style.SQL_KEYWORD('NULL')) | ||||||
|     for field_constraints in opts.unique_together: |     for field_constraints in opts.unique_together: | ||||||
|         table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \ |         table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \ | ||||||
| @@ -232,9 +216,8 @@ def _get_sql_for_pending_references(model, pending_references): | |||||||
|     """ |     """ | ||||||
|     Get any ALTER TABLE statements to add constraints after the fact. |     Get any ALTER TABLE statements to add constraints after the fact. | ||||||
|     """ |     """ | ||||||
|     from django.db import backend, get_creation_module |     from django.db import backend | ||||||
|     from django.db.backends.util import truncate_name |     from django.db.backends.util import truncate_name | ||||||
|     data_types = get_creation_module().DATA_TYPES |  | ||||||
|  |  | ||||||
|     final_output = [] |     final_output = [] | ||||||
|     if backend.supports_constraints: |     if backend.supports_constraints: | ||||||
| @@ -257,11 +240,9 @@ def _get_sql_for_pending_references(model, pending_references): | |||||||
|     return final_output |     return final_output | ||||||
|  |  | ||||||
| def _get_many_to_many_sql_for_model(model): | def _get_many_to_many_sql_for_model(model): | ||||||
|     from django.db import backend, get_creation_module |     from django.db import backend, models | ||||||
|     from django.contrib.contenttypes import generic |     from django.contrib.contenttypes import generic | ||||||
|  |  | ||||||
|     data_types = get_creation_module().DATA_TYPES |  | ||||||
|  |  | ||||||
|     opts = model._meta |     opts = model._meta | ||||||
|     final_output = [] |     final_output = [] | ||||||
|     for f in opts.many_to_many: |     for f in opts.many_to_many: | ||||||
| @@ -275,19 +256,19 @@ def _get_many_to_many_sql_for_model(model): | |||||||
|                 style.SQL_TABLE(backend.quote_name(f.m2m_db_table())) + ' ('] |                 style.SQL_TABLE(backend.quote_name(f.m2m_db_table())) + ' ('] | ||||||
|             table_output.append('    %s %s %s%s,' % \ |             table_output.append('    %s %s %s%s,' % \ | ||||||
|                 (style.SQL_FIELD(backend.quote_name('id')), |                 (style.SQL_FIELD(backend.quote_name('id')), | ||||||
|                 style.SQL_COLTYPE(data_types['AutoField']), |                 style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type()), | ||||||
|                 style.SQL_KEYWORD('NOT NULL PRIMARY KEY'), |                 style.SQL_KEYWORD('NOT NULL PRIMARY KEY'), | ||||||
|                 tablespace_sql)) |                 tablespace_sql)) | ||||||
|             table_output.append('    %s %s %s %s (%s)%s,' % \ |             table_output.append('    %s %s %s %s (%s)%s,' % \ | ||||||
|                 (style.SQL_FIELD(backend.quote_name(f.m2m_column_name())), |                 (style.SQL_FIELD(backend.quote_name(f.m2m_column_name())), | ||||||
|                 style.SQL_COLTYPE(data_types[get_rel_data_type(opts.pk)] % opts.pk.__dict__), |                 style.SQL_COLTYPE(models.ForeignKey(model).db_type()), | ||||||
|                 style.SQL_KEYWORD('NOT NULL REFERENCES'), |                 style.SQL_KEYWORD('NOT NULL REFERENCES'), | ||||||
|                 style.SQL_TABLE(backend.quote_name(opts.db_table)), |                 style.SQL_TABLE(backend.quote_name(opts.db_table)), | ||||||
|                 style.SQL_FIELD(backend.quote_name(opts.pk.column)), |                 style.SQL_FIELD(backend.quote_name(opts.pk.column)), | ||||||
|                 backend.get_deferrable_sql())) |                 backend.get_deferrable_sql())) | ||||||
|             table_output.append('    %s %s %s %s (%s)%s,' % \ |             table_output.append('    %s %s %s %s (%s)%s,' % \ | ||||||
|                 (style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name())), |                 (style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name())), | ||||||
|                 style.SQL_COLTYPE(data_types[get_rel_data_type(f.rel.to._meta.pk)] % f.rel.to._meta.pk.__dict__), |                 style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type()), | ||||||
|                 style.SQL_KEYWORD('NOT NULL REFERENCES'), |                 style.SQL_KEYWORD('NOT NULL REFERENCES'), | ||||||
|                 style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)), |                 style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)), | ||||||
|                 style.SQL_FIELD(backend.quote_name(f.rel.to._meta.pk.column)), |                 style.SQL_FIELD(backend.quote_name(f.rel.to._meta.pk.column)), | ||||||
| @@ -517,7 +498,7 @@ def _emit_post_sync_signal(created_models, verbosity, interactive): | |||||||
|  |  | ||||||
| def syncdb(verbosity=1, interactive=True): | def syncdb(verbosity=1, interactive=True): | ||||||
|     "Creates the database tables for all apps in INSTALLED_APPS whose tables haven't already been created." |     "Creates the database tables for all apps in INSTALLED_APPS whose tables haven't already been created." | ||||||
|     from django.db import backend, connection, transaction, models, get_creation_module |     from django.db import backend, connection, transaction, models | ||||||
|     from django.conf import settings |     from django.conf import settings | ||||||
|  |  | ||||||
|     disable_termcolors() |     disable_termcolors() | ||||||
| @@ -533,8 +514,6 @@ def syncdb(verbosity=1, interactive=True): | |||||||
|         except ImportError: |         except ImportError: | ||||||
|             pass |             pass | ||||||
|  |  | ||||||
|     data_types = get_creation_module().DATA_TYPES |  | ||||||
|  |  | ||||||
|     cursor = connection.cursor() |     cursor = connection.cursor() | ||||||
|  |  | ||||||
|     # Get a list of all existing database tables, |     # Get a list of all existing database tables, | ||||||
| @@ -1266,8 +1245,7 @@ runserver.args = '[--noreload] [--adminmedia=ADMIN_MEDIA_PATH] [optional port nu | |||||||
|  |  | ||||||
| def createcachetable(tablename): | def createcachetable(tablename): | ||||||
|     "Creates the table needed to use the SQL cache backend" |     "Creates the table needed to use the SQL cache backend" | ||||||
|     from django.db import backend, connection, transaction, get_creation_module, models |     from django.db import backend, connection, transaction, models | ||||||
|     data_types = get_creation_module().DATA_TYPES |  | ||||||
|     fields = ( |     fields = ( | ||||||
|         # "key" is a reserved word in MySQL, so use "cache_key" instead. |         # "key" is a reserved word in MySQL, so use "cache_key" instead. | ||||||
|         models.CharField(name='cache_key', maxlength=255, unique=True, primary_key=True), |         models.CharField(name='cache_key', maxlength=255, unique=True, primary_key=True), | ||||||
| @@ -1277,7 +1255,7 @@ def createcachetable(tablename): | |||||||
|     table_output = [] |     table_output = [] | ||||||
|     index_output = [] |     index_output = [] | ||||||
|     for f in fields: |     for f in fields: | ||||||
|         field_output = [backend.quote_name(f.name), data_types[f.get_internal_type()] % f.__dict__] |         field_output = [backend.quote_name(f.name), f.db_type()] | ||||||
|         field_output.append("%sNULL" % (not f.null and "NOT " or "")) |         field_output.append("%sNULL" % (not f.null and "NOT " or "")) | ||||||
|         if f.unique: |         if f.unique: | ||||||
|             field_output.append("UNIQUE") |             field_output.append("UNIQUE") | ||||||
|   | |||||||
| @@ -12,7 +12,6 @@ DATA_TYPES = { | |||||||
|     'ImageField':        'varchar(100)', |     'ImageField':        'varchar(100)', | ||||||
|     'IntegerField':      'int', |     'IntegerField':      'int', | ||||||
|     'IPAddressField':    'char(15)', |     'IPAddressField':    'char(15)', | ||||||
|     'ManyToManyField':   None, |  | ||||||
|     'NullBooleanField':  'bit', |     'NullBooleanField':  'bit', | ||||||
|     'OneToOneField':     'int', |     'OneToOneField':     'int', | ||||||
|     'PhoneNumberField':  'varchar(20)', |     'PhoneNumberField':  'varchar(20)', | ||||||
|   | |||||||
| @@ -16,7 +16,6 @@ DATA_TYPES = { | |||||||
|     'ImageField':        'varchar(100)', |     'ImageField':        'varchar(100)', | ||||||
|     'IntegerField':      'integer', |     'IntegerField':      'integer', | ||||||
|     'IPAddressField':    'char(15)', |     'IPAddressField':    'char(15)', | ||||||
|     'ManyToManyField':   None, |  | ||||||
|     'NullBooleanField':  'bool', |     'NullBooleanField':  'bool', | ||||||
|     'OneToOneField':     'integer', |     'OneToOneField':     'integer', | ||||||
|     'PhoneNumberField':  'varchar(20)', |     'PhoneNumberField':  'varchar(20)', | ||||||
|   | |||||||
| @@ -16,7 +16,6 @@ DATA_TYPES = { | |||||||
|     'ImageField':        'varchar(100)', |     'ImageField':        'varchar(100)', | ||||||
|     'IntegerField':      'integer', |     'IntegerField':      'integer', | ||||||
|     'IPAddressField':    'char(15)', |     'IPAddressField':    'char(15)', | ||||||
|     'ManyToManyField':   None, |  | ||||||
|     'NullBooleanField':  'bool', |     'NullBooleanField':  'bool', | ||||||
|     'OneToOneField':     'integer', |     'OneToOneField':     'integer', | ||||||
|     'PhoneNumberField':  'varchar(20)', |     'PhoneNumberField':  'varchar(20)', | ||||||
|   | |||||||
| @@ -19,7 +19,6 @@ DATA_TYPES = { | |||||||
|     'ImageField':                   'NVARCHAR2(100)', |     'ImageField':                   'NVARCHAR2(100)', | ||||||
|     'IntegerField':                 'NUMBER(11)', |     'IntegerField':                 'NUMBER(11)', | ||||||
|     'IPAddressField':               'VARCHAR2(15)', |     'IPAddressField':               'VARCHAR2(15)', | ||||||
|     'ManyToManyField':              None, |  | ||||||
|     'NullBooleanField':             'NUMBER(1) CHECK ((%(column)s IN (0,1)) OR (%(column)s IS NULL))', |     'NullBooleanField':             'NUMBER(1) CHECK ((%(column)s IN (0,1)) OR (%(column)s IS NULL))', | ||||||
|     'OneToOneField':                'NUMBER(11)', |     'OneToOneField':                'NUMBER(11)', | ||||||
|     'PhoneNumberField':             'VARCHAR2(20)', |     'PhoneNumberField':             'VARCHAR2(20)', | ||||||
|   | |||||||
| @@ -16,7 +16,6 @@ DATA_TYPES = { | |||||||
|     'ImageField':        'varchar(100)', |     'ImageField':        'varchar(100)', | ||||||
|     'IntegerField':      'integer', |     'IntegerField':      'integer', | ||||||
|     'IPAddressField':    'inet', |     'IPAddressField':    'inet', | ||||||
|     'ManyToManyField':   None, |  | ||||||
|     'NullBooleanField':  'boolean', |     'NullBooleanField':  'boolean', | ||||||
|     'OneToOneField':     'integer', |     'OneToOneField':     'integer', | ||||||
|     'PhoneNumberField':  'varchar(20)', |     'PhoneNumberField':  'varchar(20)', | ||||||
|   | |||||||
| @@ -15,7 +15,6 @@ DATA_TYPES = { | |||||||
|     'ImageField':                   'varchar(100)', |     'ImageField':                   'varchar(100)', | ||||||
|     'IntegerField':                 'integer', |     'IntegerField':                 'integer', | ||||||
|     'IPAddressField':               'char(15)', |     'IPAddressField':               'char(15)', | ||||||
|     'ManyToManyField':              None, |  | ||||||
|     'NullBooleanField':             'bool', |     'NullBooleanField':             'bool', | ||||||
|     'OneToOneField':                'integer', |     'OneToOneField':                'integer', | ||||||
|     'PhoneNumberField':             'varchar(20)', |     'PhoneNumberField':             'varchar(20)', | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
|  | from django.db import get_creation_module | ||||||
| from django.db.models import signals | from django.db.models import signals | ||||||
| from django.dispatch import dispatcher | from django.dispatch import dispatcher | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| @@ -117,6 +118,30 @@ class Field(object): | |||||||
|         """ |         """ | ||||||
|         return value |         return value | ||||||
|  |  | ||||||
|  |     def db_type(self): | ||||||
|  |         """ | ||||||
|  |         Returns the database column data type for this field, taking into | ||||||
|  |         account the DATABASE_ENGINE setting. | ||||||
|  |         """ | ||||||
|  |         # The default implementation of this method looks at the | ||||||
|  |         # backend-specific DATA_TYPES dictionary, looking up the field by its | ||||||
|  |         # "internal type". | ||||||
|  |         # | ||||||
|  |         # A Field class can implement the get_internal_type() method to specify | ||||||
|  |         # which *preexisting* Django Field class it's most similar to -- i.e., | ||||||
|  |         # an XMLField is represented by a TEXT column type, which is the same | ||||||
|  |         # as the TextField Django field type, which means XMLField's | ||||||
|  |         # get_internal_type() returns 'TextField'. | ||||||
|  |         # | ||||||
|  |         # But the limitation of the get_internal_type() / DATA_TYPES approach | ||||||
|  |         # is that it cannot handle database column types that aren't already | ||||||
|  |         # mapped to one of the built-in Django field types. In this case, you | ||||||
|  |         # can implement db_type() instead of get_internal_type() to specify | ||||||
|  |         # exactly which wacky database column type you want to use. | ||||||
|  |         data_types = get_creation_module().DATA_TYPES | ||||||
|  |         internal_type = self.get_internal_type() | ||||||
|  |         return data_types[internal_type] % self.__dict__ | ||||||
|  |  | ||||||
|     def validate_full(self, field_data, all_data): |     def validate_full(self, field_data, all_data): | ||||||
|         """ |         """ | ||||||
|         Returns a list of errors for this field. This is the main interface, |         Returns a list of errors for this field. This is the main interface, | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| from django.db import backend, transaction | from django.db import backend, transaction | ||||||
| from django.db.models import signals, get_model | from django.db.models import signals, get_model | ||||||
| from django.db.models.fields import AutoField, Field, IntegerField, get_ul_class | from django.db.models.fields import AutoField, Field, IntegerField, PositiveIntegerField, PositiveSmallIntegerField, get_ul_class | ||||||
| from django.db.models.related import RelatedObject | from django.db.models.related import RelatedObject | ||||||
| from django.utils.text import capfirst | from django.utils.text import capfirst | ||||||
| from django.utils.translation import ugettext_lazy, string_concat, ungettext, ugettext as _ | from django.utils.translation import ugettext_lazy, string_concat, ungettext, ugettext as _ | ||||||
| @@ -556,6 +556,16 @@ class ForeignKey(RelatedField, Field): | |||||||
|         defaults.update(kwargs) |         defaults.update(kwargs) | ||||||
|         return super(ForeignKey, self).formfield(**defaults) |         return super(ForeignKey, self).formfield(**defaults) | ||||||
|  |  | ||||||
|  |     def db_type(self): | ||||||
|  |         # The database column type of a ForeignKey is the column type | ||||||
|  |         # of the field to which it points. An exception is if the ForeignKey | ||||||
|  |         # points to an AutoField/PositiveIntegerField/PositiveSmallIntegerField, | ||||||
|  |         # in which case the column type is simply that of an IntegerField. | ||||||
|  |         rel_field = self.rel.get_related_field() | ||||||
|  |         if isinstance(rel_field, (AutoField, PositiveIntegerField, PositiveSmallIntegerField)): | ||||||
|  |             return IntegerField().db_type() | ||||||
|  |         return rel_field.db_type() | ||||||
|  |  | ||||||
| class OneToOneField(RelatedField, IntegerField): | class OneToOneField(RelatedField, IntegerField): | ||||||
|     def __init__(self, to, to_field=None, **kwargs): |     def __init__(self, to, to_field=None, **kwargs): | ||||||
|         try: |         try: | ||||||
| @@ -622,6 +632,16 @@ class OneToOneField(RelatedField, IntegerField): | |||||||
|         defaults.update(kwargs) |         defaults.update(kwargs) | ||||||
|         return super(OneToOneField, self).formfield(**defaults) |         return super(OneToOneField, self).formfield(**defaults) | ||||||
|  |  | ||||||
|  |     def db_type(self): | ||||||
|  |         # The database column type of a OneToOneField is the column type | ||||||
|  |         # of the field to which it points. An exception is if the OneToOneField | ||||||
|  |         # points to an AutoField/PositiveIntegerField/PositiveSmallIntegerField, | ||||||
|  |         # in which case the column type is simply that of an IntegerField. | ||||||
|  |         rel_field = self.rel.get_related_field() | ||||||
|  |         if isinstance(rel_field, (AutoField, PositiveIntegerField, PositiveSmallIntegerField)): | ||||||
|  |             return IntegerField().db_type() | ||||||
|  |         return rel_field.db_type() | ||||||
|  |  | ||||||
| class ManyToManyField(RelatedField, Field): | class ManyToManyField(RelatedField, Field): | ||||||
|     def __init__(self, to, **kwargs): |     def __init__(self, to, **kwargs): | ||||||
|         kwargs['verbose_name'] = kwargs.get('verbose_name', None) |         kwargs['verbose_name'] = kwargs.get('verbose_name', None) | ||||||
| @@ -745,6 +765,11 @@ class ManyToManyField(RelatedField, Field): | |||||||
|             defaults['initial'] = [i._get_pk_val() for i in defaults['initial']] |             defaults['initial'] = [i._get_pk_val() for i in defaults['initial']] | ||||||
|         return super(ManyToManyField, self).formfield(**defaults) |         return super(ManyToManyField, self).formfield(**defaults) | ||||||
|  |  | ||||||
|  |     def db_type(self): | ||||||
|  |         # A ManyToManyField is not represented by a single column, | ||||||
|  |         # so return None. | ||||||
|  |         return None | ||||||
|  |  | ||||||
| class ManyToOneRel(object): | class ManyToOneRel(object): | ||||||
|     def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None, |     def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None, | ||||||
|         max_num_in_admin=None, num_extra_on_change=1, edit_inline=False, |         max_num_in_admin=None, num_extra_on_change=1, edit_inline=False, | ||||||
|   | |||||||
| @@ -981,6 +981,77 @@ See the `One-to-one relationship model example`_ for a full example. | |||||||
|  |  | ||||||
| .. _One-to-one relationship model example: http://www.djangoproject.com/documentation/models/one_to_one/ | .. _One-to-one relationship model example: http://www.djangoproject.com/documentation/models/one_to_one/ | ||||||
|  |  | ||||||
|  | Custom field types | ||||||
|  | ------------------ | ||||||
|  |  | ||||||
|  | **New in Django development version** | ||||||
|  |  | ||||||
|  | Django's built-in field types don't cover every possible database column type -- | ||||||
|  | only the common types, such as ``VARCHAR`` and ``INTEGER``. For more obscure | ||||||
|  | column types, such as geographic polygons or even user-created types such as | ||||||
|  | `PostgreSQL custom types`_, you can define your own Django ``Field`` subclasses. | ||||||
|  |  | ||||||
|  | .. _PostgreSQL custom types: http://www.postgresql.org/docs/8.2/interactive/sql-createtype.html | ||||||
|  |  | ||||||
|  | .. admonition:: Experimental territory | ||||||
|  |  | ||||||
|  |     This is an area of Django that traditionally has not been documented, but | ||||||
|  |     we're starting to include bits of documentation, one feature at a time. | ||||||
|  |     Please forgive the sparseness of this section. | ||||||
|  |  | ||||||
|  |     If you like living on the edge and are comfortable with the risk of | ||||||
|  |     unstable, undocumented APIs, see the code for the core ``Field`` class | ||||||
|  |     in ``django/db/models/fields/__init__.py`` -- but if/when the innards | ||||||
|  |     change, don't say we didn't warn you. | ||||||
|  |  | ||||||
|  | To create a custom field type, simply subclass ``django.db.models.Field``. | ||||||
|  | Here is an incomplete list of the methods you should implement: | ||||||
|  |  | ||||||
|  | ``db_type()`` | ||||||
|  | ~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  | Returns the database column data type for the ``Field``, taking into account | ||||||
|  | the current ``DATABASE_ENGINE`` setting. | ||||||
|  |  | ||||||
|  | Say you've created a PostgreSQL custom type called ``mytype``. You can use this | ||||||
|  | field with Django by subclassing ``Field`` and implementing the ``db_type()`` | ||||||
|  | method, like so:: | ||||||
|  |  | ||||||
|  |     from django.db import models | ||||||
|  |  | ||||||
|  |     class MytypeField(models.Field): | ||||||
|  |         def db_type(self): | ||||||
|  |             return 'mytype' | ||||||
|  |  | ||||||
|  | Once you have ``MytypeField``, you can use it in any model, just like any other | ||||||
|  | ``Field`` type:: | ||||||
|  |  | ||||||
|  |     class Person(models.Model): | ||||||
|  |         name = models.CharField(maxlength=80) | ||||||
|  |         gender = models.CharField(maxlength=1) | ||||||
|  |         something_else = MytypeField() | ||||||
|  |  | ||||||
|  | If you aim to build a database-agnostic application, you should account for | ||||||
|  | differences in database column types. For example, the date/time column type | ||||||
|  | in PostgreSQL is called ``timestamp``, while the same column in MySQL is called | ||||||
|  | ``datetime``. The simplest way to handle this in a ``db_type()`` method is to | ||||||
|  | import the Django settings module and check the ``DATABASE_ENGINE`` setting. | ||||||
|  | For example:: | ||||||
|  |  | ||||||
|  |     class MyDateField(models.Field): | ||||||
|  |         def db_type(self): | ||||||
|  |             from django.conf import settings | ||||||
|  |             if settings.DATABASE_ENGINE == 'mysql': | ||||||
|  |                 return 'datetime' | ||||||
|  |             else: | ||||||
|  |                 return 'timestamp' | ||||||
|  |  | ||||||
|  | The ``db_type()`` method is only called by Django when the framework constructs | ||||||
|  | the ``CREATE TABLE`` statements for your application -- that is, when you first | ||||||
|  | create your tables. It's not called at any other time, so it can afford to | ||||||
|  | execute slightly complex code, such as the ``DATABASE_ENGINE`` check in the | ||||||
|  | above example. | ||||||
|  |  | ||||||
| Meta options | Meta options | ||||||
| ============ | ============ | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user