2

I'm using a metaclass to automatically set my Django model db table names from camel case to '_' seperated names, e.g. MyGreatModel will have a table name like appname_my_great_model instead of the default appname_mygreatmodel:

class PrettyModelBase(ModelBase):

    def __new__(cls, name, bases, attrs):
        super_new = ModelBase.__new__(cls, name, bases, attrs)
        module_name = camel_to_underscore(name)
        model_module = sys.modules[cls.__module__]

        app_label = super_new.__module__.split('.')[-2]
        db_table = '%s_%s' % (app_label, module_name)
        if not getattr(super_new._meta, 'proxy', False):
             super_new._meta.db_table = db_table

        return super_new


class BaseModel(models.Model):

    __metaclass__ = PrettyModelBase

    class Meta:
        abstract = True

class MyGreatModel(BaseModel):
    somefield = models.TextField(default="")

However migrations doesn't seem to pick up this db_table name. If I run makemigrations, then CreateModel does not show db_table in the options for this model. If I hard-code it in the class Meta of MyGreatModel then it does. Moreover if I check the SQL to be run with python manage.py sqlmigrate... then it shows that it will create the appname_mygreatmodel table, not the delimited table name.

However if I do run this migration and then examine the model in the Django shell, then MyGreatModel._meta.db_table shows my_great_model as I would expect from my metaclass. If I manually add the db_table attribute the 0001 init migration options for CreateModel, then all is OK for subsequent migrations too, but Django should be doing that automatically I think...

Why is Django migrations not picking up the db_table here?

I'm using Django==1.10.5.

Thanks for any help

Kevin Christopher Henry
  • 46,175
  • 7
  • 116
  • 102
fpghost
  • 2,834
  • 4
  • 32
  • 61
  • I'm not sure why that isn't working, but my advice would be to use the public `Meta` API rather than mucking around with `_meta`. Specifically, set `Meta.db_table` (from `attrs`) before calling `ModelBase.__new__()`. That will make you far less dependent on the internal details of Django's metaclass and migrations implementations. – Kevin Christopher Henry Jul 01 '18 at 06:41
  • Any luck yet? I am running into precisely the same issue. I'm trying to leverage Postgres schemas by setting all my table names to "app_label.model_name" instead of "app_label_model_name". – DrS Jun 26 '19 at 13:11
  • I just think this is a bad idea. Why do you want to change the names of your tables? Also, Django 1.10.5 is insecure. You need to update to at least 1.11, preferably 2.2. – Bobort Jun 26 '19 at 15:02

1 Answers1

2

This question was re-raised on the django-developers mailing list: https://groups.google.com/forum/#!msg/django-developers/ncMCwMF2J3g/V3guuDbJCwAJ

Django migrations uses model._meta.original_attrs to determine the definition of the meta class: https://github.com/django/django/blob/stable/2.2.x/django/db/migrations/state.py#L454

Thus if you're dynamically mutating model._meta post-creation, you'll also need to mutate its original_attrs dict correspondingly. I think that should work (untested).

Adam Johnson
  • 471
  • 1
  • 5
  • 11