Обновление Django GenericForeignKey

Я пытаюсь преобразовать ForeignKey в GenericForeignKey в django. Я планирую сделать это в три миграции, mig1, mig2, mig3.

Миграция 1 (mig1) имеет следующий код

class Migration(migrations.Migration):

    dependencies = [
        ('contenttypes', '0002_remove_content_type_name'),
        ('post_service', '0008_auto_20180802_1112'),
    ]

    operations = [
        migrations.AddField(
            model_name='comment',
            name='content_type',
            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType'),
        ),
        migrations.AddField(
            model_name='comment',
            name='object_id',
            field=models.PositiveIntegerField(null=True),
        ),
    ]

Миграция 2 (mig2) имеет следующий код

def change_posts_to_generic_key_comment(apps, schema_editor):
    Comment  = apps.get_model('post_service', 'Comment')
    db_alias = schema_editor.connection.alias
    comments = Comment.objects.using(db_alias).all()
    for comment in comments:
        Comment.objects.filter(id=comment.id).update(content_object=comment.post)

def reverse_change_posts_to_generic_key_comment(apps, schema_editor):
    Comment  = apps.get_model('post_service', 'Comment')
    db_alias = schema_editor.connection.alias
    comments = Comment.objects.using(db_alias).all()
    for comment in comments:
        Comment.objects.filter(id=comment.id).update(content_object=)

class Migration(migrations.Migration):

    dependencies = [
        ('post_service', '0009_auto_20180802_1623'),
    ]

    operations = [
        migrations.RunPython(change_posts_to_generic_key_comment, reverse_change_posts_to_generic_key_comment),
    ]

Я пытался использовать как обновление, так и прямое назначение объекта

comment.content_object = content.post, а затем comment.save()

ни один из них не работает. Как обновить общее поле внешнего ключа.

Один из способов — вручную установить content_type и object_id. Есть ли лучший способ сделать это?

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

class Comment(models.Model):

    post = models.ForeignKey(Post,on_delete=models.CASCADE)

    # Fields for generic relation
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, null=True)
    object_id = models.PositiveIntegerField(null=True)
    content_object = GenericForeignKey()

person Harwee    schedule 03.08.2018    source источник
comment
Я столкнулся с этим сегодня. Моя теория заключается в том, что что-то происходит, потому что экземпляры модели являются не настоящими экземплярами класса модели, а экземплярами класса, возвращаемыми функцией apps.get_model, которая является представлением класса модели в какой-то момент истории. Но ТБХ, я понятия не имею. Я надеюсь изучить это подробнее, когда у меня будет больше времени. Удалось обойти это, установив content_type_id и object_id непосредственно в миграции.   -  person Craig Anderson    schedule 22.02.2019
comment
У меня та же проблема, на мой взгляд, как вы обновили поле GenericForeignKey в Django?   -  person sg_sg94    schedule 14.05.2020
comment
@codinginquarantine я запустил функцию, которая извлекает все элементы в модели и назначает каждому из них вручную в цикле, назначает content_type и object_id, а затем сохраняет их, но это очень неэффективно. По крайней мере, так я думаю, что я сделал это тогда.   -  person Harwee    schedule 29.05.2020


Ответы (2)


Здесь нет причин использовать фильтр и обновление. У вас уже есть объект.

for comment in comments:
    comment.content_object = comment.post
    comment.save()
person Daniel Roseman    schedule 03.08.2018
comment
Я пытаюсь поэтапно заменить post Внешний ключ на content_type GenericForeignKey . Поэтому мне нужно сопоставить все сопоставления внешних ключей из сообщения с GenericForeignkey. общий внешний ключ не поддерживает обновление или прямое назначение. - person Harwee; 03.08.2018
comment
Какая? Да, иначе как бы вы могли его установить? Может быть, вы должны показать свои модели. - person Daniel Roseman; 03.08.2018
comment
Я добавил рассматриваемую модель Comment. - person Harwee; 03.08.2018
comment
Спасибо, но я все еще не понимаю, что вы говорите. Вы, конечно, можете напрямую присваивать content_object, и код, который я дал, отлично работает. - person Daniel Roseman; 03.08.2018
comment
Хм, даже я думаю, что это должно работать, но я не знаю, почему это не работает, обновленные данные не отражались после миграции, поэтому я вручную установил content_type и object_id, и это работает. - person Harwee; 03.08.2018
comment
Я также столкнулся с тем, что общие внешние ключи не сохранялись при миграции. я не знаю почему - person Timmy O'Mahony; 11.09.2020

У меня были проблемы с обновлением общих внешних ключей при миграции. Если я попытался установить ключ непосредственно для объекта, он ничего не установил при сохранении (поскольку он не распознал GenericForeignKey), и если я попытался установить content_type и object_id, я получил ошибки о том, что content_type должен быть установлен на ContentType (что и было Я делал).

В конце концов я создал команду управления, которая запускалась из миграции следующим образом:

from django.db import migrations, models
import django.db.models.deletion
from django.core.management import call_command

def populate_discounts(apps, schema_editor):
    """
    I could not get special to update as a generic foriegn key in the 
    migration. Do it as a one off management command
    """
    call_command('initialise_credit_specials')


class Migration(migrations.Migration):

    dependencies = [
        ('contenttypes', '0002_remove_content_type_name'),
        ('money', '0068_auto_20190123_0147'),
    ]

    operations = [
        migrations.AddField(
            model_name='invoicecredit',
            name='special_id',
            field=models.PositiveIntegerField(blank=True, null=True),
        ),
        migrations.AddField(
            model_name='invoicecredit',
            name='special_type',
            field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='contenttypes.ContentType'),
        ),
        migrations.RunPython(populate_discounts)
    ]

и моя команда управления была довольно простой:

from django.core.management.base import CommandError, BaseCommand    
from money.models import InvoiceCredit, PromotionCode, Sale


class Command(BaseCommand):
    """
    Report how much we need to refund at the end of the financial year
    """
    def handle(self, *args, **options):
        print('updating discounts')
        # first promotions 
        for pc in PromotionCode.objects.all():
            ics = InvoiceCredit.objects.filter(
                desc__contains=pc.code
                )
            for ic in ics.all():
                ic.special = pc
                ic.save()
                print('invoice credit %d updated with %s' % (ic.id, ic.special.code))

        # Then sales
        for sale in Sale.objects.all():
            ics = InvoiceCredit.objects.filter(
                desc__startswith=Sale.desc,
                invoice__booking__tour_date__tour=sale.tour_combinations.first().base_tour
                )
            for ic in ics.all():
                ic.special = sale
                ic.save()
                print('invoice credit %d updated with sale %d' % (ic.id, sale.id))
person MagicLAMP    schedule 25.01.2019
comment
если я попытался установить content_type и object_id, я получил ошибки о том, что content_type должен быть установлен на ContentType (что я и делал) —— Для тех, кто задается той же проблемой, я понял. Убедитесь, что вы импортируете с использованием ContentType = apps.get_model("contenttypes", "ContentType") вместо модуля Python import. - person Larry; 19.06.2020