2014-06-10 5 views
0

I ont le modèle suivant qui représente les adresses IP:Django + MySQL IntegrityError sur save()

class Ip(models.Model): 
    ip = models.CharField(max_length=20, primary_key=True) 
    ... 

Procédé envoie des paramètres IP à une fonction qui doit UPSERT lignes IP dans la base de données.

Cela fonctionne bien, jusqu'à ce que le processus commence à « bombardent » avec les adresses IP, qui parfois (environ 1 à toutes les quelques demandes de centaines) jette l'erreur suivante:

IntegrityError: (1062, "Duplicate entry '1.2.3.4' for key 'PRIMARY'")

Pourquoi cela?
Pourquoi Django essaie-t-il d'INSÉRER lorsque cette ligne existe déjà (elle est définie comme clé primaire)?
Comment puis-je résoudre ce problème sans sélectionner manuellement et en décidant INSERT ou UPDATE?

Nous utilisons Django 1.5.1

EDIT:
Voilà comment nous le upsert:

obj = Ip(ip='1.2.3.4', ...) 
obj.save() 

Selon les docs (et notre expérience), cela devrait effectuer une opération de UPSERT, sur la base la clé primaire ip dont a toujours la valeur.

+0

Comment sauvegardez-vous votre modèle? – knbk

+0

s'il vous plaît coller un petit code sur la façon dont vous le faites, nous pouvons proposer une solution basée sur cela. Accroding à moi que vous avez spécifié l'IP comme clé primaire ainsi quand la même IP vient donne l'erreur d'intégrité –

+0

Édité avec le code qui enregistre le modèle – user1102018

Répondre

1

La méthode save est soumise à une condition de concurrence concernant la clé primaire. Il détermine d'abord si un enregistrement avec cette clé primaire existe et si une instruction update est exécutée, sinon une instruction insert est exécutée. Si une requête simultanée insère un nouvel enregistrement entre ces deux requêtes, il y aura une erreur d'intégrité.

Ce qui suit va gérer une condition de course (sauf si les enregistrements sont supprimés entre les requêtes):

import sys 
from django.utils import six 

try: 
    obj.save() 
except IntegrityError: 
    # The save might have been subject to a race condition. 
    # If it is, a record with this object's pk exists, so try to update it. 
    exc_info = sys.exc_info() 
    try: 
     obj.save(force_update=True) 
    except: 
     six.reraise(*exc_info) 

Si quelque chose d'autre est faux, cela augmentera la première, exception initiale, donc il n'y a pas IntegrityError cachés s à part celui qui est causé par cette condition de race. Je sais que c'est un peu bavard pour une sauvegarde simple, mais vous pouvez toujours remplacer la méthode save sur votre modèle pour le faire pour vous.

+0

Merci @knbk. Le fonctionnement de notre système, une adresse IP peut entrer dans le processus une seule fois, donc les conditions de course sont peu probables. Néanmoins, je vais essayer votre suggestion et la mettre à jour ici plus tard. – user1102018