Ticket #1261: firebird-6660.diff

File firebird-6660.diff, 74.9 KB (added by Ivan Illarionov, 16 years ago)

All tests passed

  • django/db/models/base.py

     
    241241            db_values = [f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True)) for f in self._meta.fields if not isinstance(f, AutoField)]
    242242            # If the PK has been manually set, respect that.
    243243            if pk_set:
    244                 field_names += [f.column for f in self._meta.fields if isinstance(f, AutoField)]
     244                if connection.features.quote_autofields:
     245                    field_names += [qn(f.column) for f in self._meta.fields if isinstance(f, AutoField)]
     246                else:
     247                    field_names += [f.column for f in self._meta.fields if isinstance(f, AutoField)]
    245248                db_values += [f.get_db_prep_save(raw and getattr(self, f.attname) or f.pre_save(self, True)) for f in self._meta.fields if isinstance(f, AutoField)]
    246249            placeholders = ['%s'] * len(field_names)
    247250            if self._meta.order_with_respect_to:
  • django/db/models/fields/__init__.py

     
    66except ImportError:
    77    from django.utils import _decimal as decimal    # for Python 2.3
    88
    9 from django.db import get_creation_module
     9from django.db import connection, get_creation_module
    1010from django.db.models import signals
    1111from django.dispatch import dispatcher
    1212from django.conf import settings
     
    6666#
    6767#     getattr(obj, opts.pk.attname)
    6868
    69 class Field(object):
     69class _Field(object):
    7070    # Provide backwards compatibility for the maxlength attribute and
    7171    # argument for this class and all subclasses.
    7272    __metaclass__ = LegacyMaxlength
     
    8383        core=False, rel=None, default=NOT_PROVIDED, editable=True, serialize=True,
    8484        prepopulate_from=None, unique_for_date=None, unique_for_month=None,
    8585        unique_for_year=None, validator_list=None, choices=None, radio_admin=None,
    86         help_text='', db_column=None, db_tablespace=None):
     86        help_text='', db_column=None, db_tablespace=None, encoding=None):
    8787        self.name = name
    8888        self.verbose_name = verbose_name
    8989        self.primary_key = primary_key
    9090        self.max_length, self.unique = max_length, unique
     91        self.encoding = encoding
    9192        self.blank, self.null = blank, null
    9293        # Oracle treats the empty string ('') as null, so coerce the null
    9394        # option whenever '' is a possible value.
     
    148149        data_types = get_creation_module().DATA_TYPES
    149150        internal_type = self.get_internal_type()
    150151        if internal_type not in data_types:
    151             return None
     152            return None   
    152153        return data_types[internal_type] % self.__dict__
    153154
    154155    def validate_full(self, field_data, all_data):
     
    402403        "Returns the value of this field in the given model instance."
    403404        return getattr(obj, self.attname)
    404405
     406# Use the backend's Field class if it defines one. Otherwise, use _Field.
     407if connection.features.uses_custom_field:
     408    Field = connection.ops.field_class(_Field)
     409else:
     410    Field = _Field
     411
    405412class AutoField(Field):
    406413    empty_strings_allowed = False
    407414    def __init__(self, *args, **kwargs):
     
    688695        defaults.update(kwargs)
    689696        return super(DecimalField, self).formfield(**defaults)
    690697
     698class DefaultCharField(CharField):
     699    def __init__(self, *args, **kwargs):
     700        DEFAULT_MAX_LENGTH = 100
     701        if hasattr(settings, 'DEFAULT_MAX_LENGTH'):
     702           DEFAULT_MAX_LENGTH = settings.DEFAULT_MAX_LENGT
     703        kwargs['max_length'] = kwargs.get('max_length', DEFAULT_MAX_LENGTH)
     704        CharField.__init__(self, *args, **kwargs)
     705
    691706class EmailField(CharField):
    692707    def __init__(self, *args, **kwargs):
    693708        kwargs['max_length'] = kwargs.get('max_length', 75)
     
    890905        defaults.update(kwargs)
    891906        return super(IPAddressField, self).formfield(**defaults)
    892907
     908class LargeTextField(Field):
     909    def get_manipulator_field_objs(self):
     910        return [oldforms.LargeTextField]
     911
     912    def formfield(self, **kwargs):
     913        defaults = {'widget': forms.Textarea}
     914        defaults.update(kwargs)
     915        return super(LargeTextField, self).formfield(**defaults)
     916
    893917class NullBooleanField(Field):
    894918    empty_strings_allowed = False
    895919    def __init__(self, *args, **kwargs):
     
    9971021            # doesn't support microseconds.
    9981022            if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'):
    9991023                value = value.replace(microsecond=0)
    1000             if settings.DATABASE_ENGINE == 'oracle':
    1001                 # cx_Oracle expects a datetime.datetime to persist into TIMESTAMP field.
     1024            if settings.DATABASE_ENGINE in ('oracle', 'firebird'):
     1025                # cx_Oracle and kinterbasdb expect a datetime.datetime to persist into TIMESTAMP field.
    10021026                if isinstance(value, datetime.time):
    10031027                    value = datetime.datetime(1900, 1, 1, value.hour, value.minute,
    10041028                                              value.second, value.microsecond)
  • django/db/models/fields/related.py

     
    331331                        new_ids.add(obj)
    332332                # Add the newly created or already existing objects to the join table.
    333333                # First find out which items are already added, to avoid adding them twice
     334                qn = connection.ops.quote_name
    334335                cursor = connection.cursor()
    335336                cursor.execute("SELECT %s FROM %s WHERE %s = %%s AND %s IN (%s)" % \
    336                     (target_col_name, self.join_table, source_col_name,
    337                     target_col_name, ",".join(['%s'] * len(new_ids))),
     337                    (qn(target_col_name), qn(self.join_table), qn(source_col_name),
     338                    qn(target_col_name), ",".join(['%s'] * len(new_ids))),
    338339                    [self._pk_val] + list(new_ids))
    339340                existing_ids = set([row[0] for row in cursor.fetchall()])
    340341
    341342                # Add the ones that aren't there already
    342343                for obj_id in (new_ids - existing_ids):
    343                     cursor.execute("INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
    344                         (self.join_table, source_col_name, target_col_name),
    345                         [self._pk_val, obj_id])
     344                    cursor.execute('INSERT INTO %s (%s, %s) VALUES (%%s, %%s)' % \
     345                        (qn(self.join_table), qn(source_col_name), qn(target_col_name)),
     346                        (self._pk_val, obj_id))
    346347                transaction.commit_unless_managed()
    347348
    348349        def _remove_items(self, source_col_name, target_col_name, *objs):
  • django/db/models/query.py

     
    612612        columns = [f.column for f in fields]
    613613        select = ['%s.%s' % (qn(self.model._meta.db_table), qn(c)) for c in columns]
    614614        if extra_select:
    615             select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), qn(s[0])) for s in extra_select])
     615            if not settings.DATABASE_ENGINE == 'firebird':
     616                select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), qn(s[0])) for s in extra_select])
     617            else:
     618                select.extend(['(%s) AS %s' % (connection.ops.quote_id_plus_number(s[1]), qn(s[0])) for s in extra_select])
    616619            field_names.extend([f[0] for f in extra_select])
    617620
    618621        cursor = connection.cursor()
     
    11111114            # Last query term was a normal field.
    11121115            column = field.column
    11131116            db_type = field.db_type()
    1114 
    11151117        where.append(get_where_clause(lookup_type, current_table + '.', column, value, db_type))
    11161118        params.extend(field.get_db_prep_lookup(lookup_type, value))
    11171119
     
    11461148            if isinstance(f, generic.GenericRelation):
    11471149                from django.contrib.contenttypes.models import ContentType
    11481150                query_extra = 'AND %s=%%s' % f.rel.to._meta.get_field(f.content_type_field_name).column
     1151                if settings.DATABASE_ENGINE == 'firebird':
     1152                    query_extra = 'AND %s=%%s' % qn(f.rel.to._meta.get_field(f.content_type_field_name).column)
    11491153                args_extra = [ContentType.objects.get_for_model(cls).id]
    11501154            else:
    11511155                query_extra = ''
  • django/db/backends/__init__.py

     
    4848    needs_upper_for_iops = False
    4949    supports_constraints = True
    5050    supports_tablespaces = False
     51    quote_autofields = False
    5152    uses_case_insensitive_names = False
     53    uses_custom_field = False
    5254    uses_custom_queryset = False
    5355
    5456class BaseDatabaseOperations(object):
     
    6567        This SQL is executed when a table is created.
    6668        """
    6769        return None
    68 
     70   
     71    def cascade_delete_update_sql(self):
     72        """
     73        Returns the SQL necessary to make a cascading deletes and updates
     74        of foreign key references during a CREATE TABLE statement.
     75        """
     76        return ''
     77   
    6978    def date_extract_sql(self, lookup_type, field_name):
    7079        """
    7180        Given a lookup_type of 'year', 'month' or 'day', returns the SQL that
     
    127136        contain a '%s' placeholder for the value being searched against.
    128137        """
    129138        raise NotImplementedError('Full-text search is not implemented for this database backend')
    130 
     139   
    131140    def last_executed_query(self, cursor, sql, params):
    132141        """
    133142        Returns a string of the query last executed by the given cursor, with
     
    175184        is no limit.
    176185        """
    177186        return None
    178 
     187   
    179188    def pk_default_value(self):
    180189        """
    181190        Returns the value to use during an INSERT statement to specify that
  • django/db/backends/firebird/base.py

     
     1"""
     2Firebird database backend for Django.
     3
     4Requires KInterbasDB 3.2: http://kinterbasdb.sourceforge.net/
     5The egenix mx (mx.DateTime) is NOT required
     6
     7Database charset should be UNICODE_FSS or UTF8 (FireBird 2.0+)
     8To use UTF8 encoding add FIREBIRD_CHARSET = 'UTF8' to your settings.py
     9UNICODE_FSS works with all versions and uses less memory
     10"""
     11
     12from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures, BaseDatabaseOperations, util
     13import sys
     14try:
     15    import decimal
     16except ImportError:
     17    from django.utils import _decimal as decimal    # for Python 2.3
     18
     19try:
     20    import kinterbasdb as Database
     21except ImportError, e:
     22    from django.core.exceptions import ImproperlyConfigured
     23    raise ImproperlyConfigured, "Error loading KInterbasDB module: %s" % e
     24
     25DatabaseError = Database.DatabaseError
     26IntegrityError = Database.IntegrityError
     27
     28class DatabaseFeatures(BaseDatabaseFeatures):
     29    inline_fk_references = False
     30    needs_datetime_string_cast = False
     31    needs_upper_for_iops = True
     32    quote_autofields = True
     33    supports_constraints = True #turn this off to pass the tests with forward/post references
     34    uses_custom_field = True
     35    uses_custom_queryset = True
     36
     37class DatabaseOperations(BaseDatabaseOperations):
     38    _max_name_length = 31
     39    def __init__(self):
     40        self._firebird_version = None
     41        self._page_size = None
     42   
     43    def get_generator_name(self, name):
     44        return '%s_G' % util.truncate_name(name.strip('"'), self._max_name_length-2).upper()
     45
     46    def get_trigger_name(self, name):
     47        return '%s_T' % util.truncate_name(name.strip('"'), self._max_name_length-2).upper()
     48   
     49    def _get_firebird_version(self):
     50        if self._firebird_version is None:
     51            from django.db import connection
     52            self._firebird_version = [int(val) for val in connection.server_version.split()[-1].split('.')]
     53        return self._firebird_version
     54    firebird_version = property(_get_firebird_version)
     55 
     56    def _get_page_size(self):
     57        if self._page_size is None:
     58            from django.db import connection
     59            self._page_size = connection.database_info(Database.isc_info_page_size, 'i')
     60        return self._page_size
     61    page_size = property(_get_page_size)
     62   
     63    def _get_index_limit(self):
     64        if self.firebird_version[0] < 2:
     65            self._index_limit = 252
     66        else:
     67            page_size = self._get_page_size()
     68            self._index_limit = page_size/4
     69        return self._index_limit
     70    index_limit = property(_get_index_limit)
     71
     72    def autoinc_sql(self, style, table_name, column_name):
     73        """
     74        To simulate auto-incrementing primary keys in Firebird, we have to
     75        create a generator and a trigger.
     76   
     77        Create the generators and triggers names based only on table name
     78        since django only support one auto field per model
     79        """
     80   
     81        generator_name = self.get_generator_name(table_name)
     82        trigger_name = self.get_trigger_name(table_name)
     83        column_name = self.quote_name(column_name)
     84        table_name = self.quote_name(table_name)
     85       
     86        generator_sql = "%s %s;" % ( style.SQL_KEYWORD('CREATE GENERATOR'),
     87                                     style.SQL_TABLE(generator_name))     
     88        trigger_sql = "\n".join([
     89            "%s %s %s %s" % ( \
     90            style.SQL_KEYWORD('CREATE TRIGGER'), style.SQL_TABLE(trigger_name), style.SQL_KEYWORD('FOR'),
     91            style.SQL_TABLE(table_name)),
     92            "%s 0 %s" % (style.SQL_KEYWORD('ACTIVE BEFORE INSERT POSITION'), style.SQL_KEYWORD('AS')),
     93            style.SQL_KEYWORD('BEGIN'),
     94            "  %s ((%s.%s %s) %s (%s.%s = 0)) %s" % ( \
     95                style.SQL_KEYWORD('IF'),
     96                style.SQL_KEYWORD('NEW'), style.SQL_FIELD(column_name), style.SQL_KEYWORD('IS NULL'),
     97                style.SQL_KEYWORD('OR'), style.SQL_KEYWORD('NEW'), style.SQL_FIELD(column_name),
     98                style.SQL_KEYWORD('THEN')
     99            ),
     100            "  %s" % style.SQL_KEYWORD('BEGIN'),
     101            "    %s.%s = %s(%s, 1);" % ( \
     102                style.SQL_KEYWORD('NEW'), style.SQL_FIELD(column_name),
     103                style.SQL_KEYWORD('GEN_ID'), style.SQL_TABLE(generator_name)
     104            ),
     105            "  %s" % style.SQL_KEYWORD('END'),
     106            style.SQL_KEYWORD('END')
     107            ])
     108        return (generator_sql, trigger_sql)
     109
     110    def max_name_length(self):
     111        return self._max_name_length
     112         
     113    def field_class(this, DefaultField):
     114        from django.db import connection
     115        from django.db.models.fields import prep_for_like_query
     116        class FirebirdField(DefaultField):
     117            def get_db_prep_lookup(self, lookup_type, value):       
     118                "Returns field's value prepared for database lookup."
     119                if lookup_type in ('exact', 'regex', 'iregex', 'gt', 'gte', 'lt',
     120                    'lte', 'month', 'day', 'search', 'icontains',
     121                    'startswith', 'istartswith'):
     122                    return [value]
     123                elif lookup_type in ('range', 'in'):
     124                    return value
     125                elif lookup_type in ('contains',):
     126                    return ["%%%s%%" % prep_for_like_query(value)]
     127                elif lookup_type == 'iexact':
     128                    return [prep_for_like_query(value)]
     129                elif lookup_type in ('endswith', 'iendswith'):
     130                    return ["%%%s" % prep_for_like_query(value)]
     131                elif lookup_type == 'isnull':
     132                    return []
     133                elif lookup_type == 'year':
     134                    try:
     135                        value = int(value)
     136                    except ValueError:
     137                        raise ValueError("The __year lookup type requires an integer argument")
     138                    return ['%s-01-01 00:00:00' % value, '%s-12-31 23:59:59.999999' % value]
     139                raise TypeError("Field has invalid lookup: %s" % lookup_type)
     140        return FirebirdField
     141
     142    def query_set_class(this, DefaultQuerySet):
     143        from django.db import connection
     144        from django.db.models.query import EmptyResultSet, GET_ITERATOR_CHUNK_SIZE
     145        class FirebirdQuerySet(DefaultQuerySet):
     146            def _get_sql_clause(self):
     147                from django.db.models.query import SortedDict, handle_legacy_orderlist, orderfield2column, fill_table_cache
     148                qn = this.quote_name
     149                opts = self.model._meta
     150
     151                # Construct the fundamental parts of the query: SELECT X FROM Y WHERE Z.
     152                select = ["%s.%s" % (qn(opts.db_table), qn(f.column)) for f in opts.fields]
     153                tables = [qn(t) for t in self._tables]
     154                joins = SortedDict()
     155                where = self._where[:]
     156                params = self._params[:]
     157
     158                # Convert self._filters into SQL.
     159                joins2, where2, params2 = self._filters.get_sql(opts)
     160                joins.update(joins2)
     161                where.extend(where2)
     162                params.extend(params2)
     163
     164                # Add additional tables and WHERE clauses based on select_related.
     165                if self._select_related:
     166                    fill_table_cache(opts, select, tables, where,
     167                                     old_prefix=opts.db_table,
     168                                     cache_tables_seen=[opts.db_table],
     169                                     max_depth=self._max_related_depth)
     170               
     171                # Add any additional SELECTs.
     172                if self._select:
     173                    select.extend([('(%s AS %s') % (qn(s[1]), qn(s[0])) for s in self._select.items()])
     174
     175                # Start composing the body of the SQL statement.
     176                sql = [" FROM", qn(opts.db_table)]
     177
     178                # Compose the join dictionary into SQL describing the joins.
     179                if joins:
     180                    sql.append(" ".join(["%s %s %s ON %s" % (join_type, table, alias, condition)
     181                                    for (alias, (table, join_type, condition)) in joins.items()]))
     182
     183                # Compose the tables clause into SQL.
     184                if tables:
     185                    sql.append(", " + ", ".join(tables))
     186
     187                # Compose the where clause into SQL.
     188                if where:
     189                    sql.append(where and "WHERE " + " AND ".join(where))
     190
     191                # ORDER BY clause
     192                order_by = []
     193                if self._order_by is not None:
     194                    ordering_to_use = self._order_by
     195                else:
     196                    ordering_to_use = opts.ordering
     197                for f in handle_legacy_orderlist(ordering_to_use):
     198                    if f == '?': # Special case.
     199                        order_by.append(connection.ops.random_function_sql())
     200                    else:
     201                        if f.startswith('-'):
     202                            col_name = f[1:]
     203                            order = "DESC"
     204                        else:
     205                            col_name = f
     206                            order = "ASC"
     207                        if "." in col_name:
     208                            table_prefix, col_name = col_name.split('.', 1)
     209                            table_prefix = qn(table_prefix) + '.'
     210                        else:
     211                            # Use the database table as a column prefix if it wasn't given,
     212                            # and if the requested column isn't a custom SELECT.
     213                            if "." not in col_name and col_name not in (self._select or ()):
     214                                table_prefix = qn(opts.db_table) + '.'
     215                            else:
     216                                table_prefix = ''
     217                        order_by.append('%s%s %s' % \
     218                            (table_prefix, qn(orderfield2column(col_name, opts)), order))
     219                if order_by:
     220                    sql.append("ORDER BY " + ", ".join(order_by))
     221
     222                return select, " ".join(sql), params
     223           
     224            def iterator(self):
     225                "Performs the SELECT database lookup of this QuerySet."
     226                from django.db.models.query import get_cached_row
     227                try:
     228                    select, sql, params = self._get_sql_clause()
     229                except EmptyResultSet:
     230                    raise StopIteration
     231                   
     232                # self._select is a dictionary, and dictionaries' key order is
     233                # undefined, so we convert it to a list of tuples.
     234                extra_select = self._select.items()
     235               
     236                cursor = connection.cursor()
     237                limit_offset_before = ""
     238                if self._limit is not None:
     239                    limit_offset_before += "FIRST %s " % self._limit
     240                    if self._offset:
     241                        limit_offset_before += "SKIP %s " % self._offset
     242                else:
     243                    assert self._offset is None, "'offset' is not allowed without 'limit'"
     244                cursor.execute("SELECT " + limit_offset_before + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params)
     245                fill_cache = self._select_related
     246                fields = self.model._meta.fields
     247                index_end = len(fields)
     248                while 1:
     249                    rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE)
     250                    if not rows:
     251                        raise StopIteration
     252                    for row in rows:
     253                        row = self.resolve_columns(row, fields)
     254                        if fill_cache:
     255                            obj, index_end = get_cached_row(klass=self.model, row=row,
     256                                                            index_start=0, max_depth=self._max_related_depth)
     257                        else:
     258                            obj = self.model(*row[:index_end])
     259                        for i, k in enumerate(extra_select):
     260                            setattr(obj, k[0], row[index_end+i])
     261                        yield obj
     262           
     263            def resolve_columns(self, row, fields=()):
     264                from django.db.models.fields import DateField, DateTimeField, \
     265                    TimeField, BooleanField, NullBooleanField, DecimalField, Field
     266                values = []
     267                for value, field in map(None, row, fields):
     268                    #if value is None and isinstance(field, Field) and field.empty_strings_allowed:
     269                    #    value = u''
     270                    # Convert 1 or 0 to True or False
     271                    if value in (1, 0) and isinstance(field, (BooleanField, NullBooleanField)):
     272                        value = bool(value)
     273
     274                    values.append(value)
     275                return values
     276               
     277            def extra(self, select=None, where=None, params=None, tables=None):
     278                assert self._limit is None and self._offset is None, \
     279                        "Cannot change a query once a slice has been taken"
     280                clone = self._clone()
     281                qn = this.quote_name
     282                if select: clone._select.update(select)
     283                if where:
     284                    qn_where = []
     285                    for where_item in where:
     286                        try:
     287                            table, col_exact = where_item.split(".")
     288                            col, value = col_exact.split("=")
     289                            where_item = "%s.%s = %s" % (qn(table.strip()),
     290                                qn(col.strip()), value.strip())
     291                        except:
     292                            try:
     293                                table, value = where_item.split("=")
     294                                where_item = "%s = %s" % (qn(table.strip()), qn(value.strip()))
     295                            except:
     296                                raise TypeError, "Can't understand extra WHERE clause: %s" % where
     297                        qn_where.append(where_item)
     298                    clone._where.extend(qn_where)
     299                if params: clone._params.extend(params)
     300                if tables: clone._tables.extend(tables)
     301                return clone
     302               
     303        return FirebirdQuerySet
     304
     305    def quote_name(self, name):
     306        name = '"%s"' % util.truncate_name(name.strip('"'), self._max_name_length)
     307        return name
     308   
     309    def quote_id_plus_number(self, name):
     310        try:
     311            return '"%s" + %s' % tuple(s.strip() for s in name.strip('"').split('+'))
     312        except:
     313            return self.quote_name(name)
     314           
     315    def pk_default_value(self):
     316        """
     317        Returns the value to use during an INSERT statement to specify that
     318        the field should use its default value.
     319        """
     320        return 'NULL'
     321   
     322    def field_cast_sql(self, db_type):
     323        return '%s'
     324   
     325    def last_insert_id(self, cursor, table_name, pk_name=None):
     326        generator_name = self.get_generator_name(table_name)
     327        cursor.execute('SELECT GEN_ID(%s, 0) from RDB$DATABASE' % generator_name)
     328        return cursor.fetchone()[0]
     329
     330    def date_extract_sql(self, lookup_type, column_name):
     331        # lookup_type is 'year', 'month', 'day'
     332        return "EXTRACT(%s FROM %s)" % (lookup_type, column_name)
     333
     334    def date_trunc_sql(self, lookup_type, column_name):
     335        if lookup_type == 'year':
     336             sql = "EXTRACT(year FROM %s)||'-01-01 00:00:00'" % column_name
     337        elif lookup_type == 'month':
     338            sql = "EXTRACT(year FROM %s)||'-'||EXTRACT(month FROM %s)||'-01 00:00:00'" % (column_name, column_name)
     339        elif lookup_type == 'day':
     340            sql = "EXTRACT(year FROM %s)||'-'||EXTRACT(month FROM %s)||'-'||EXTRACT(day FROM %s)||' 00:00:00'" % (column_name, column_name, column_name)
     341        return "CAST(%s AS TIMESTAMP)" % sql
     342   
     343    def cascade_delete_update_sql(self):
     344        #TODO: Use ON DELETE CASCADE only on M2M tables by default
     345        return " ON DELETE CASCADE ON UPDATE CASCADE"
     346   
     347    def datetime_cast_sql(self):
     348        return None
     349
     350    def limit_offset_sql(self, limit, offset=None):
     351        # limits are handled in custom FirebirdQuerySet
     352        assert False, 'Limits are handled in a different way in Firebird'
     353        return ""
     354
     355    def random_function_sql(self):
     356        return "rand()"
     357
     358    def start_transaction_sql(self):
     359        return ""
     360
     361    def sequence_reset_sql(self, style, model_list):
     362        from django.db import models
     363        output = []
     364        sql = ['%s %s %s' % (style.SQL_KEYWORD('CREATE OR ALTER PROCEDURE'),
     365                             style.SQL_TABLE('"GENERATOR_RESET"'),
     366                             style.SQL_KEYWORD('AS'))]
     367        sql.append('%s %s' % (style.SQL_KEYWORD('DECLARE VARIABLE'), style.SQL_COLTYPE('start_val integer;')))
     368        sql.append('%s %s' % (style.SQL_KEYWORD('DECLARE VARIABLE'), style.SQL_COLTYPE('gen_val integer;')))
     369        sql.append('\t%s' % style.SQL_KEYWORD('BEGIN'))
     370        sql.append('\t\t%s %s %s %s %s %s;' % (style.SQL_KEYWORD('SELECT MAX'), style.SQL_FIELD('(%(col)s)'),
     371                                           style.SQL_KEYWORD('FROM'), style.SQL_TABLE('%(table)s'),
     372                                           style.SQL_KEYWORD('INTO'), style.SQL_COLTYPE(':start_val')))
     373        sql.append('\t\t%s (%s %s) %s' % (style.SQL_KEYWORD('IF'), style.SQL_COLTYPE('start_val'),
     374                                    style.SQL_KEYWORD('IS NULL'), style.SQL_KEYWORD('THEN')))
     375        sql.append('\t\t\t%s = %s(%s, 1 - %s(%s, 0));' %\
     376            (style.SQL_COLTYPE('gen_val'), style.SQL_KEYWORD('GEN_ID'), style.SQL_TABLE('%(gen)s'),
     377             style.SQL_KEYWORD('GEN_ID'), style.SQL_TABLE('%(gen)s')))
     378        sql.append('\t\t%s' % style.SQL_KEYWORD('ELSE'))
     379        sql.append('\t\t\t%s = %s(%s, %s - %s(%s, 0));' %\
     380            (style.SQL_COLTYPE('gen_val'), style.SQL_KEYWORD('GEN_ID'),
     381             style.SQL_TABLE('%(gen)s'), style.SQL_COLTYPE('start_val'), style.SQL_KEYWORD('GEN_ID'),
     382             style.SQL_TABLE('%(gen)s')))
     383        sql.append('\t\t%s;' % style.SQL_KEYWORD('EXIT'))
     384        sql.append('%s;' % style.SQL_KEYWORD('END'))
     385        sql ="\n".join(sql)
     386        for model in model_list:
     387            for f in model._meta.fields:
     388                if isinstance(f, models.AutoField):
     389                    generator_name = self.get_generator_name(model._meta.db_table)
     390                    column_name = self.quote_name(f.db_column or f.name)
     391                    table_name = self.quote_name(model._meta.db_table)
     392                    output.append(sql % {'col' : column_name, 'table' : table_name, 'gen' : generator_name})
     393                    output.append('%s %s;' % (style.SQL_KEYWORD('EXECUTE PROCEDURE'),
     394                                              style.SQL_TABLE('"GENERATOR_RESET"')))
     395                    break # Only one AutoField is allowed per model, so don't bother continuing.
     396            for f in model._meta.many_to_many:
     397                generator_name = self.get_generator_name(f.m2m_db_table())
     398                table_name = self.quote_name(f.m2m_db_table())
     399                column_name = '"id"'
     400                output.append(sql % {'col' : column_name, 'table' : table_name, 'gen' : generator_name})
     401                output.append('%s %s;' % (style.SQL_KEYWORD('EXECUTE PROCEDURE'),
     402                                          style.SQL_TABLE('"GENERATOR_RESET"')))
     403        return output
     404   
     405    def sql_flush(self, style, tables, sequences):
     406        if tables:
     407            #TODO: Alter all tables witk FKs without ON DELETE CASCADE to set it
     408            # Them reset to previous state when all are deleted
     409            # Becasue we may not want to have ON DELETE CASCADE by default on all FK fields
     410            sql = ['%s %s %s;' % \
     411                    (style.SQL_KEYWORD('DELETE'),
     412                     style.SQL_KEYWORD('FROM'),
     413                     style.SQL_FIELD(self.quote_name(table))
     414                     ) for table in tables]
     415            for generator_info in sequences:
     416                table_name = generator_info['table']
     417                query = "%s %s %s 0;" % (style.SQL_KEYWORD('SET GENERATOR'),
     418                    style.SQL_TABLE(self.get_generator_name(table_name)), style.SQL_KEYWORD('TO'))
     419                sql.append(query)
     420            return sql
     421        else:
     422            return []
     423
     424    def fulltext_search_sql(self, field_name):
     425        # We use varchar for TextFields so this is possible
     426        # Look at http://www.volny.cz/iprenosil/interbase/ip_ib_strings.htm
     427        return '%%s CONTAINING %s' % self.quote_name(field_name)
     428       
     429    def drop_sequence_sql(self, table):
     430        return "DROP GENERATOR %s;" % self.get_generator_name(table)
     431       
     432class FirebirdCursorWrapper(object):
     433    """
     434    Django uses "format" ('%s') style placeholders, but firebird uses "qmark" ('?') style.
     435    This fixes it -- but note that if you want to use a literal "%s" in a query,
     436    you'll need to use "%%s".
     437   
     438    We also do all automatic type conversions here.
     439    """
     440    import kinterbasdb.typeconv_datetime_stdlib as tc_dt
     441    import kinterbasdb.typeconv_fixed_decimal as tc_fd
     442    import kinterbasdb.typeconv_text_unicode as tc_tu
     443    import django.utils.encoding as dj_ue
     444   
     445   
     446    def timestamp_conv_in(self, timestamp):
     447        if isinstance(timestamp, basestring):
     448            #Replaces 6 digits microseconds to 4 digits allowed in Firebird
     449            timestamp = timestamp[:24]
     450        return self.tc_dt.timestamp_conv_in(timestamp)
     451
     452    def time_conv_in(self, value):
     453        import datetime
     454        if isinstance(value, datetime.datetime):
     455            value = datetime.time(value.hour, value.minute, value.second, value.microsecond)       
     456        return self.tc_dt.time_conv_in(value)
     457   
     458    def ascii_conv_in(self, text):
     459        if text is not None: 
     460            return self.dj_ue.smart_str(text, 'ascii')
     461   
     462    def ascii_conv_out(self, text):
     463        if text is not None:
     464            return self.dj_ue.smart_unicode(text)
     465   
     466    def fixed_conv_in(self, (val, scale)):
     467        if val is not None:
     468            if isinstance(val, basestring):
     469                val = decimal.Decimal(val)
     470            return self.tc_fd.fixed_conv_in_precise((val, scale))
     471
     472    def unicode_conv_in(self, text):
     473        if text[0] is not None:
     474            return self.tc_tu.unicode_conv_in((self.dj_ue.smart_unicode(text[0]), self.FB_CHARSET_CODE))
     475
     476    def blob_conv_in(self, text):
     477        return self.tc_tu.unicode_conv_in((self.dj_ue.smart_unicode(text), self.FB_CHARSET_CODE))
     478
     479    def blob_conv_out(self, text):
     480        return self.tc_tu.unicode_conv_out((text, self.FB_CHARSET_CODE))
     481       
     482    def __init__(self, cursor, connection):   
     483        self.cursor = cursor
     484        self._connection = connection
     485        self._statement = None #prepared statement
     486        self.FB_CHARSET_CODE = 3 #UNICODE_FSS
     487        if connection.charset == 'UTF8':
     488            self.FB_CHARSET_CODE = 4 # UTF-8 with Firebird 2.0+
     489        DEFAULT_MAX_LENGTH = connection.DEFAULT_MAX_LENGTH
     490        self.cursor.set_type_trans_in({
     491            'DATE':             self.tc_dt.date_conv_in,
     492            'TIME':             self.time_conv_in,
     493            'TIMESTAMP':        self.timestamp_conv_in,
     494            'FIXED':            self.fixed_conv_in,
     495            'TEXT':             self.ascii_conv_in,
     496            'TEXT_UNICODE':     self.unicode_conv_in,
     497            'BLOB':             self.blob_conv_in
     498        })
     499        self.cursor.set_type_trans_out({
     500            'DATE':             self.tc_dt.date_conv_out,
     501            'TIME':             self.tc_dt.time_conv_out,
     502            'TIMESTAMP':        self.tc_dt.timestamp_conv_out,
     503            'FIXED':            self.tc_fd.fixed_conv_out_precise,
     504            'TEXT':             self.ascii_conv_out,
     505            'TEXT_UNICODE':     self.tc_tu.unicode_conv_out,
     506            'BLOB':             self.blob_conv_out
     507        })
     508        # Create DOMAINS
     509        domains = [ ('AutoField', 'integer'),
     510                    ('BooleanField', 'smallint CHECK (VALUE IN (0,1))'),
     511                    ('DateField', 'date'),
     512                    ('CharField', 'varchar(%i)' % DEFAULT_MAX_LENGTH),
     513                    ('DateTimeField', 'timestamp'),
     514                    ('FloatField', 'double precision'),
     515                    ('IntegerField', 'integer'),
     516                    ('IPAddressField', 'varchar(15) CHARACTER SET ASCII'),
     517                    ('NullBooleanField', 'smallint CHECK ((VALUE IN (0,1)) OR (VALUE IS NULL))'),
     518                    ('OneToOneField', 'integer'),
     519                    ('PhoneNumberField', 'varchar(20) CHARACTER SET ASCII'),
     520                    ('PositiveIntegerField', 'integer CHECK ((VALUE >= 0) OR (VALUE IS NULL))'),
     521                    ('PositiveSmallIntegerField', 'smallint CHECK ((VALUE >= 0) OR (VALUE IS NULL))'),
     522                    ('SmallIntegerField', 'smallint'),
     523                    ('TextField', 'varchar(%s)' % connection.FB_MAX_VARCHAR),
     524                    ('LargeTextField', 'blob sub_type text'),
     525                    ('TimeField', 'time'),
     526                    ('USStateField', 'varchar(2) CHARACTER SET ASCII') ]
     527        # Create domains only if they do not exist already   
     528        cursor.execute("SELECT RDB$FIELD_NAME FROM RDB$FIELDS")
     529        existing_domains = set([row[0].strip() for row in cursor.fetchall() if not row[0].startswith('RDB$')])
     530        domains = map(lambda domain: '%s "%s" AS %s;' % ('CREATE DOMAIN', domain[0], domain[1]),
     531            filter(lambda x: x[0] not in existing_domains, domains))
     532        for domain in domains:
     533            connection.execute_immediate(domain)
     534        # But alter CharField to sync with any changes in settings for use with DefaultCharField object
     535        connection.execute_immediate('ALTER DOMAIN "CharField" TYPE varchar(%i)' % DEFAULT_MAX_LENGTH)
     536       
     537        # Declare FUNCTIONS
     538        cursor.execute("SELECT RDB$FUNCTION_NAME FROM RDB$FUNCTIONS")
     539        existing_functions = set([row[0].strip().upper() for row in cursor.fetchall()])
     540        if 'RAND' not in existing_functions:
     541            connection.execute_immediate("""
     542                DECLARE EXTERNAL FUNCTION RAND
     543                RETURNS DOUBLE PRECISION
     544                BY VALUE ENTRY_POINT 'IB_UDF_rand' MODULE_NAME 'ib_udf';
     545                """)
     546 
     547    def _get_query(self):
     548        if self._statement:
     549            return self._statement.sql
     550    def _get_statement(self):
     551        if self._statement:
     552            return self._statement
     553    query = property(_get_query)
     554    statement = property(_get_statement)
     555   
     556    def execute_immediate(self, query, params=()):
     557        query = query % tuple(params)
     558        self._connection.execute_immediate(query)
     559   
     560    # Prepared Statement
     561    # http://kinterbasdb.sourceforge.net/dist_docs/usage.html#adv_prepared_statements
     562    def prepare(self, query):
     563        query.replace("%s", "?")
     564        return self.cursor.prep(query)
     565   
     566    def execute_prepared(self, statement, params):
     567        return self.cursor.execute(statement, params)
     568   
     569    def execute_straight(self, query, params=()):
     570        # Use kinterbasdb style with '?' instead of '%s'
     571        return self.cursor.execute(query, params)
     572   
     573    def execute(self, query, params=()):
     574        cquery = self.convert_query(query, len(params))
     575        if self._get_query() != cquery:
     576            try:
     577                self._statement = self.cursor.prep(cquery)
     578            except Database.ProgrammingError, e:
     579                output = ["Prepare query error."]
     580                output.extend(str(e).split("'")[1].split('\\n'))
     581                output.append("Query:")
     582                output.append(cquery)
     583                raise Database.ProgrammingError, "\n".join(output)
     584        try:
     585            return self.cursor.execute(self._statement, params)
     586        except Database.ProgrammingError, e:
     587            err_no = int(str(e).split()[0].strip(',()'))
     588            output = ["Execute query error. FB error No. %i" % err_no]
     589            output.extend(str(e).split("'")[1].split('\\n'))
     590            output.append("Query:")
     591            output.append(cquery)
     592            output.append("Parameters:")
     593            output.append(str(params))
     594            raise Database.ProgrammingError, "\n".join(output)
     595   
     596    def executemany(self, query, param_list):
     597        try:
     598            cquery = self.convert_query(query, len(param_list[0]))
     599        except IndexError:
     600            return None
     601        if self._get_query() != cquery:
     602            self._statement = self.cursor.prep(cquery)
     603        return self.cursor.executemany(self._statement, param_list)
     604
     605    def convert_query(self, query, num_params):
     606        try:
     607            return query % tuple("?" * num_params)
     608        except TypeError, e:
     609            print query, num_params
     610            raise TypeError, e
     611   
     612    def __getattr__(self, attr):
     613        if attr in self.__dict__:
     614            return self.__dict__[attr]
     615        else:
     616            return getattr(self.cursor, attr)
     617
     618class DatabaseWrapper(BaseDatabaseWrapper):
     619    features = DatabaseFeatures()
     620    ops = DatabaseOperations()
     621    operators = {
     622        'exact': '= %s',
     623        'iexact': '= UPPER(%s)',
     624        'contains': "LIKE %s ESCAPE'\\'",
     625        'icontains': 'CONTAINING %s', #case is ignored
     626        'gt': '> %s',
     627        'gte': '>= %s',
     628        'lt': '< %s',
     629        'lte': '<= %s',
     630        'startswith': 'STARTING WITH %s', #looks to be faster then LIKE
     631        'endswith': "LIKE %s ESCAPE'\\'",
     632        'istartswith': 'STARTING WITH UPPER(%s)',
     633        'iendswith': "LIKE UPPER(%s) ESCAPE'\\'"
     634    }
     635   
     636    def __init__(self, **kwargs):
     637        from django.conf import settings
     638        super(DatabaseWrapper, self).__init__(**kwargs)
     639        self. _current_cursor = None
     640        self._raw_cursor = None
     641        self.charset = 'UNICODE_FSS'
     642        self.FB_MAX_VARCHAR = 10921 #32765 MAX /3
     643        self.BYTES_PER_DEFAULT_CHAR = 3
     644        if hasattr(settings, 'FIREBIRD_CHARSET'):
     645            if settings.FIREBIRD_CHARSET == 'UTF8':
     646                self.charset = 'UTF8'
     647                self.FB_MAX_VARCHAR = 8191 #32765 MAX /4
     648                self.BYTES_PER_DEFAULT_CHAR = 4
     649        self.DEFAULT_MAX_LENGTH = 100
     650        if hasattr(settings, 'DEFAULT_MAX_LENGTH'):
     651           # You can set DEFAULT_MAX_LENGTH in settings.py and all
     652           # DefaultCharField objects will have this value automaticaly
     653           # with a little help from Firebird's domains
     654           self.DEFAULT_MAX_LENGTH = settings.DEFAULT_MAX_LENGTH 
     655   
     656       
     657    def _connect(self, settings):
     658        if settings.DATABASE_NAME == '':
     659            from django.core.exceptions import ImproperlyConfigured
     660            raise ImproperlyConfigured, "You need to specify DATABASE_NAME in your Django settings file."
     661        kwargs = {'charset' : self.charset }
     662        if settings.DATABASE_HOST:
     663            kwargs['dsn'] = "%s:%s" % (settings.DATABASE_HOST, settings.DATABASE_NAME)
     664        else:
     665            kwargs['dsn'] = "localhost:%s" % settings.DATABASE_NAME
     666        if settings.DATABASE_USER:
     667            kwargs['user'] = settings.DATABASE_USER
     668        if settings.DATABASE_PASSWORD:
     669            kwargs['password'] = settings.DATABASE_PASSWORD
     670        self.connection = Database.connect(**kwargs)
     671        assert self.connection.charset == self.charset
     672       
     673    def cursor(self):
     674        from django.conf import settings
     675        cursor = self._cursor(settings)
     676        if settings.DEBUG:
     677            self._debug_cursor = self.make_debug_cursor(cursor)
     678            return self._debug_cursor
     679        return cursor
     680   
     681    def _cursor(self, settings):
     682        if self.connection is None:
     683            self._connect(settings)
     684        cursor = self.connection.cursor()
     685        self._raw_cursor = cursor
     686        cursor = FirebirdCursorWrapper(cursor, self)
     687        self._current_cursor = cursor
     688        return cursor
     689
     690    def __getattr__(self, attr):
     691        if attr in self.__dict__:
     692            return self.__dict__[attr]
     693        else:
     694            return getattr(self.connection, attr)
     695
  • django/db/backends/firebird/client.py

     
     1from django.conf import settings
     2import os
     3
     4def runshell():
     5    args = [settings.DATABASE_NAME]
     6    args += ["-u %s" % settings.DATABASE_USER]
     7    if settings.DATABASE_PASSWORD:
     8        args += ["-p %s" % settings.DATABASE_PASSWORD]
     9    if 'FIREBIRD' not in os.environ:
     10        path = '/opt/firebird/bin/'
     11    os.system(path + 'isql ' + ' '.join(args))
  • django/db/backends/firebird/introspection.py

     
     1from django.db import transaction
     2from django.db.backends.firebird.base import DatabaseOperations
     3
     4quote_name = DatabaseOperations().quote_name
     5
     6def get_table_list(cursor):
     7    "Returns a list of table names in the current database."
     8    cursor.execute("""
     9        SELECT rdb$relation_name FROM rdb$relations
     10        WHERE rdb$system_flag = 0 AND rdb$view_blr IS NULL ORDER BY rdb$relation_name""")
     11    return [str(row[0].strip().lower()) for row in cursor.fetchall()]
     12
     13def get_table_description(cursor, table_name):
     14    "Returns a description of the table, with the DB-API cursor.description interface."
     15    #cursor.execute("SELECT FIRST 1 * FROM %s" % quote_name(table_name))
     16    #return cursor.description
     17    # (name, type_code, display_size, internal_size, precision, scale, null_ok)
     18    cursor.execute("""
     19        SELECT DISTINCT R.RDB$FIELD_NAME AS FNAME,
     20                  F.RDB$FIELD_TYPE AS FTYPE,
     21                  F.RDB$FIELD_LENGTH AS FLENGTH,
     22                  F.RDB$FIELD_PRECISION AS FPRECISION,
     23                  F.RDB$FIELD_SCALE AS FSCALE,
     24                  R.RDB$NULL_FLAG AS NULL_FLAG,
     25                  R.RDB$FIELD_POSITION
     26        FROM RDB$RELATION_FIELDS R
     27             JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME
     28        WHERE F.RDB$SYSTEM_FLAG=0 and R.RDB$RELATION_NAME= %s
     29        ORDER BY R.RDB$FIELD_POSITION
     30    """, (table_name,))
     31    return [(row[0].lower().rstrip(), row[1], row[2], row[2] or 0, row[3], row[4], row[5] and True or False) for row in cursor.fetchall()]
     32
     33
     34def get_relations(cursor, table_name):
     35    """
     36    Returns a dictionary of {field_index: (field_index_other_table, other_table)}
     37    representing all relationships to the given table. Indexes are 0-based.
     38    """
     39    cursor.execute("""
     40        SELECT seg.rdb$field_name, seg_ref.rdb$field_name, idx_ref.rdb$relation_name
     41        FROM rdb$indices idx
     42        INNER JOIN rdb$index_segments seg
     43            ON seg.rdb$index_name = idx.rdb$index_name
     44        INNER JOIN rdb$indices idx_ref
     45            ON idx_ref.rdb$index_name = idx.rdb$foreign_key
     46        INNER JOIN rdb$index_segments seg_ref
     47            ON seg_ref.rdb$index_name = idx_ref.rdb$index_name
     48        WHERE idx.rdb$relation_name = %s
     49            AND idx.rdb$foreign_key IS NOT NULL""", [table_name])
     50
     51    relations = {}
     52    for row in cursor.fetchall():
     53        relations[row[0].rstrip()] = (row[1].strip(), row[2].strip())
     54    return relations
     55
     56def get_indexes(cursor, table_name):
     57    """
     58    Returns a dictionary of fieldname -> infodict for the given table,
     59    where each infodict is in the format:
     60        {'primary_key': boolean representing whether it's the primary key,
     61         'unique': boolean representing whether it's a unique index}
     62    """
     63
     64    # This query retrieves each field name and index type on the given table.
     65    cursor.execute("""
     66        SELECT seg.rdb$field_name, const.rdb$constraint_type
     67        FROM rdb$relation_constraints const
     68        LEFT JOIN rdb$index_segments seg
     69            ON seg.rdb$index_name = const.rdb$index_name
     70        WHERE const.rdb$relation_name = %s
     71            AND (const.rdb$constraint_type = 'PRIMARY KEY'
     72                OR const.rdb$constraint_type = 'UNIQUE')""", [table_name])
     73    indexes = {}
     74    for row in cursor.fetchall():
     75        indexes[row[0].strip()] = {
     76            'primary_key': ('PRIMARY KEY' == row[1].strip()),
     77            'unique': ('UNIQUE' == row[1].strip())}
     78    return indexes
     79
     80# Maps type codes to Django Field types.
     81# !todo
     82DATA_TYPES_REVERSE = {
     83    7: 'BooleanField',
     84    7: 'SmallIntegerField',
     85    8: 'IntegerField',
     86    261: 'TextField',
     87    37: 'IPAddressField',
     88    37: 'CharField',
     89    12: 'DateField',
     90    13: 'TimeField',
     91    35: 'DateTimeField',
     92    10: 'FloatField',
     93}
  • django/db/backends/firebird/creation.py

     
     1# This dictionary maps Field objects to their associated Firebird column
     2# types, as strings. Column-type strings can contain format strings; they'll
     3# be interpolated against the values of Field.__dict__ before being output.
     4# If a column type is set to None, it won't be included in the output.
     5
     6import sys, os.path, codecs
     7from kinterbasdb import connect, create_database
     8from django.core.management import call_command
     9from django.conf import settings
     10from django.db import connection
     11
     12
     13
     14DATA_TYPES = {
     15    'AutoField':                     '"AutoField"',
     16    'BooleanField':                  '"BooleanField"',
     17    'CharField':                     'varchar(%(max_length)s)',
     18    'CommaSeparatedIntegerField':    'varchar(%(max_length)s) CHARACTER SET ASCII',
     19    'DateField':                     '"DateField"',
     20    'DateTimeField':                 '"DateTimeField"',
     21    'DecimalField':                  'numeric(%(max_digits)s, %(decimal_places)s)',
     22    'DefaultCharField':              '"CharField"',
     23    'FileField':                     'varchar(%(max_length)s)',
     24    'FilePathField':                 'varchar(%(max_length)s)',
     25    'FloatField':                    '"FloatField"',
     26    'ImageField':                    '"varchar(%(max_length)s)"',
     27    'IntegerField':                  '"IntegerField"',
     28    'IPAddressField':                'varchar(15) CHARACTER SET ASCII',
     29    'NullBooleanField':              '"NullBooleanField"',
     30    'OneToOneField':                 '"OneToOneField"',
     31    'PhoneNumberField':              '"PhoneNumberField"',
     32    'PositiveIntegerField':          '"PositiveIntegerField"',
     33    'PositiveSmallIntegerField':     '"PositiveSmallIntegerField"',
     34    'SlugField':                     'varchar(%(max_length)s)',
     35    'SmallIntegerField':             '"SmallIntegerField"',
     36    'LargeTextField':                '"LargeTextField"',
     37    'TextField':                     '"TextField"',
     38    'TimeField':                     '"TimeField"',
     39    'URLField':                      'varchar(%(max_length)s) CHARACTER SET ASCII',
     40    'USStateField':                  '"USStateField"'
     41}
     42     
     43PYTHON_TO_FB_ENCODING_MAP = {
     44    'ascii':        'ASCII',
     45    'utf_8':        connection.charset,
     46    'shift_jis':    'SJIS_0208',
     47    'euc_jp':       'EUCJ_0208',
     48    'cp737':        'DOS737',
     49    'cp437':        'DOS437',
     50    'cp850':        'DOS850',
     51    'cp865':        'DOS865',
     52    'cp860':        'DOS860',
     53    'cp863':        'DOS863',
     54    'cp775':        'DOS775',
     55    'cp862':        'DOS862',
     56    'cp864':        'DOS864',
     57    'iso8859_1':    'ISO8859_1',
     58    'iso8859_2':    'ISO8859_2',
     59    'iso8859_3':    'ISO8859_3',
     60    'iso8859_4':    'ISO8859_4',
     61    'iso8859_5':    'ISO8859_5',
     62    'iso8859_6':    'ISO8859_6',
     63    'iso8859_7':    'ISO8859_7',
     64    'iso8859_8':    'ISO8859_8',
     65    'iso8859_9':    'ISO8859_9',
     66    'iso8859_13':   'ISO8859_13',
     67    'euc_kr':       'KSC_5601',
     68    'cp852':        'DOS852',
     69    'cp857':        'DOS857',
     70    'cp861':        'DOS861',
     71    'cp866':        'DOS866',
     72    'cp869':        'DOS869',
     73    'cp1250':       'WIN1250',
     74    'cp1251':       'WIN1251',
     75    'cp1252':       'WIN1252',
     76    'cp1253':       'WIN1253',
     77    'cp1254':       'WIN1254',
     78    'big5':         'BIG_5',
     79    'gb2312':       'GB_2312',
     80    'cp1255':       'WIN1255',
     81    'cp1256':       'WIN1256',
     82    'cp1257':       'WIN1257',
     83    'koi8_r':       'KOI8-R',
     84    'koi8_u':       'KOI8-U',
     85    'cp1258':       'WIN1258'
     86  }
     87
     88def get_data_size(data_type, max_length = 100):
     89    char_bytes = connection.BYTES_PER_DEFAULT_CHAR
     90    size_map = {
     91        'AutoField':                     8,
     92        'BooleanField':                  4,
     93        'CharField':                     char_bytes*max_length,
     94        'CommaSeparatedIntegerField':    max_length,
     95        'DateField':                     16,
     96        'DateTimeField':                 16,
     97        'DecimalField':                  16,
     98        'FileField':                     char_bytes*max_length,
     99        'FilePathField':                 'varchar(%(max_length)s)',
     100        'FloatField':                    16,
     101        'ImageField':                    char_bytes*max_length,
     102        'IntegerField':                  8,
     103        'IPAddressField':                15,
     104        'NullBooleanField':              4,
     105        'OneToOneField':                 8,
     106        'PhoneNumberField':              20,
     107        'PositiveIntegerField':          8,
     108        'PositiveSmallIntegerField':     4,
     109        'SlugField':                     char_bytes*max_length,
     110        'SmallIntegerField':             4,
     111        'TextBlob':                      8,
     112        'TextField':                     32767,
     113        'TimeField':                     16,
     114        'URLField':                      max_length,
     115        'USStateField':                  char_bytes*2
     116    }
     117    return size_map[data_type]
     118
     119def sql_model_create(model, style, known_models=set()):
     120    """
     121    Returns the SQL required to create a single model, as a tuple of:
     122        (list_of_sql, pending_references_dict)
     123    """
     124    from django.db import connection, models
     125
     126    opts = model._meta
     127    final_output = []
     128    table_output = []
     129    pending_references = {}
     130    qn = connection.ops.quote_name
     131    inline_references = connection.features.inline_fk_references
     132    # Check that row size is less than 64k and adjust TextFields if needed
     133    row_size = 0
     134    columns = [(f.db_type().strip('"'), f.get_internal_type(), f) for f in opts.fields]
     135    columns_simple = [col[0] for col in columns]
     136    text_field_type = '"TextField"'
     137    max_alowed_bytes = 32765
     138    if 'TextField' in columns_simple:
     139        max_length = 100
     140        num_text_fields = 0
     141        for column in columns:
     142            num_text_fields += (column[0] == 'TextField')
     143            if column[0].startswith('varchar'):
     144                max_length = int(column[0].split('(')[1].split(')')[0])
     145            if column[1] in DATA_TYPES:
     146                row_size += get_data_size(column[1], max_length)
     147        if row_size > 65536:
     148            max_alowed_bytes = int( (max_alowed_bytes/num_text_fields) - (row_size-65536) )
     149            n = max_alowed_bytes / connection.BYTES_PER_DEFAULT_CHAR
     150            if n > 512:
     151                text_field_type = 'varchar(%s)' % n
     152                FB_TEXTFIELD_ALTERED = True   
     153                print
     154                print "WARNING: Maximum number of characters in TextFields has changed to %s." % n
     155                print "         TextField columns with custom charsets will have %s chars available" % max_alowed_bytes
     156                print "         The change affects %s table only." % opts.db_table
     157                print "         TextFields in other tables will have %s characters maximum" % connection.FB_MAX_VARCHAR
     158                print "         or 32765 characters with custom (non-UTF) encoding."
     159                print "         If you need more space in those fields try LargeTextFields instead."
     160                print
     161            else:
     162                # Swich to blobs if size is too small (<1024)   
     163                text_field_type = '"LargeTextField"'   
     164    for f in opts.fields:
     165        col_type = f.db_type()
     166
     167        if col_type.strip('"') == 'TextField':
     168            col_type = text_field_type
     169        fb_version = "%s.%s" % (connection.ops.firebird_version[0], connection.ops.firebird_version[1])
     170        page_size = connection.ops.page_size
     171        #look at: http://www.volny.cz/iprenosil/interbase/ip_ib_indexcalculator.htm
     172        if connection.ops.index_limit < 1000:
     173            strip2ascii = False
     174            custom_charset = False
     175            if col_type.startswith('varchar'):
     176                if (f.unique or f.primary_key or f.db_index):
     177                    length = f.max_length
     178                    if f.encoding:
     179                        if not f.encoding.upper().startswith('UTF'):
     180                            custom_charset = True
     181                    if not custom_charset:
     182                        try:
     183                            length = f.max_length * connection.BYTES_PER_DEFAULT_CHAR
     184                        except TypeError:
     185                            length = 100*connection.BYTES_PER_DEFAULT_CHAR #Default for CharField
     186                    if length >= connection.ops.index_limit:   
     187                        strip2ascii = True
     188                if len(opts.unique_together) > 0:
     189                    if f.column in opts.unique_together[0]:
     190                        num_unique_char_fields = len([ fld for fld in opts.unique_together[0] if opts.get_field(fld).db_type().startswith('varchar') ])
     191                        num_unique_fileds = len(opts.unique_together[0])
     192                        num_unique_nonchar_fileds = num_unique_fileds - num_unique_char_fields
     193                        limit = connection.ops.index_limit
     194                        limit -= ((num_unique_fileds - 1)*64)
     195                        limit -= 8*num_unique_nonchar_fileds
     196                        max_length = limit/num_unique_char_fields
     197                        ascii_length = int(f.max_length)
     198                        old_length = ascii_length*connection.BYTES_PER_DEFAULT_CHAR
     199                         
     200                        if (old_length >= max_length) and (ascii_length < max_length):
     201                            strip2ascii = True
     202                        elif old_length > max_length:
     203                            strip2ascii = False #We change it here
     204                            col_type = "varchar(%i) CHARACTER SET ASCII" % max_length
     205                            msg =  "WARNING: Character set of the '%s' field\n"
     206                            msg += "         (table %s)\n"
     207                            msg += "         has changed to ASCII"
     208                            msg += " to fit %s-byte limit in FB %s"
     209                            if not page_size:
     210                                print  msg % (f.column, opts.db_table, connection.ops.index_limit, fb_version)
     211                            else:
     212                                msg += " with page size %s"
     213                                print  msg % (f.column, opts.db_table, connection.ops.index_limit, fb_version, page_size)
     214                            print "         The maximum length of '%s' is now %s instead of %s"\
     215                             % (f.column, max_length, old_length)     
     216            if strip2ascii:
     217                col_type = "%s %s %s" % (col_type, "CHARACTER SET", "ASCII")
     218                msg =  "WARNING: Character set of the '%s' field\n"
     219                msg += "         (table %s)\n"
     220                msg += "         has changed to ASCII"
     221                msg += " to fit %s-byte limit in FB %s"
     222                if not page_size:
     223                    print  msg % (f.column, opts.db_table, connection.ops.index_limit, fb_version)
     224                else:
     225                    msg += " with page size %s"
     226                    print  msg % (f.column, opts.db_table, connection.ops.index_limit, fb_version, page_size)
     227                   
     228        if (col_type.startswith('varchar') or col_type.strip('"') == 'TextField') and f.encoding:
     229            charset = PYTHON_TO_FB_ENCODING_MAP[codecs.lookup(f.encoding).name]
     230            if col_type.strip('"') == 'TextField':
     231                col_type = 'varchar(%i)' % max_alowed_bytes
     232            col_type = "%s %s %s" % (col_type, "CHARACTER SET", charset)
     233 
     234           
     235        if col_type is None:
     236            # Skip ManyToManyFields, because they're not represented as
     237            # database columns in this table.
     238            continue
     239        # Make the definition (e.g. 'foo VARCHAR(30)') for this field.
     240        field_output = [style.SQL_FIELD(qn(f.column)),
     241            style.SQL_COLTYPE(col_type)]
     242        field_output.append(style.SQL_KEYWORD('%s' % (not f.null and 'NOT NULL' or 'DEFAULT NULL')))
     243        if f.unique:
     244            field_output.append(style.SQL_KEYWORD('UNIQUE'))
     245        if f.primary_key:
     246            field_output.append(style.SQL_KEYWORD('PRIMARY KEY'))
     247        if f.rel:
     248            # We haven't yet created the table to which this field
     249            # is related, so save it for later.
     250            pr = pending_references.setdefault(f.rel.to, []).append((model, f))
     251        table_output.append(' '.join(field_output))
     252    if opts.order_with_respect_to:
     253        table_output.append(style.SQL_FIELD(qn('_order')) + ' ' + \
     254            style.SQL_COLTYPE(models.IntegerField().db_type()) + ' ' + \
     255            style.SQL_KEYWORD('NULL'))
     256    for field_constraints in opts.unique_together:
     257        table_output.append(style.SQL_KEYWORD('UNIQUE') + ' (%s)' % \
     258            ", ".join([qn(style.SQL_FIELD(opts.get_field(f).column)) for f in field_constraints]))
     259
     260    full_statement = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + style.SQL_TABLE(qn(opts.db_table)) + ' (']
     261    for i, line in enumerate(table_output): # Combine and add commas.
     262        full_statement.append('    %s%s' % (line, i < len(table_output)-1 and ',' or ''))
     263    full_statement.append(');')
     264    final_output.append('\n'.join(full_statement))
     265
     266    if opts.has_auto_field:
     267        # Add any extra SQL needed to support auto-incrementing primary keys.
     268        auto_column = opts.auto_field.db_column or opts.auto_field.name
     269        autoinc_sql = connection.ops.autoinc_sql(style, opts.db_table, auto_column)
     270        if autoinc_sql:
     271            for stmt in autoinc_sql:
     272                final_output.append(stmt)
     273
     274    return final_output, pending_references
     275
     276def many_to_many_sql_for_model(model, style):
     277    from django.db import connection, models
     278    from django.contrib.contenttypes import generic
     279    from django.db.backends.util import truncate_name
     280
     281    opts = model._meta
     282    final_output = []
     283    qn = connection.ops.quote_name
     284    for f in opts.many_to_many:
     285        if not isinstance(f.rel, generic.GenericRel):
     286            table_output = [style.SQL_KEYWORD('CREATE TABLE') + ' ' + \
     287                style.SQL_TABLE(qn(f.m2m_db_table())) + ' (']
     288            table_output.append('    %s %s %s,' %
     289                (style.SQL_FIELD(qn('id')),
     290                style.SQL_COLTYPE(models.AutoField(primary_key=True).db_type()),
     291                style.SQL_KEYWORD('NOT NULL PRIMARY KEY')))
     292
     293            table_output.append('    %s %s %s,' %
     294                (style.SQL_FIELD(qn(f.m2m_column_name())),
     295                style.SQL_COLTYPE(models.ForeignKey(model).db_type()),
     296                style.SQL_KEYWORD('NOT NULL')))
     297            table_output.append('    %s %s %s,' %
     298                (style.SQL_FIELD(qn(f.m2m_reverse_name())),
     299                style.SQL_COLTYPE(models.ForeignKey(f.rel.to).db_type()),
     300                style.SQL_KEYWORD('NOT NULL')))
     301            deferred = [
     302                (f.m2m_db_table(), f.m2m_column_name(), opts.db_table,
     303                    opts.pk.column),
     304                ( f.m2m_db_table(), f.m2m_reverse_name(),
     305                    f.rel.to._meta.db_table, f.rel.to._meta.pk.column)
     306                ]
     307
     308            table_output.append('    %s (%s, %s)' %
     309                (style.SQL_KEYWORD('UNIQUE'),
     310                style.SQL_FIELD(qn(f.m2m_column_name())),
     311                style.SQL_FIELD(qn(f.m2m_reverse_name()))))
     312            table_output.append(');')
     313            final_output.append('\n'.join(table_output))
     314           
     315            autoinc_sql = connection.ops.autoinc_sql(style, f.m2m_db_table(), 'id')
     316            if autoinc_sql:
     317                for stmt in autoinc_sql:
     318                    final_output.append(stmt)
     319           
     320            if connection.features.supports_constraints:
     321                for r_table, r_col, table, col in deferred:
     322                    r_name = '%s_%s_%x' % (r_col, col,
     323                            abs(hash((r_table, table))))
     324                    final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' %
     325                    (qn(r_table),
     326                    truncate_name(r_name, connection.ops.max_name_length()),
     327                    qn(r_col), qn(table), qn(col),
     328                    'ON DELETE CASCADE ON UPDATE CASCADE'))
     329
     330    return final_output
     331
     332TEST_DATABASE_PREFIX = 'test_'
     333def create_test_db(settings, connection, verbosity, autoclobber):
     334    # KInterbasDB supports dynamic database creation and deletion
     335    # via the module-level function create_database and the method Connection.drop_database.
     336       
     337    if settings.TEST_DATABASE_NAME:
     338        TEST_DATABASE_NAME = settings.TEST_DATABASE_NAME
     339    else:
     340        dbnametuple = os.path.split(settings.DATABASE_NAME)
     341        TEST_DATABASE_NAME = os.path.join(dbnametuple[0], TEST_DATABASE_PREFIX + dbnametuple[1])
     342   
     343    dsn = "localhost:%s" % TEST_DATABASE_NAME
     344    if settings.DATABASE_HOST:
     345        dsn = "%s:%s" % (settings.DATABASE_HOST, TEST_DATABASE_NAME)
     346   
     347    if os.path.isfile(TEST_DATABASE_NAME):
     348        sys.stderr.write("Database %s already exists\n" % TEST_DATABASE_NAME)
     349        if not autoclobber:
     350            confirm = raw_input("Type 'yes' if you would like to try deleting the test database '%s', or 'no' to cancel: " % TEST_DATABASE_NAME)
     351        if autoclobber or confirm == 'yes':
     352            if verbosity >= 1:
     353                print "Destroying old test database..."
     354            old_connection = connect(dsn=dsn, user=settings.DATABASE_USER, password=settings.DATABASE_PASSWORD)
     355            old_connection.drop_database()
     356        else:
     357                print "Tests cancelled."
     358                sys.exit(1)
     359           
     360    if verbosity >= 1:
     361        print "Creating test database..."
     362    try:
     363        charset = 'UNICODE_FSS'
     364        if hasattr(settings, 'FIREBIRD_CHARSET'):
     365            if settings.FIREBIRD_CHARSET == 'UTF8':
     366                charset='UTF8'               
     367        create_database("create database '%s' user '%s' password '%s' default character set %s" % \
     368            (dsn, settings.DATABASE_USER, settings.DATABASE_PASSWORD, charset))
     369    except Exception, e:
     370        sys.stderr.write("Got an error creating the test database: %s\n" % e)
     371        sys.exit(2)
     372
     373    connection.close()
     374    settings.DATABASE_NAME = TEST_DATABASE_NAME
     375
     376    call_command('syncdb', verbosity=verbosity, interactive=False)
     377
     378    if settings.CACHE_BACKEND.startswith('db://'):
     379        cache_name = settings.CACHE_BACKEND[len('db://'):]
     380        call_command('createcachetable', cache_name)
     381
     382    # Get a cursor (even though we don't need one yet). This has
     383    # the side effect of initializing the test database.
     384    cursor = connection.cursor()
     385
     386    return TEST_DATABASE_NAME
     387
     388def destroy_test_db(settings, connection, old_database_name, verbosity):
     389    # KInterbasDB supports dynamic database deletion via the method Connection.drop_database.
     390    if verbosity >= 1:
     391        print "Destroying test database..."
     392    connection.drop_database()
     393   
     394
  • django/core/cache/backends/db.py

     
    4747        if timeout is None:
    4848            timeout = self.default_timeout
    4949        cursor = connection.cursor()
    50         cursor.execute("SELECT COUNT(*) FROM %s" % self._table)
     50        qn = connection.ops.quote_name
     51        cursor.execute("SELECT COUNT(*) FROM %s" % qn(self._table))
    5152        num = cursor.fetchone()[0]
    5253        now = datetime.now().replace(microsecond=0)
    5354        exp = datetime.fromtimestamp(time.time() + timeout).replace(microsecond=0)
    5455        if num > self._max_entries:
    5556            self._cull(cursor, now)
    5657        encoded = base64.encodestring(pickle.dumps(value, 2)).strip()
    57         cursor.execute("SELECT cache_key FROM %s WHERE cache_key = %%s" % self._table, [key])
     58        cursor.execute("SELECT cache_key FROM %s WHERE cache_key = %%s" % qn(self._table), [key])
    5859        try:
    5960            if mode == 'set' and cursor.fetchone():
    60                 cursor.execute("UPDATE %s SET value = %%s, expires = %%s WHERE cache_key = %%s" % self._table, [encoded, str(exp), key])
     61                cursor.execute("UPDATE %s SET value = %%s, expires = %%s WHERE cache_key = %%s" % qn(self._table), [encoded, str(exp), key])
    6162            else:
    62                 cursor.execute("INSERT INTO %s (cache_key, value, expires) VALUES (%%s, %%s, %%s)" % self._table, [key, encoded, str(exp)])
     63                cursor.execute("INSERT INTO %s (cache_key, value, expires) VALUES (%%s, %%s, %%s)" % qn(self._table), [key, encoded, str(exp)])
    6364        except DatabaseError:
    6465            # To be threadsafe, updates/inserts are allowed to fail silently
    6566            pass
  • django/core/management/sql.py

     
    11from django.core.management.base import CommandError
     2from django.conf import settings
    23import os
    34import re
    45
     
    245246    Returns the SQL required to create a single model, as a tuple of:
    246247        (list_of_sql, pending_references_dict)
    247248    """
    248     from django.db import connection, models
    249 
     249    from django.db import connection, models, get_creation_module
     250    creation_module = get_creation_module()
     251    # If the database backend wants to create model itself, let it
     252    if hasattr(creation_module, "sql_model_create"):
     253        return creation_module.sql_model_create(model, style, known_models)
    250254    opts = model._meta
    251255    final_output = []
    252256    table_output = []
     
    332336                # For MySQL, r_name must be unique in the first 64 characters.
    333337                # So we are careful with character usage here.
    334338                r_name = '%s_refs_%s_%x' % (r_col, col, abs(hash((r_table, table))))
    335                 final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % \
     339                final_output.append(style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s%s;' % \
    336340                    (qn(r_table), truncate_name(r_name, connection.ops.max_name_length()),
    337341                    qn(r_col), qn(table), qn(col),
     342                    connection.ops.cascade_delete_update_sql(),
    338343                    connection.ops.deferrable_sql()))
    339344            del pending_references[model]
    340345    return final_output
    341346
    342347def many_to_many_sql_for_model(model, style):
    343     from django.db import connection, models
     348    from django.db import connection, models, get_creation_module
    344349    from django.contrib.contenttypes import generic
    345350    from django.db.backends.util import truncate_name
    346 
     351   
     352    creation_module = get_creation_module()
     353    # If the database backend wants to create many_to_many sql itself, let it
     354    if hasattr(creation_module, "many_to_many_sql_for_model"):
     355        return creation_module.many_to_many_sql_for_model(model, style)
     356       
    347357    opts = model._meta
    348358    final_output = []
    349359    qn = connection.ops.quote_name
  • tests/modeltests/custom_methods/models.py

     
    2727        """
    2828        from django.db import connection
    2929        cursor = connection.cursor()
     30        # Some backends really really need quotes!
    3031        cursor.execute("""
    31             SELECT id, headline, pub_date
    32             FROM custom_methods_article
    33             WHERE pub_date = %s
    34                 AND id != %s""", [str(self.pub_date), self.id])
     32            SELECT "id", "headline", "pub_date"
     33            FROM "custom_methods_article"
     34            WHERE "pub_date" = %s
     35                AND "id" != %s""", [str(self.pub_date), self.id])
    3536        # The asterisk in "(*row)" tells Python to expand the list into
    3637        # positional arguments to Article().
    3738        return [self.__class__(*row) for row in cursor.fetchall()]
    38 
     39   
    3940__test__ = {'API_TESTS':"""
    4041# Create a couple of Articles.
    4142>>> from datetime import date
  • tests/modeltests/lookup/models.py

     
    274274>>> a4.save()
    275275>>> a5 = Article(pub_date=now, headline='hey-Foo')
    276276>>> a5.save()
     277"""}
    277278
     279# Firebird support 'magic values'
     280if settings.DATABASE_ENGINE in ('firebird',):
     281    __test__['API_TESTS'] += r"""
     282# and yet more:
     283>>> a10 = Article(pub_date='today', headline='foobar')
     284>>> a10.save()
     285>>> a11 = Article(pub_date='tomorrow', headline='foobaz')
     286>>> a11.save()
     287>>> a12 = Article(pub_date='yesterday', headline='ooF')
     288>>> a12.save()
     289>>> a13 = Article(pub_date='now', headline='foobarbaz')
     290>>> a13.save()
     291>>> a14 = Article(pub_date='today', headline='zoocarfaz')
     292>>> a14.save()
     293>>> a15 = Article(pub_date='today', headline='barfoobaz')
     294>>> a15.save()
     295>>> a16 = Article(pub_date='today', headline='bazbaRFOO')
     296>>> a16.save()
     297>>> Article.objects.filter(pub_date__exact='yesterday')
     298[<Article: ooF>]
     299"""
     300
     301
     302# Firebird doesn't support regular expression lookups
     303if settings.DATABASE_ENGINE not in ('firebird',):
     304    __test__['API_TESTS'] += r"""
    278305# zero-or-more
    279306>>> Article.objects.filter(headline__regex=r'fo*')
    280307[<Article: f>, <Article: fo>, <Article: foo>, <Article: fooo>]
     
    348375[<Article: barfoobaz>, <Article: baz>, <Article: bazbaRFOO>, <Article: foobarbaz>, <Article: foobaz>]
    349376>>> Article.objects.filter(headline__iregex=r'b.*ar')
    350377[<Article: bar>, <Article: barfoobaz>, <Article: bazbaRFOO>, <Article: foobar>, <Article: foobarbaz>]
    351 """}
     378"""
    352379
    353380
    354 if settings.DATABASE_ENGINE not in ('mysql', 'mysql_old'):
     381if settings.DATABASE_ENGINE not in ('mysql', 'mysql_old', 'firebird'):
    355382    __test__['API_TESTS'] += r"""
    356383# grouping and backreferences
    357384>>> Article.objects.filter(headline__regex=r'b(.).*b\1')
  • tests/regressiontests/serializers_regress/tests.py

     
    2222    import decimal
    2323except ImportError:
    2424    from django.utils import _decimal as decimal
     25try:
     26    set
     27except NameError:
     28    from sets import Set as set   # Python 2.3 fallback
    2529
    2630# A set of functions that can be used to recreate
    2731# test data objects of various kinds.
     
    8387    testcase.assertEqual(data, instance.data_id)
    8488
    8589def m2m_compare(testcase, pk, klass, data):
     90    # Use sets to ignore order of data
    8691    instance = klass.objects.get(id=pk)
    87     testcase.assertEqual(data, [obj.id for obj in instance.data.all()])
     92    testcase.assertEqual(set(data), set([obj.id for obj in instance.data.all()]))
    8893
    8994def o2o_compare(testcase, pk, klass, data):
    9095    instance = klass.objects.get(data=data)
  • tests/regressiontests/backends/models.py

     
    2222>>> opts = Square._meta
    2323>>> f1, f2 = opts.get_field('root'), opts.get_field('square')
    2424>>> query = ('INSERT INTO %s (%s, %s) VALUES (%%s, %%s)'
    25 ...         % (t_convert(opts.db_table), qn(f1.column), qn(f2.column)))
     25...         % ((qn(t_convert(opts.db_table)), qn(f1.column), qn(f2.column))))
    2626>>> cursor.executemany(query, [(i, i**2) for i in range(-5, 6)]) and None or None
    2727>>> Square.objects.order_by('root')
    2828[<Square: -5 ** 2 == 25>, <Square: -4 ** 2 == 16>, <Square: -3 ** 2 == 9>, <Square: -2 ** 2 == 4>, <Square: -1 ** 2 == 1>, <Square: 0 ** 2 == 0>, <Square: 1 ** 2 == 1>, <Square: 2 ** 2 == 4>, <Square: 3 ** 2 == 9>, <Square: 4 ** 2 == 16>, <Square: 5 ** 2 == 25>]
Back to Top