2012-12-04 9 views

Antwort

56
unique_fields = ['field_1', …, 'field_n'] 

duplicates = (MyModel.objects.values(*unique_fields) 
          .order_by() 
          .annotate(max_id=models.Max('id'), 
             count_id=models.Count('id')) 
          .filter(count_id__gt=1)) 

for duplicate in duplicates: 
    (MyModel.objects.filter(**{x: duplicate[x] for x in unique_fields}) 
        .exclude(id=duplicate['max_id']) 
        .delete()) 

Sie sollten es nicht oft. Verwenden Sie stattdessen unique_together Constraints für die Datenbank.

Basiswert SQL-Code

Wenn Anmerkungen versehen django ORM GROUP BY Anweisung auf allen Modellen Felder verwendet, in der Abfrage verwendet. So die Verwendung von .values() Methode. GROUP BY gruppiert alle Datensätze mit diesen Werten identisch. Die duplizierten (mehr als eine id für unique_fields) werden später in HAVING Anweisung ausgedruckt von .filter() auf annotiert QuerySet ausgefiltert.

SELECT 
    field_1, 
    … 
    field_n, 
    MAX(id) as max_id, 
    COUNT(id) as count_id 
FROM 
    app_mymodel 
GROUP BY 
    field_1, 
    … 
    field_n 
HAVING 
    count_id > 1 

die duplizierte Datensätze werden in der später for Schleife mit Ausnahme von den häufigsten einem für jede Gruppe gelöscht.

Leere .order_by()

Nur um sicherzugehen, ist es immer ratsam, einen leeren .order_by() Anruf hinzufügen, bevor eine QuerySet aggregieren.

Die Felder für die Bestellung der QuerySet sind ebenfalls in der GROUP BY-Anweisung enthalten. Leer .order_by() überschreibt Spalten, die in Meta des Modells deklariert wurden, und im Ergebnis sind sie nicht in der SQL-Abfrage enthalten (z. B. kann die Standardsortierung nach Datum die Ergebnisse ruinieren).

Sie müssen es möglicherweise im aktuellen Moment nicht überschreiben, aber jemand könnte die Standardbestellung später hinzufügen und daher Ihren wertvollen Lösch-Duplikat-Code ruinieren, ohne das zu wissen. Ja, ich bin mir sicher, dass Sie 100% Testabdeckung haben ...

Fügen Sie einfach .order_by() hinzu, um sicher zu sein. ;-)

https://docs.djangoproject.com/en/1.11/topics/db/aggregation/#interaction-with-default-ordering-or-order-by

Transaktion

Natürlich sollte man sie alle in einer einzigen Transaktion in Erwägung ziehen.

https://docs.djangoproject.com/en/1.11/topics/db/transactions/#django.db.transaction.atomic

+0

Vielen Dank! Damit ich jedoch verstehen kann (ich bin immer noch sehr ein Django-Novize), könntest du bitte erklären, was bei jedem Schritt passiert? Ich verstehe, dass 'MyModel.objects.values ​​(* unique_fields)' eine Reihe von Wörterbüchern generiert, wobei jedes Wörterbuch zu einem Objekt gehört. Aber dann bin ich verloren - was macht das Annotat? – Westerley

+1

Ich hoffe, dass mein Update die Dinge ein wenig klären wird. –

+1

Brilliant! Funktioniert perfekt!Ich brauchte ziemlich viel Recherche und Nachdenken, um genau herauszufinden, wie ** es funktioniert (Ihre Erklärung half mir ziemlich und half mir herauszufinden, was ich lesen musste ...), aber es funktioniert! Nochmals vielen Dank (und Entschuldigung für die Verzögerung, um zurück zu diesem) – Westerley

Verwandte Themen