2016-04-08 10 views
0

Jeder "time range" -Eintrag der TimeClass ist voneinander abhängig.abhängige Multi-Objekt-Validierung in Django Admin

Sie können nicht überlappen und start_time < end_time.

models.py

class Xyz(models.Model): 
    ... 

class TimeRangeClass(models.Model) 
    start_time = models.TimeField() 
    end_time = models.TimeField() 
    xyz = models.ForeignKey(Xyz) 
    # other fields here 

    def clean(self): 
     # Here I loop through TimeRangeClass.objects.all() and 
     # check for conflicts through my custom "my_validator_method". 
     # If there is a conflict I throw an error 
     #(I've since modified it to just be one single query as per Titusz advice)    
     for each in TimeRangeClass.objects.filter(xyz=self.xyz).exclude(id=self.id): 
      my_validator_method(start_time1=self.start_time, 
           end_time1=self.end_time, 
           start_time2=each.start_time, 
           end_time2=each.end_time) 

admin.py

from .models import TimeRangeClass, Xyz 
class TimeRangeClassInLine(admin.TabularInline): 
    model = TimeRangeClass 
    extra = 3 

@admin.register(Xyz) 
class Xyz(admin.ModelAdmin): 
    exclude = [] 
    inlines = [TimeRangeClassInLine] 

Problem: Ich kann auf einmal durch den Admin mehrere TimeRangeClass des bearbeiten/hinzufügen. Aber da die models.clean Methode nur eine Änderung gleichzeitig bewertet, kann ich mehrere Änderungen nicht gegenseitig validieren.

Beispiel:

  1. Speichern ein Entry1 & entry2 ohne Konflikt

  2. ändern entry2 einen Validierungsfehler zu erzeugen

  3. Entry1 einstellen (statt # 2), so dass sie nicht überlappen

  4. Dies wird nicht registriert, da keine Änderungen in th geschrieben werden e db.

Ich bin auf der Suche nach einem Workaround.

+0

Sie sollten eine konkretere Erklärung des Problems geben. Wo/Wie ändern Sie Eintrag Nr. 2? Über die Admin-Oberfläche, aus der Ansicht oder einem Formular? – Titusz

Antwort

1

Einige Hinweise auf das Problem:

Sie sollten die vollständige Tabelle nicht durchlaufen, wenn Zeilen für überlappende Überprüfung. Nur Filter für die problematischen Zeilen ... so etwas wie:

overlaps = TimeRangeClass.objects.filter(
    Q(start_time__gte=self.start_time, start_time__lt=self.end_time) | 
    Q(end_time__gt=self.start_time, end_time__lte=self.end_time) 
) 

overlaps ist nun ein queryset, die auswertet, wenn Sie über sie iterieren und liefert nur die widerstreitenden Objekte.

Wenn Sie Django mit Postgres verwenden, sollten Sie https://docs.djangoproject.com/es/1.9/ref/contrib/postgres/fields/#datetimerangefield überprüfen.

Sobald Sie die widersprüchlichen Objekte haben, sollten Sie in der Lage sein, ihre Start- und Endzeiten innerhalb der Funktion zu ändern und die Änderungen zu speichern. Model.save() ruft die model.clean() -Methode nicht automatisch auf. Aber Vorsicht, wenn Sie ein Objekt vom Django-Administrator speichern, wird die model.clean() -Methode vor dem Speichern aufrufen.

So etwas wie das:

def clean(): 
    overlaps = TimeRangeClass.overlaps.for_trc(self) 
    for trc_object in overlaps: 
     fixed_object = fix_start_end(trc_object, self) 
     fixed_object.save() 

Wenn Sie mutig fühlen, sollten Sie auch auf Transaktionen zu lesen, um die Mutation von mehreren Objekten in der Datenbank zu machen alle Erfolg haben oder scheitern alle und nichts dazwischen.

def clean(): 
    with transaction.atomic(): 
     # do your multi object magic here ... 

Update zu klären Frage:

Wenn Sie pre/Prozessdaten validieren möchten oder die von Admin inlines Sie in das entsprechende ModelAdmin Methode Haken haben kommt (s).Es gibt mehrere Möglichkeiten, dies zu erreichen. Ich denke, das einfachste wäre, ModelAdmin.save_fromset zu überschreiben. Hier haben Sie Zugriff auf alle Inlineforms, bevor sie gespeichert wurden.

+0

Vielen Dank für die Empfehlung, Q() zu verwenden, anstatt das Abfrage-Set zu durchlaufen. Ich bin mir nicht sicher, warum ich mich in diesem Fall für eine Schleife entschieden habe, da ich in anderen Fällen eine ähnliche Abfrage verwende. – Maxim

+0

Ich habe kurz die Dokumentation zu transaction.atomic() angeschaut. Würde ich damit noch nicht gespeicherte Variablen für die Clean-Methode vergleichen können? – Maxim

+0

Was meinen Sie mit noch nicht gespeicherten Variablen? Woher kommen Ihre * noch nicht gespeicherten Variablen *? Sie vergleichen die neue, möglicherweise nicht gespeicherte TimeRangeClass-Instanz mit vorhandenen Instanzen, die Sie über das Abfrage-Set in den Arbeitsspeicher geladen haben. – Titusz