django-parler: Translated models can't be used in datamigration

Probably similar to #100

When I try to use translated models in data migration I have an error when I try to create new instance and save it. The error is:

File "/path/to/virtualenv/lib/python3.4/site-packages/django/db/models/query.py", line 346, in create
    obj = self.model(**kwargs)
File "/path/to/virtualenv/lib/python3.4/site-packages/parler/models.py", line 236, in __init__
    for field in self._parler_meta.get_all_fields():
AttributeError: 'NoneType' object has no attribute 'get_all_fields'

Also when I try to query translated models with specific methods to parler’s TranslatableManager it raises AttributeError.

In the initial migration when model is created in migrations.CreateModel step I see that bases are defined like that:

bases=(parler.models.TranslatableModelMixin, models.Model),

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 10
  • Comments: 18 (8 by maintainers)

Commits related to this issue

Most upvoted comments

same issue using django-parler==2.0 django==2.1.7

used python manage.py makemigrations when migrating from my old model:

from django.db import models
from parler.models import TranslatableModel, TranslatedFields

from webapp.core.models.base_model import BaseModel


class MyModel(models.Model):
    class Meta:
        verbose_name = 'My Model'
        verbose_name_plural = 'My Model'

    title = models.CharField(max_length=50)

to new Model:

from django.db import models
from parler.models import TranslatableModel, TranslatedFields

from webapp.core.models.base_model import BaseModel


class MyModel(TranslatableModel):
    class Meta:
        verbose_name = 'My Model'
        verbose_name_plural = 'My Model'

    translations = TranslatedFields(
        title=models.CharField("Title", max_length=50)
    )
Traceback (most recent call last):
  File "manage.py", line 32, in <module>
    execute_from_command_line(sys.argv)
  File "/usr/local/lib/python3.7/site-packages/django/core/management/__init__.py", line 381, in execute_from_command_line
    utility.execute()
  File "/usr/local/lib/python3.7/site-packages/django/core/management/__init__.py", line 375, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/local/lib/python3.7/site-packages/django/core/management/base.py", line 316, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/usr/local/lib/python3.7/site-packages/django/core/management/base.py", line 353, in execute
    output = self.handle(*args, **options)
  File "/usr/local/lib/python3.7/site-packages/django/core/management/base.py", line 83, in wrapped
    res = handle_func(*args, **kwargs)
  File "/usr/local/lib/python3.7/site-packages/django/core/management/commands/migrate.py", line 203, in handle
    fake_initial=fake_initial,
  File "/usr/local/lib/python3.7/site-packages/django/db/migrations/executor.py", line 117, in migrate
    state = self._migrate_all_forwards(state, plan, full_plan, fake=fake, fake_initial=fake_initial)
  File "/usr/local/lib/python3.7/site-packages/django/db/migrations/executor.py", line 147, in _migrate_all_forwards
    state = self.apply_migration(state, migration, fake=fake, fake_initial=fake_initial)
  File "/usr/local/lib/python3.7/site-packages/django/db/migrations/executor.py", line 244, in apply_migration
    state = migration.apply(state, schema_editor)
  File "/usr/local/lib/python3.7/site-packages/django/db/migrations/migration.py", line 114, in apply
    operation.state_forwards(self.app_label, project_state)
  File "/usr/local/lib/python3.7/site-packages/django/db/migrations/operations/fields.py", line 73, in state_forwards
    state.reload_model(app_label, self.model_name_lower, delay=delay)
  File "/usr/local/lib/python3.7/site-packages/django/db/migrations/state.py", line 158, in reload_model
    self._reload(related_models)
  File "/usr/local/lib/python3.7/site-packages/django/db/migrations/state.py", line 191, in _reload
    self.apps.render_multiple(states_to_be_rendered)
  File "/usr/local/lib/python3.7/site-packages/django/db/migrations/state.py", line 306, in render_multiple
    model.render(self)
  File "/usr/local/lib/python3.7/site-packages/django/db/migrations/state.py", line 574, in render
    return type(self.name, bases, body)
  File "/usr/local/lib/python3.7/site-packages/django/db/models/base.py", line 299, in __new__
    new_class._meta.apps.register_model(new_class._meta.app_label, new_class)
  File "/usr/local/lib/python3.7/site-packages/django/db/migrations/state.py", line 337, in register_model
    self.do_pending_operations(model)
  File "/usr/local/lib/python3.7/site-packages/django/apps/registry.py", line 420, in do_pending_operations
    function(model)
  File "/usr/local/lib/python3.7/site-packages/django/apps/registry.py", line 399, in apply_next_model
    self.lazy_model_operation(next_function, *more_models)
  File "/usr/local/lib/python3.7/site-packages/django/apps/registry.py", line 411, in lazy_model_operation
    apply_next_model(model_class)
  File "/usr/local/lib/python3.7/site-packages/django/apps/registry.py", line 399, in apply_next_model
    self.lazy_model_operation(next_function, *more_models)
  File "/usr/local/lib/python3.7/site-packages/django/apps/registry.py", line 385, in lazy_model_operation
    function()
  File "/usr/local/lib/python3.7/site-packages/django/db/models/fields/related.py", line 316, in resolve_related_class
    field.do_related_class(related, model)
  File "/usr/local/lib/python3.7/site-packages/django/db/models/fields/related.py", line 387, in do_related_class
    self.contribute_to_related_class(other, self.remote_field)
  File "/usr/local/lib/python3.7/site-packages/parler/fields.py", line 54, in contribute_to_related_class
    self.model.contribute_translations(cls)
  File "/usr/local/lib/python3.7/site-packages/parler/models.py", line 939, in contribute_translations
    base = shared_model._parler_meta
AttributeError: type object 'MyModel' has no attribute '_parler_meta'

by manually changing the migration File from:

bases=(parler.models.TranslatedFieldsModelMixin, models.Model),

to

bases=(parler.models.TranslatableModel, models.Model),

migrate is doing it’s job.

I’m working a solution which involves refactoring the base class. Can’t promise any ETA, though, but I’ll keep you updated

Any chance we can get a release that includes this fix?

I experienced the exact same problem. I managed to make it work by setting model bases inside the RunPython script during runtime like this:

MyModel.__bases__ = (models.Model,)

Here is the full code:

def my_script(apps, schema_editor):
    MyModel = apps.get_model("my_app", "MyModel")
    MyModel.__bases__ = (models.Model,)

    MyModelTranslation = apps.get_model("my_app", "MyModelTranslation")
    ...

class Migration(migrations.Migration):
    dependencies = [
        ('my_app', '0001_initial'),
    ]

    operations = [
        migrations.CreateModel(
            name='MyModel',
            fields=[...],
            options={...},
            bases=(parler.models.TranslatableModelMixin, models.Model),
        ),
        ... ,
        migrations.RunPython(my_script),
    ]

This way you will not need to modify any migration files.

I was able to make this work by replacing base = shared_model._parler_meta in models.py:939 with base = getattr(shared_model, '_parler_meta', None) But I’m not sure if this will cause problems in other situations.

I’m quite surprised to see bases=(parler.models.TranslatableModelMixin, models.Model),.

This doesn’t activate our metaclass, so _parler_meta will not be initialized. If you use bases=(parler.models.TranslatableModel,), this issue probably disappears.

Which Django version have you used? If you must, you access your translated fields model directly (the only that inherits TranslatedFieldsModel in the migration).