1
votes

All,

I have several custom fields on my models. I have tried adding "south_field_triple" methods on them in order to get db migration working. Initializing my application with south ("python manage.py convert_so_south myApp") works. But generating the first migration ("python manage.py schemamigration myApp --auto") fails with the following error:

TypeError: type() takes 1 or 3 arguments

The problem is occurring with the following field (I am not showing the code for the custom FormField or Widget; I assume those are not related to the cause of the problem):

class MyCustomField(models.CharField):

    _type = "EnumerationField"

    enumerationAppName    = ""
    enumerationModelName  = ""

    def __init__(self,*args,**kwargs):
        enumeration = kwargs.pop('enumeration',None)
        super(MyCustomField,self).__init__(*args,**kwargs)

        if enumeration:
            (self.enumerationAppName, self.enumerationModelName) =  enumeration.split(".")

    def getEnumeration(self):
        try:
            app = get_app(self.enumerationAppName)
            enumeration = getattr(app,self.enumerationModelName)
            return enumeration
        except:
            msg = "failed to get enumeration '%s.%s'" % (self.enumerationAppName,self.enumerationModelName)
            print "error: %s" % msg
            return None

    def south_field_triple(self):
        field_class_path = self.__class__.__module__ + "." + self.__class__.__name__
        args,kwargs = introspector(self)
        return (field_class_path,args,kwargs)

For what it's worth, this field presents the user with a set of choices. Those choices are defined in another class (specified by the "enumeration" argument to init). The FormField and Widget associated with this field use MultiValueField and MultiWidget, respectively, to present the user with a combo-box and a separate text box where the user can enter their own custom value not in the original enumeration. However, in the case of the model in the application being migrated - an enumeration is not provided.

Any ideas on what's gone wrong? Thanks.

edit: stacktrace added

  File "manage.py", line 11, in <module>
    execute_manager(settings)
  File "/usr/local/lib/python2.7/site-packages/Django-1.4-py2.7.egg/django/core/management/__init__.py", line 459, in execute_manager
    utility.execute()
  File "/usr/local/lib/python2.7/site-packages/Django-1.4-py2.7.egg/django/core/management/__init__.py", line 382, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python2.7/site-packages/django/core/management/base.py", line 196, in run_from_argv
    self.execute(*args, **options.__dict__)
  File "/usr/local/lib/python2.7/site-packages/django/core/management/base.py", line 232, in execute
    output = self.handle(*args, **options)
  File "/usr/local/lib/python2.7/site-packages/south/management/commands/schemamigration.py", line 97, in handle
    old_orm = last_migration.orm(),
  File "/usr/local/lib/python2.7/site-packages/south/utils.py", line 62, in method
    value = function(self)
  File "/usr/local/lib/python2.7/site-packages/south/migration/base.py", line 422, in orm
    return FakeORM(self.migration_class(), self.app_label())
  File "/usr/local/lib/python2.7/site-packages/south/orm.py", line 46, in FakeORM
    _orm_cache[args] = _FakeORM(*args)  
  File "/usr/local/lib/python2.7/site-packages/south/orm.py", line 126, in __init__
    self.models[name] = self.make_model(app_label, model_name, data)
  File "/usr/local/lib/python2.7/site-packages/south/orm.py", line 320, in make_model
    field = self.eval_in_context(code, app, extra_imports)
  File "/usr/local/lib/python2.7/site-packages/south/orm.py", line 238, in eval_in_context
    return eval(code, globals(), fake_locals)
  File "<string>", line 1, in <module>
TypeError: type() takes 1 or 3 arguments
1
Can you post the stacktrace as well? - Thomas

1 Answers

0
votes

Aha! Turns out it was another custom field causing the problem; One that was created using a factory method.

class MyOtherCustomField(models.Field):

    def __init__(self,*args,**kwargs):
        super(MyOtherCustomField,self).__init__(**kwargs)

    @classmethod
    def Factory(cls,model_field_class_name,**kwargs):
        try:
            # there is a global dictionary of potential field_classes and extra kwargs to pass to the constructor
            model_field_class_info = MODELFIELD_MAP[model_field_class_name.lower()]
            model_field_class = model_field_class_info[0]
            model_field_class_kwargs = model_field_class_info[1]
        except KeyError:
            msg = "unknown field type: '%s'" % model_field_class_name
            print "error: %s" % msg
            raise MyError(msg)

        class _MyOtherCustomField(cls,model_field_class):

            def __init__(self,*args,**kwargs):
                kwargs.update(model_field_class_kwargs)
                super(_MyOtherCustomField,self).__init__(**kwargs)
                self._type   = model_field_class_name

            def south_field_triple(self):
                # I was doing this which didn't work...
                #field_class_path = self.__class__.__module__ + "." + self.__class__.__name__
                # I'm now doing this which does work...
                field_class_path = "django.db.models.fields" + "." + model_field_class.__name__
                args,kwargs = introspector(self)
                return (field_class_path,args,kwargs)

        return _MyOtherCustomField(**kwargs)