2015-03-20 20 views
8

Bene, ora sto usando Django 1.6 +Django: trovare tutti i riferimenti inversa da chiavi esterne

E ho un modello:

class FileReference(models.Model): 
    # some data fields 
    # ... 
    pass 

class Person(models.Model): 
    avatar = models.ForeignKey(FileReference, related_name='people_with_avatar') 

class House(models.Model): 
    images = models.ManyToManyField(FileReference, related_name='houses_with_images') 

class Document(model.Model): 
    attachment = models.OneToOneField(FileReference, related_name='document_with_attachment') 

Così, molti altri modello avrà una chiave esterna riferimento al modello FileReference.

Ma a volte, i modelli di riferimento vengono cancellati, con l'oggetto FileReference lasciato.

Desidero eliminare gli oggetti FileReference senza riferimenti a chiave esterna.

Ma così tanti altri posti avranno le chiavi esterne.

Esiste un modo efficace per trovare tutti i riferimenti? Ad esempio, ottieni il numero di riferimento di qualche oggetto modello?

+2

Utilizzare il 'Collector': http://stackoverflow.com/a/12162619/548165 – catavaran

+0

@catavaran Ho provato che, quando chiamo' collector.collect (file_ref_obj) ', solleva:' TypeError: 'FileReference' l'oggetto non supporta l'indicizzazione' –

+0

Passa una lista con la singola istanza: 'collector.collect ([file_ref_obj])' – catavaran

risposta

0

Mi sono imbattuto in questa domanda e ho trovato una soluzione per voi. Si noti, che django==1.6 non è supportato più, quindi questa soluzione probabilmente funzionerà sulla django>=1.9

Diciamo che stiamo parlando di due degli oggetti, per ora:

class FileReference(models.Model): 
    pass 

class Person(models.Model): 
    avatar = models.ForeignKey(FileReference, related_name='people_with_avatar', on_delete=models.CASCADE) 

Come si può vedere nel ForeignKey.on_delete documentazione, quando si elimina l'oggetto FileReference correlato, viene eliminato anche l'oggetto di riferimento Person.

Ora per la tua domanda. Come facciamo i riveriti? Vogliamo la cancellazione Person che l'oggetto FileReference verrà rimosso anche.

Lo faremo utilizzando post_delete signal:

def delete_reverse(sender, **kwargs): 
    try: 
     if kwargs['instance'].avatar: 
      kwargs['instance'].avatar.delete() 
    except: 
     pass 

post_delete.connect(delete_reverse, sender=Person) 

Quello che abbiamo fatto c'era la soppressione del riferimento nel avatar campo su Person eliminazione. Si noti che il blocco try: except: impedisce le eccezioni di loop.

Extra:

La soluzione di cui sopra funziona su tutti gli oggetti futuri. Se si desidera rimuovere tutti gli oggetti passati senza un riferimento effettuare le seguenti operazioni:

Nel pacchetto aggiungere il seguente file e directory: management/commands/remove_unused_file_reference.py

from django.core.management.base import BaseCommand, CommandError 


class Command(BaseCommand): 

    def handle(self, *args, **options): 

     file_references = FileReference.objects.all() 
     file_reference_mapping = {file_reference.id: file_reference for file_reference in file_references} 

     persons = Person.objects.all() 
     person_avatar_mapping = {person.avatar.id: person for person in persons} 


     for file_reference_id, file_reference in file_reference_mapping.items(): 
      if file_reference_id not in person_avatar_mapping: 
       file_reference.delete() 

volta finito, chiamare: python manage.py remove_unused_file_reference Questo è il idea base, puoi cambiarla in bulk delete, ecc ...

Spero che questo possa aiutare qualcuno là fuori. In bocca al lupo!

Problemi correlati