2011-06-17 5 views
18

J'ai une petite application qui permet à un utilisateur d'évaluer une vidéo.Django - save() mise à jour sur la clé en double

L'utilisateur ne peut évaluer qu'une seule fois. J'ai donc défini l'unicité sur le modèle.

Mais il devrait pouvoir changer son taux. Ainsi, le save() devrait mettre à jour le double clé

class VideoRate(models.Model): 
    """Users can Rate each Video on the criterias defined for the topic""" 
    user = models.ForeignKey(User) 
    video = models.ForeignKey(VideoFile) 
    crit = models.ForeignKey(VideoCrit) 
    rate = models.DecimalField(max_digits=2, decimal_places=1, choices=RATE_CHOICES) 
    class Meta: 
    unique_together = (('user', 'video', 'crit'),) 
    verbose_name = 'Video Rating' 

Si je

rate = VideoRate(user_id=1, video_id=1, crit_id=1, rate=2) 
rate.save() 

Il économise la note, mais si je

rate = VideoRate(user_id=1, video_id=1, crit_id=1, rate=3) 
rate.save() 

Je reçois l'erreur normale

IntegrityError: (1062, "Duplicate entry '1-1-1' for key 'user_id'") 

Même si j'utilise force_update=True (puisque basé uniquement sur les clés primaires)

Existe-t-il un moyen de mettre à jour la notation si elle existe déjà sans avoir à vérifier les données avant?

Répondre

28

Pour mettre à jour un classement existant, vous devez en fait avoir celui que vous souhaitez mettre à jour. Si vous connaissez l'objet peut ne pas exister, utilisez get_or_create:

rate, created = VideoRate.objects.get_or_create(user_id=1, video_id=1, crit_id=1) 
rate.rate = 2 
rate.save() 

Vous pouvez court-circuiter le processus en utilisant update():

VideoRate.objects.filter(user_id=1, video_id=1, crit_id=1).update(rate=2) 

Mais cela silencieusement échouera si la cote n'existe pas - il n'en créera pas.

+0

+1: La première option fera 2 ou 3 requêtes, tandis que la seconde fera l'affaire 1. – sdolan

+0

Semble pas mal. Vous voulez dire que Django ne peut pas effectuer un 'INSERT INTO ... ON DUPLICATE KEY UPDATE ...'? –

+3

Non, parce que c'est une extension spécifique à MySQL, et Django travaille avec de nombreuses bases de données. –

7

D'abord, vous devez vérifier si le classement existe. Ainsi, vous pouvez soit utiliser ce Daniel Roseman dit ou utiliser existe, mais vous ne pouvez pas résoudre ce avec une simple mise à jour depuis la mise à jour ne crée pas de nouveaux records ...

rating = 2 
rate, created = VideoRate.objects.get_or_create(user_id=1, video_id=1, crit_id=1, 
    defaults={'rate':rating})#if create, also save the rate infdormation 

if not created:# update 
    rate.rate = rating 
    rate.save() 

Vous pouvez utiliser par défaut passer exrta arguments, donc si elle est un insert, enregistrement de la base sera créé avec toutes les informations nécessaires et vous n'avez pas besoin de le mettre à jour à nouveau ...

Documentation

Mise à jour: Cette réponse est assez ancienne tout comme la question. Comme @peterthomassen mention, Django ont maintenant update_or_create() méthode

+0

Le mot clé 'defaults' est également très intéressant –

+1

Les valeurs de l'argument' defaults' sont également utilisées pour la mise à jour dans le cas où l'objet n'a pas besoin d'être créé dans la base de données, voir https://docs.djangoproject.com /fr/1.11/ref/models/querysets/#update-or-create. Par conséquent, la partie 'if' de votre code est no-op. – peterthomassen

+0

@peterthomassen merci pour la notification. Mise à jour de la réponse – FallenAngel

Questions connexes