【问题标题】:Django GenericForeignKey updateDjango GenericForeignKey 更新
【发布时间】:2018-08-03 10:17:43
【问题描述】:

我正在尝试在 django 中将 ForeignKey 转换为 GenericForeignKey。我计划在三个迁移中执行此操作,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_typeobject_id。有没有更好的方法来做到这一点?

编辑:评论模型

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()

【问题讨论】:

  • 我今天遇到了这个。我的理论是发生了一些事情,因为模型实例不是模型类的 true 实例,而是apps.get_model 返回的类的实例,这是模型类在某个时刻的表示历史。但是TBH,我不知道。当我有更多时间时,我希望能进一步研究这个问题。可以通过直接在迁移中设置 content_type_idobject_id 来解决问题。
  • 我也有同样的问题,你是如何更新 Django 中的 GenericForeignKey 字段的?
  • @codinginquarantine 我运行了一个函数,它获取模型中的所有项目并在循环中手动分配每个项目,然后分配 content_type 和 object_id 然后保存它,但效率非常低。至少我当时是这么认为的。

标签: django django-migrations generic-foreign-key


【解决方案1】:

我在迁移中更新通用外键时遇到问题。如果我尝试将密钥直接设置为对象,它不会在保存时设置任何内容(因为它无法识别 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))

【讨论】:

  • "如果我尝试设置 content_type 和 object_id 我得到了关于 content_type 必须设置为 ContentType 的错误(这是我正在做的)" —— 谁想知道同样的问题,我想通了。确保使用 ContentType = apps.get_model("contenttypes", "ContentType") 而不是 python 模块 import 导入。
【解决方案2】:

这里没有理由使用过滤器和更新。您已经拥有该对象。

for comment in comments:
    comment.content_object = comment.post
    comment.save()

【讨论】:

  • 我正在尝试将 post 外键逐步淘汰为 content_type GenericForeignKey 。所以我需要将所有的外键映射从 post 映射到 GenericForeignkey。通用外键不支持更新或直接赋值。
  • 什么?是的,它确实如此,否则你怎么能设置它?也许你应该展示你的模型。
  • 我已添加有问题的Comment 模型。
  • 嗯,即使我认为它应该可以工作,但我不知道为什么它不起作用,更新后的数据在迁移后没有反映,所以我手动设置了content_type和@987654326 @它有效。
  • 我也遇到过通用外键未保存在迁移中的情况。我不知道为什么
猜你喜欢
  • 2012-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-18
  • 1970-01-01
  • 2012-12-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多