Fix deduplicating books that are on a shelf or in a list

Previously when the deduplicate_book_data script tried to merge an
edition that was on a shelf or in a list then it would fail because when
the canonical book was added to the shelf or the list then it wouldn’t
set the extra fields of the linking table for the “through” model of the
field. These would end up defaulting to NULL, but that is not valid for
some of the fields in ShelfItem and ListItem so postgres wouldn’t accept
it.

To fix that, this patch makes it skip updating fields that have a
non-autogenerated linking table. The linking table would appear as a
separate model anyway so the book will be moved via that instead.

Fixes: #2817
This commit is contained in:
Neil Roberts 2023-04-15 11:36:18 +02:00
parent 8e25ae34d6
commit 2bbc9a16ad

View file

@ -1,7 +1,7 @@
""" PROCEED WITH CAUTION: uses deduplication fields to permanently """ PROCEED WITH CAUTION: uses deduplication fields to permanently
merge book data objects """ merge book data objects """
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from django.db.models import Count from django.db.models import Count, ManyToManyField
from bookwyrm import models from bookwyrm import models
@ -12,6 +12,16 @@ def update_related(canonical, obj):
(r.remote_field.name, r.related_model) for r in canonical._meta.related_objects (r.remote_field.name, r.related_model) for r in canonical._meta.related_objects
] ]
for (related_field, related_model) in related_models: for (related_field, related_model) in related_models:
# Skip the ManyToMany fields that arent auto-created. These
# should have a corresponding OneToMany field in the model for
# the linking table anyway. If we update it through that model
# instead then we wont lose the extra fields in the linking
# table.
related_field_obj = related_model._meta.get_field(related_field)
if isinstance(related_field_obj, ManyToManyField):
through = related_field_obj.remote_field.through
if not through._meta.auto_created:
continue
related_objs = related_model.objects.filter(**{related_field: obj}) related_objs = related_model.objects.filter(**{related_field: obj})
for related_obj in related_objs: for related_obj in related_objs:
print("replacing in", related_model.__name__, related_field, related_obj.id) print("replacing in", related_model.__name__, related_field, related_obj.id)