I am trying to convert a ForeignKey to GenericForeignKey in django. I plan to do this in three migrations, mig1, mig2, mig3.
Migration 1 (mig1) has the following code
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),
),
]
Migration 2 (mig2) has the following code
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),
]
I tried to use both update and direct assignment of object
comment.content_object = content.post
followed by comment.save()
none of them seems to work. How do i update generic foreign key field.
One method is to manually set content_type
and object_id
. Is there any better way of doing this?
EDIT: Comment Model
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()
I had problems updating generic foreign keys in the migration. If I tried setting the key directly to the object, it did not set anything on save (because it did not recognise the GenericForeignKey), and if I tried setting content_type and object_id I got errors about content_type must be set to ContentType (which was what I was doing).
In the end I created a management command which was run from the migration as follows:
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)
]
and my management command was pretty simple:
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))
There's no reason to use filter and update here. You have the object already.
for comment in comments:
comment.content_object = comment.post
comment.save()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With