Fixed #22001 -- Ensure db_type is respected.

db_parameters should respect an already existing db_type method and
return that as its type string. In particular, this was causing some
fields from gis to not be generated.

Thanks to @bigsassy and @blueyed for their work on the patch.

Also fixed #22260
This commit is contained in:
Marc Tamlyn 2014-03-14 22:18:20 +00:00
parent 37f7f233f5
commit d22b291890
8 changed files with 105 additions and 15 deletions

View File

@ -147,6 +147,7 @@ answer newbie questions, and generally made Django that much better:
Ricardo Javier Cárdenes Medina <ricardo.cardenes@gmail.com>
Jeremy Carbaugh <jcarbaugh@gmail.com>
Graham Carlyle <graham.carlyle@maplecroft.net>
Eric Palakovich Carr <carreric@gmail.com>
Juan Catalano <catalanojuan@gmail.com>
Antonio Cavedoni <http://cavedoni.com/>
cedric@terramater.net

View File

@ -0,0 +1,33 @@
from django.db import models, migrations
import django.contrib.gis.db.models.fields
# Used for regression test of ticket #22001: https://code.djangoproject.com/ticket/22001
class Migration(migrations.Migration):
operations = [
migrations.CreateModel(
name='Neighborhood',
fields=[
(u'id', models.AutoField(verbose_name=u'ID', serialize=False, auto_created=True, primary_key=True)),
('name', models.TextField(unique=True)),
('geom', django.contrib.gis.db.models.fields.MultiPolygonField(srid=4326, null=True)),
],
options={
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Household',
fields=[
(u'id', models.AutoField(verbose_name=u'ID', serialize=False, auto_created=True, primary_key=True)),
('neighborhood', models.ForeignKey(to='gis.Neighborhood', to_field=u'id', null=True)),
('address', models.TextField()),
('zip_code', models.IntegerField(null=True, blank=True)),
('geom', django.contrib.gis.db.models.fields.PointField(srid=4326, null=True, geography=True)),
],
options={
},
bases=(models.Model,),
)
]

View File

@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.core.management import call_command
from django.db import connection
from django.test import override_settings, override_system_checks, TransactionTestCase
class MigrateTests(TransactionTestCase):
"""
Tests running the migrate command in Geodjango.
"""
available_apps = ["django.contrib.gis"]
def get_table_description(self, table):
with connection.cursor() as cursor:
return connection.introspection.get_table_description(cursor, table)
def assertTableExists(self, table):
with connection.cursor() as cursor:
self.assertIn(table, connection.introspection.get_table_list(cursor))
def assertTableNotExists(self, table):
with connection.cursor() as cursor:
self.assertNotIn(table, connection.introspection.get_table_list(cursor))
@override_system_checks([])
@override_settings(MIGRATION_MODULES={"gis": "django.contrib.gis.tests.migrations.migrations"})
def test_migrate_gis(self):
"""
Tests basic usage of the migrate command when a model uses Geodjango
fields. Regression test for ticket #22001:
https://code.djangoproject.com/ticket/22001
"""
# Make sure no tables are created
self.assertTableNotExists("migrations_neighborhood")
self.assertTableNotExists("migrations_household")
# Run the migrations to 0001 only
call_command("migrate", "gis", "0001", verbosity=0)
# Make sure the right tables exist
self.assertTableExists("gis_neighborhood")
self.assertTableExists("gis_household")
# Unmigrate everything
call_command("migrate", "gis", "zero", verbosity=0)
# Make sure it's all gone
self.assertTableNotExists("gis_neighborhood")
self.assertTableNotExists("gis_household")

View File

@ -511,7 +511,7 @@ class Field(RegisterLookupMixin):
connection.
"""
# The default implementation of this method looks at the
# backend-specific DATA_TYPES dictionary, looking up the field by its
# backend-specific data_types dictionary, looking up the field by its
# "internal type".
#
# A Field class can implement the get_internal_type() method to specify
@ -525,24 +525,20 @@ class Field(RegisterLookupMixin):
# mapped to one of the built-in Django field types. In this case, you
# can implement db_type() instead of get_internal_type() to specify
# exactly which wacky database column type you want to use.
params = self.db_parameters(connection)
if params['type']:
if params['check']:
return "%s CHECK (%s)" % (params['type'], params['check'])
else:
return params['type']
return None
data = DictWrapper(self.__dict__, connection.ops.quote_name, "qn_")
try:
return connection.creation.data_types[self.get_internal_type()] % data
except KeyError:
return None
def db_parameters(self, connection):
"""
Replacement for db_type, providing a range of different return
values (type, checks)
Extension of db_type(), providing a range of different return
values (type, checks).
This will look at db_type(), allowing custom model fields to override it.
"""
data = DictWrapper(self.__dict__, connection.ops.quote_name, "qn_")
try:
type_string = connection.creation.data_types[self.get_internal_type()] % data
except KeyError:
type_string = None
type_string = self.db_type(connection)
try:
check_string = connection.creation.data_type_check_constraints[self.get_internal_type()] % data
except KeyError:

View File

@ -74,3 +74,8 @@ class JSONField(six.with_metaclass(models.SubfieldBase, models.TextField)):
if value is None:
return None
return json.dumps(value)
class CustomTypedField(models.TextField):
def db_type(self, connection):
return 'custom_field'

View File

@ -3,9 +3,10 @@ from __future__ import unicode_literals
import inspect
from django.core import serializers
from django.db import connection
from django.test import TestCase
from .fields import Small
from .fields import Small, CustomTypedField
from .models import DataModel, MyModel, OtherModel
@ -104,3 +105,10 @@ class CustomField(TestCase):
data = dict(inspect.getmembers(MyModel))
self.assertIn('__module__', data)
self.assertEqual(data['__module__'], 'field_subclassing.models')
class TestDbType(TestCase):
def test_db_parameters_respects_db_type(self):
f = CustomTypedField()
self.assertEqual(f.db_parameters(connection)['type'], 'custom_field')