0

Chaque entrée "Time Plage" de la TimeClass dépend l'une de l'autre.Validation multi-objets dépendants dans admin django

Ils ne peuvent pas se chevaucher et 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] 

Problème: Je peux modifier/ajouter plusieurs années TimeRangeClass à la fois par l'administrateur. Mais étant donné que la méthode models.Model clean n'évalue qu'un changement à la fois, je ne peux pas valider plusieurs modifications les unes par rapport aux autres.

Exemple:

  1. Enregistrer un Entrée1 & inscription2 sans conflit

  2. changement inscription2 pour produire une erreur de validation

  3. Adjust Entrée1 (au lieu de # 2) de sorte qu'ils ne se chevauchent pas

  4. Ceci ne s'enregistre pas car aucun des changements n'est écrit e db.

Je suis à la recherche d'une solution de contournement.

+0

Vous devriez donner une explication plus concrète du problème. Où/Comment changez-vous l'entrée 2? Depuis l'interface d'administration, à partir de la vue ou d'un formulaire? – Titusz

Répondre

1

Quelques conseils sur le problème:

Vous ne devriez pas itérer sur la table lors de la vérification complète des lignes qui se chevauchent. Juste pour filtrer les lignes problématiques ... quelque chose comme:

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 est maintenant un queryset qui évalue lorsque vous itérer sur elle et renvoie uniquement les objets en conflit.

Si vous utilisez Django avec postgres, vous devriez vérifier https://docs.djangoproject.com/es/1.9/ref/contrib/postgres/fields/#datetimerangefield. Une fois que vous avez les objets en conflit, vous devriez pouvoir changer leurs heures de début et de fin dans la fonction et enregistrer les changements. Model.save() n'appelle pas automatiquement la méthode model.clean(). Mais sachez que si vous enregistrez un objet depuis l'administrateur Django, appellera la méthode model.clean() avant de l'enregistrer.

donc quelque chose comme ça:

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

Si vous vous sentez courageux, vous devriez également lire sur les transactions pour faire la mutation d'objets multiples dans la base de données tout reussit ou tout échec et rien entre les deux.

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

Mise à jour sur la question clarifiée:

Si vous souhaitez valider ou des données pré/processus qui vient de inline administrateur, vous devez brancher dans la méthode ModelAdmin correspondante (s).Il y a plusieurs façons d'aborder cela. Je suppose que le plus facile serait de remplacer ModelAdmin.save_fromset. Ici, vous avez accès à tous les formulaires en ligne avant qu'ils ne soient sauvegardés.

+0

Merci de nous avoir conseillé d'utiliser Q() au lieu de boucler sur le jeu de requête. Je ne sais pas pourquoi j'ai décidé de faire une boucle dans ce cas, car j'utilise une requête similaire dans d'autres cas. – Maxim

+0

J'ai brièvement regardé la documentation de transaction.atomic(). Cela me permettrait-il de comparer des variables non encore sauvegardées pour la méthode propre? – Maxim

+0

Que voulez-vous dire par pas encore sauvegardé des variables? D'où viendraient vos * variables non encore enregistrées *? Vous comparez la nouvelle instance TimeRangeClass non sauvegardée aux instanciations existantes que vous avez chargées dans la mémoire via votre ensemble de requêtes. – Titusz