I have such model:
class Place(models.Model):
name = models.CharField(max_length=80, db_index=True)
city = models.ForeignKey(City)
address = models.CharField(max_length=255, db_index=True)
# and so on
Since I'm importing them from many sources, and users of my website are able to add new Places, I need a way to merge them from an admin interface. Problem is, name is not very reliable since they can be spelled in many different ways, etc I'm used to use something like this:
class Place(models.Model):
name = models.CharField(max_length=80, db_index=True) # canonical
city = models.ForeignKey(City)
address = models.CharField(max_length=255, db_index=True)
# and so on
class PlaceName(models.Model):
name = models.CharField(max_length=80, db_index=True)
place = models.ForeignKey(Place)
query like this
Place.objects.get(placename__name='St Paul\'s Cathedral', city=london)
and merge like this
class PlaceAdmin(admin.ModelAdmin):
actions = ('merge', )
def merge(self, request, queryset):
main = queryset[0]
tail = queryset[1:]
PlaceName.objects.filter(place__in=tail).update(place=main)
SomeModel1.objects.filter(place__in=tail).update(place=main)
SomeModel2.objects.filter(place__in=tail).update(place=main)
# ... etc ...
for t in tail:
t.delete()
self.message_user(request, "%s is merged with other places, now you can give it a canonical name." % main)
merge.short_description = "Merge places"
as you can see, I have to update all other models with FK to Place with new values. But it's not very good solution since I have to add every new model to this list.
How do I "cascade update" all foreign keys to some objects prior to deleting them?
Or maybe there are other solutions to do/avoid merging
If anyone intersted, here is really generic code for this:
def merge(self, request, queryset):
main = queryset[0]
tail = queryset[1:]
related = main._meta.get_all_related_objects()
valnames = dict()
for r in related:
valnames.setdefault(r.model, []).append(r.field.name)
for place in tail:
for model, field_names in valnames.iteritems():
for field_name in field_names:
model.objects.filter(**{field_name: place}).update(**{field_name: main})
place.delete()
self.message_user(request, "%s is merged with other places, now you can give it a canonical name." % main)