Почему поля GenericRelation не работают в миграции данных (Django)

Я хочу выполнить миграцию данных, чтобы добавить сообщение для чтения пользователем в базу данных. Есть такой код:

def user_read_posts(apps, schema_editor):
    User = apps.get_model("main", "User")
    Post = apps.get_model("main", "Post")
    Comment = apps.get_model("comments", "Comment")

    comments = Comment.objects.all()
    for comment in comments:
        print (comment.content_object.__class__.__name__)
        if isinstance(comment.content_object, Post):
            comment.user.read_posts.add(comment.content_object)

class Migration(migrations.Migration):

    dependencies = [
        ('main', '0039_auto_20160314_0906'),
    ]

    operations = [
        migrations.RunPython(user_read_posts),
    ]

И в строке print (comment.content_object.__class__.__name__) django вызывает ошибку: AttributeError: 'Comment' object has no attribute 'content_object'

Модель комментария:

class GuidaComment(GenericRelationModel):

    user = models.ForeignKey(GuidaUser)
    text = models.TextField()

И что я должен делать?

Спасибо.


person Q-bart    schedule 28.03.2016    source источник
comment
Можете ли вы дать атрибуты класса GenericRelationModel?   -  person ruddra    schedule 28.03.2016
comment
Я использую приложение django generic_helpers. Как и в документах: from generic_helpers.models import GenericRelationModel Вот модуль моделей github.com/marazmiki/django-generic-helpers/blob/master/   -  person Q-bart    schedule 28.03.2016
comment
Попробуйте использовать вместо этого: docs.djangoproject.com/en/1.9/ref/ contrib/contenttypes, поскольку я вижу, что пакет не обновлялся почти 1 год.   -  person ruddra    schedule 28.03.2016
comment
Спасибо, но я должен использовать это, так как это не мой код для комментариев. Но я забыл добавить: когда я вызываю content_object в оболочке или представлениях, он работает отлично. Но нет в миграции данных   -  person Q-bart    schedule 28.03.2016


Ответы (1)


В RunPython сценариях миграции apps.get_models() будет получена историческая версия моделей, а не последние модели django, которые у вас есть в исходном коде. Эти исторические модели весьма ограничены. Выдержка из документации по django:

исторические модели не будут иметь никаких пользовательских методов, которые вы определили. Однако у них будут одинаковые поля, отношения, менеджеры (только те, у которых use_in_migrations = True) и мета-параметры (также версии, поэтому они могут отличаться от ваших текущих).

Но это не значит, что вы не можете использовать последние и полнофункциональные модели, просто импортируя их и используя. В этом случае вы рискуете, что скрипт миграции не будет работать корректно в будущем, поскольку последняя версия модели может измениться (например, метод модели может быть переименован, удален, изменена логика и т. д.). Итак, это возможно, но вы должны осознавать риск использования таких моделей.

Структура типов контента довольно старая и редко меняется. ИМХО, это достаточно безопасно, чтобы использовать его таким образом.

Вдохновленный ответом и решением, которое я сделал и использую , Я сделал следующий черновик, надеюсь, вы найдете его полезным:

def user_read_posts(apps, schema_editor):
    User = apps.get_model("main", "User")
    Post = apps.get_model("main", "Post")
    Comment = apps.get_model("comments", "Comment")

    from django.contrib.contenttypes.models import ContentType

    comments = Comment.objects.all()
    for comment in comments:
        try:
            # assuming that Comment has object_id field holding reference to PK
            # of the referenced object 
            ct = ContentType.objects.get(model=comment.content_type.model, 
                                         app_label=comment.content_type.app_label)
            content_object = ct.get_object_for_this_type(pk=comment.object_id)
        except Exception, ex: 
            # TODO: can happen if some content type / model is deleted.
            continue

        print (content_object.__class__.__name__)
        if isinstance(content_object, Post):
            # TODO: maybe you will need to copy/adapt read_posts.add method's logic here
            comment.user.read_posts.add(content_object)
person Robert Lujo    schedule 10.12.2017