def _assign_disease_by_morpho_topo(instance, icdo_morpho, icdo_topo_list, disease_field='disease'): """ Given an instance with a disease:models.ForeignKey(Disease) field (specified by 'disease_field'), an icdo_morpho id, and an optional set of icdo_topo IDs in icdo_topo_list, creates a new disease matching the morpho, topo spec, or retrieves it if it already exists. Assigns the new or retrieved disease to the instance's disease field if successful. """ # approach: now that Disease is mutable, we can't simply point # multiple curation entries at the same Disease; instead, we need # to either look up a Disease that matches the morpho code + topo set # in this entry, or create it if it doesn't exist. # note that this produces a trail of Diseases that are *potentially* # no longer in use if we ever change the morpho code or topo codes. # we can either clean up these unused entries after updating, or # 'vacuum' them on a schedule with a script. this will require # identifying every model that refers to a Disease with transaction.atomic(): if not icdo_morpho: # we can't assign a disease if they didn't specify a morpho field setattr(instance, disease_field, None) instance.save() return None instance_disease = getattr(instance, disease_field) if instance_disease: # if there was previously a disease, check if it's in use and delete it if not collector = NestedObjects(using='default') # or specific database collector.collect([instance_disease]) if collector.can_fast_delete(): print("Fast-deleting old disease") instance_disease.delete() else: print("Can't fast-delete old disease: %s" % collector.nested()) # build a Q-object that's all the topo entries related to the target Disease ANDed together q_objects = Q(icd_o_morpho=icdo_morpho['id']) if icdo_topo_list: for x in icdo_topo_list: q_objects &= Q(icdotopoapidisease__icd_o_topo__id=x['id']) else: # we need to search for a morpho that has no associated topo codes q_objects &= Q(icdotopoapidisease=None) # either retrieve an existing disease that matches the description, or create it candidate = Disease.objects.filter(q_objects).first() if not candidate: candidate = Disease.objects.create( icd_o_morpho=IcdOMorpho.objects.get(id=icdo_morpho['id'])) IcdOTopoApiDisease.objects.bulk_create([ IcdOTopoApiDisease(api_disease=candidate, icd_o_topo=IcdOTopo.objects.get(id=x['id'])) for x in (icdo_topo_list if icdo_topo_list else []) ]) candidate.save() setattr(instance, disease_field, candidate) instance.save()