Django

Code

Changeset 5725

Show
Ignore:
Timestamp:
07/20/07 01:28:56 (1 year ago)
Author:
adrian
Message:

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.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/core/management.py

    r5696 r5725  
    9696    return sequence_list 
    9797 
    98 # If the foreign key points to an AutoField, a PositiveIntegerField or a 
    99 # PositiveSmallIntegerField, the foreign key should be an IntegerField, not the 
    100 # referred field type. Otherwise, the foreign key should be the same type of 
    101 # field as the field to which it points. 
    102 get_rel_data_type = lambda f: (f.get_internal_type() in ('AutoField', 'PositiveIntegerField', 'PositiveSmallIntegerField')) and 'IntegerField' or f.get_internal_type() 
    103  
    10498def get_sql_create(app): 
    10599    "Returns a list of the CREATE TABLE SQL statements for the given app." 
    106     from django.db import get_creation_module, models 
    107  
    108     data_types = get_creation_module().DATA_TYPES 
    109  
    110     if not data_types: 
     100    from django.db import models 
     101    from django.conf import settings 
     102 
     103    if settings.DATABASE_ENGINE == 'dummy': 
    111104        # This must be the "dummy" database backend, which means the user 
    112105        # hasn't set DATABASE_ENGINE. 
     
    160153    Returns list_of_sql, pending_references_dict 
    161154    """ 
    162     from django.db import backend, get_creation_module, models 
    163     data_types = get_creation_module().DATA_TYPES 
     155    from django.db import backend, models 
    164156 
    165157    opts = model._meta 
     
    168160    pending_references = {} 
    169161    for f in opts.fields: 
    170         if isinstance(f, (models.ForeignKey, models.OneToOneField)): 
    171             rel_field = f.rel.get_related_field() 
    172             while isinstance(rel_field, (models.ForeignKey, models.OneToOneField)): 
    173                 rel_field = rel_field.rel.get_related_field() 
    174             data_type = get_rel_data_type(rel_field) 
    175         else: 
    176             rel_field = f 
    177             data_type = f.get_internal_type() 
    178         col_type = data_types[data_type] 
     162        col_type = f.db_type() 
    179163        tablespace = f.db_tablespace or opts.db_tablespace 
    180164        if col_type is not None: 
    181165            # Make the definition (e.g. 'foo VARCHAR(30)') for this field. 
    182166            field_output = [style.SQL_FIELD(backend.quote_name(f.column)), 
    183                 style.SQL_COLTYPE(col_type % rel_field.__dict__)] 
     167                style.SQL_COLTYPE(col_type)] 
    184168            field_output.append(style.SQL_KEYWORD('%sNULL' % (not f.null and 'NOT ' or ''))) 
    185169            if f.unique and (not f.primary_key or backend.allows_unique_and_pk): 
     
    205189    if opts.order_with_respect_to: 
    206190        table_output.append(style.SQL_FIELD(backend.quote_name('_order')) + ' ' + \ 
    207             style.SQL_COLTYPE(data_types['IntegerField']) + ' ' + \ 
     191            style.SQL_COLTYPE(models.IntegerField().db_type()) + ' ' + \ 
    208192            style.SQL_KEYWORD('NULL')) 
    209193    for field_constraints in opts.unique_together: 
     
    233217    Get any ALTER TABLE statements to add constraints after the fact. 
    234218    """ 
    235     from django.db import backend, get_creation_module 
     219    from django.db import backend 
    236220    from django.db.backends.util import truncate_name 
    237     data_types = get_creation_module().DATA_TYPES 
    238221 
    239222    final_output = [] 
     
    258241 
    259242def _get_many_to_many_sql_for_model(model): 
    260     from django.db import backend, get_creation_module 
     243    from django.db import backend, models 
    261244    from django.contrib.contenttypes import generic 
    262  
    263     data_types = get_creation_module().DATA_TYPES 
    264245 
    265246    opts = model._meta 
     
    276257            table_output.append('    %s %s %s%s,' % \ 
    277258                (style.SQL_FIELD(backend.quote_name('id')), 
    278                 style.SQL_COLTYPE(data_types['AutoField']), 
     259                style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type()), 
    279260                style.SQL_KEYWORD('NOT NULL PRIMARY KEY'), 
    280261                tablespace_sql)) 
    281262            table_output.append('    %s %s %s %s (%s)%s,' % \ 
    282263                (style.SQL_FIELD(backend.quote_name(f.m2m_column_name())), 
    283                 style.SQL_COLTYPE(data_types[get_rel_data_type(opts.pk)] % opts.pk.__dict__), 
     264                style.SQL_COLTYPE(models.ForeignKey(model).db_type()), 
    284265                style.SQL_KEYWORD('NOT NULL REFERENCES'), 
    285266                style.SQL_TABLE(backend.quote_name(opts.db_table)), 
     
    288269            table_output.append('    %s %s %s %s (%s)%s,' % \ 
    289270                (style.SQL_FIELD(backend.quote_name(f.m2m_reverse_name())), 
    290                 style.SQL_COLTYPE(data_types[get_rel_data_type(f.rel.to._meta.pk)] % f.rel.to._meta.pk.__dict__), 
     271                style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type()), 
    291272                style.SQL_KEYWORD('NOT NULL REFERENCES'), 
    292273                style.SQL_TABLE(backend.quote_name(f.rel.to._meta.db_table)), 
     
    518499def syncdb(verbosity=1, interactive=True): 
    519500    "Creates the database tables for all apps in INSTALLED_APPS whose tables haven't already been created." 
    520     from django.db import backend, connection, transaction, models, get_creation_module 
     501    from django.db import backend, connection, transaction, models 
    521502    from django.conf import settings 
    522503 
     
    533514        except ImportError: 
    534515            pass 
    535  
    536     data_types = get_creation_module().DATA_TYPES 
    537516 
    538517    cursor = connection.cursor() 
     
    12671246def createcachetable(tablename): 
    12681247    "Creates the table needed to use the SQL cache backend" 
    1269     from django.db import backend, connection, transaction, get_creation_module, models 
    1270     data_types = get_creation_module().DATA_TYPES 
     1248    from django.db import backend, connection, transaction, models 
    12711249    fields = ( 
    12721250        # "key" is a reserved word in MySQL, so use "cache_key" instead. 
     
    12781256    index_output = [] 
    12791257    for f in fields: 
    1280         field_output = [backend.quote_name(f.name), data_types[f.get_internal_type()] % f.__dict__
     1258        field_output = [backend.quote_name(f.name), f.db_type()
    12811259        field_output.append("%sNULL" % (not f.null and "NOT " or "")) 
    12821260        if f.unique: 
  • django/trunk/django/db/backends/ado_mssql/creation.py

    r5302 r5725  
    1313    'IntegerField':      'int', 
    1414    'IPAddressField':    'char(15)', 
    15     'ManyToManyField':   None, 
    1615    'NullBooleanField':  'bit', 
    1716    'OneToOneField':     'int', 
  • django/trunk/django/db/backends/mysql/creation.py

    r5302 r5725  
    1717    'IntegerField':      'integer', 
    1818    'IPAddressField':    'char(15)', 
    19     'ManyToManyField':   None, 
    2019    'NullBooleanField':  'bool', 
    2120    'OneToOneField':     'integer', 
  • django/trunk/django/db/backends/mysql_old/creation.py

    r5302 r5725  
    1717    'IntegerField':      'integer', 
    1818    'IPAddressField':    'char(15)', 
    19     'ManyToManyField':   None, 
    2019    'NullBooleanField':  'bool', 
    2120    'OneToOneField':     'integer', 
  • django/trunk/django/db/backends/oracle/creation.py

    r5609 r5725  
    2020    'IntegerField':                 'NUMBER(11)', 
    2121    'IPAddressField':               'VARCHAR2(15)', 
    22     'ManyToManyField':              None, 
    2322    'NullBooleanField':             'NUMBER(1) CHECK ((%(column)s IN (0,1)) OR (%(column)s IS NULL))', 
    2423    'OneToOneField':                'NUMBER(11)', 
  • django/trunk/django/db/backends/postgresql/creation.py

    r5302 r5725  
    1717    'IntegerField':      'integer', 
    1818    'IPAddressField':    'inet', 
    19     'ManyToManyField':   None, 
    2019    'NullBooleanField':  'boolean', 
    2120    'OneToOneField':     'integer', 
  • django/trunk/django/db/backends/sqlite3/creation.py

    r5302 r5725  
    1616    'IntegerField':                 'integer', 
    1717    'IPAddressField':               'char(15)', 
    18     'ManyToManyField':              None, 
    1918    'NullBooleanField':             'bool', 
    2019    'OneToOneField':                'integer', 
  • django/trunk/django/db/models/fields/__init__.py

    r5625 r5725  
     1from django.db import get_creation_module 
    12from django.db.models import signals 
    23from django.dispatch import dispatcher 
     
    118119        return value 
    119120 
     121    def db_type(self): 
     122        """ 
     123        Returns the database column data type for this field, taking into 
     124        account the DATABASE_ENGINE setting. 
     125        """ 
     126        # The default implementation of this method looks at the 
     127        # backend-specific DATA_TYPES dictionary, looking up the field by its 
     128        # "internal type". 
     129        # 
     130        # A Field class can implement the get_internal_type() method to specify 
     131        # which *preexisting* Django Field class it's most similar to -- i.e., 
     132        # an XMLField is represented by a TEXT column type, which is the same 
     133        # as the TextField Django field type, which means XMLField's 
     134        # get_internal_type() returns 'TextField'. 
     135        # 
     136        # But the limitation of the get_internal_type() / DATA_TYPES approach 
     137        # is that it cannot handle database column types that aren't already 
     138        # mapped to one of the built-in Django field types. In this case, you 
     139        # can implement db_type() instead of get_internal_type() to specify 
     140        # exactly which wacky database column type you want to use. 
     141        data_types = get_creation_module().DATA_TYPES 
     142        internal_type = self.get_internal_type() 
     143        return data_types[internal_type] % self.__dict__ 
     144 
    120145    def validate_full(self, field_data, all_data): 
    121146        """ 
  • django/trunk/django/db/models/fields/related.py

    r5609 r5725  
    11from django.db import backend, transaction 
    22from django.db.models import signals, get_model 
    3 from django.db.models.fields import AutoField, Field, IntegerField, get_ul_class 
     3from django.db.models.fields import AutoField, Field, IntegerField, PositiveIntegerField, PositiveSmallIntegerField, get_ul_class 
    44from django.db.models.related import RelatedObject 
    55from django.utils.text import capfirst 
     
    557557        return super(ForeignKey, self).formfield(**defaults) 
    558558 
     559    def db_type(self): 
     560        # The database column type of a ForeignKey is the column type 
     561        # of the field to which it points. An exception is if the ForeignKey 
     562        # points to an AutoField/PositiveIntegerField/PositiveSmallIntegerField, 
     563        # in which case the column type is simply that of an IntegerField. 
     564        rel_field = self.rel.get_related_field() 
     565        if isinstance(rel_field, (AutoField, PositiveIntegerField, PositiveSmallIntegerField)): 
     566            return IntegerField().db_type() 
     567        return rel_field.db_type() 
     568 
    559569class OneToOneField(RelatedField, IntegerField): 
    560570    def __init__(self, to, to_field=None, **kwargs): 
     
    622632        defaults.update(kwargs) 
    623633        return super(OneToOneField, self).formfield(**defaults) 
     634 
     635    def db_type(self): 
     636        # The database column type of a OneToOneField is the column type 
     637        # of the field to which it points. An exception is if the OneToOneField 
     638        # points to an AutoField/PositiveIntegerField/PositiveSmallIntegerField, 
     639        # in which case the column type is simply that of an IntegerField. 
     640        rel_field = self.rel.get_related_field() 
     641        if isinstance(rel_field, (AutoField, PositiveIntegerField, PositiveSmallIntegerField)): 
     642            return IntegerField().db_type() 
     643        return rel_field.db_type() 
    624644 
    625645class ManyToManyField(RelatedField, Field): 
     
    745765            defaults['initial'] = [i._get_pk_val() for i in defaults['initial']] 
    746766        return super(ManyToManyField, self).formfield(**defaults) 
     767 
     768    def db_type(self): 
     769        # A ManyToManyField is not represented by a single column, 
     770        # so return None. 
     771        return None 
    747772 
    748773class ManyToOneRel(object): 
  • django/trunk/docs/model-api.txt

    r5638 r5725  
    981981 
    982982.. _One-to-one relationship model example: http://www.djangoproject.com/documentation/models/one_to_one/ 
     983 
     984Custom field types 
     985------------------ 
     986 
     987**New in Django development version** 
     988 
     989Django's built-in field types don't cover every possible database column type -- 
     990only the common types, such as ``VARCHAR`` and ``INTEGER``. For more obscure 
     991column types, such as geographic polygons or even user-created types such as 
     992`PostgreSQL custom types`_, you can define your own Django ``Field`` subclasses. 
     993 
     994.. _PostgreSQL custom types: http://www.postgresql.org/docs/8.2/interactive/sql-createtype.html 
     995 
     996.. admonition:: Experimental territory 
     997 
     998    This is an area of Django that traditionally has not been documented, but 
     999    we're starting to include bits of documentation, one feature at a time. 
     1000    Please forgive the sparseness of this section. 
     1001 
     1002    If you like living on the edge and are comfortable with the risk of 
     1003    unstable, undocumented APIs, see the code for the core ``Field`` class 
     1004    in ``django/db/models/fields/__init__.py`` -- but if/when the innards 
     1005    change, don't say we didn't warn you. 
     1006 
     1007To create a custom field type, simply subclass ``django.db.models.Field``. 
     1008Here is an incomplete list of the methods you should implement: 
     1009 
     1010``db_type()`` 
     1011~~~~~~~~~~~~~ 
     1012 
     1013Returns the database column data type for the ``Field``, taking into account 
     1014the current ``DATABASE_ENGINE`` setting. 
     1015 
     1016Say you've created a PostgreSQL custom type called ``mytype``. You can use this 
     1017field with Django by subclassing ``Field`` and implementing the ``db_type()`` 
     1018method, like so:: 
     1019 
     1020    from django.db import models 
     1021 
     1022    class MytypeField(models.Field): 
     1023        def db_type(self): 
     1024            return 'mytype' 
     1025 
     1026Once you have ``MytypeField``, you can use it in any model, just like any other 
     1027``Field`` type:: 
     1028 
     1029    class Person(models.Model): 
     1030        name = models.CharField(maxlength=80) 
     1031        gender = models.CharField(maxlength=1) 
     1032        something_else = MytypeField() 
     1033 
     1034If you aim to build a database-agnostic application, you should account for 
     1035differences in database column types. For example, the date/time column type 
     1036in PostgreSQL is called ``timestamp``, while the same column in MySQL is called 
     1037``datetime``. The simplest way to handle this in a ``db_type()`` method is to 
     1038import the Django settings module and check the ``DATABASE_ENGINE`` setting. 
     1039For example:: 
     1040 
     1041    class MyDateField(models.Field): 
     1042        def db_type(self): 
     1043            from django.conf import settings 
     1044            if settings.DATABASE_ENGINE == 'mysql': 
     1045                return 'datetime' 
     1046            else: 
     1047                return 'timestamp' 
     1048 
     1049The ``db_type()`` method is only called by Django when the framework constructs 
     1050the ``CREATE TABLE`` statements for your application -- that is, when you first 
     1051create your tables. It's not called at any other time, so it can afford to 
     1052execute slightly complex code, such as the ``DATABASE_ENGINE`` check in the 
     1053above example. 
    9831054 
    9841055Meta options