2011-05-24 3 views
9

Mes modèles:Django - UserProfile champ m2m dans l'admin - Erreur

class UserProfile(models.Model): 
    TYPES_CHOICES = (
     (0, _(u'teacher')), 
     (1, _(u'student')), 
    ) 
    user = models.ForeignKey(User, unique=True) 
    type = models.SmallIntegerField(default=0, choices=TYPES_CHOICES, db_index=True) 
    cities = models.ManyToManyField(City) 
class City(models.Model): 
    name = models.CharField(max_length=50) 
    slug = models.SlugField(max_length=50) 

En admin.py:

admin.site.unregister(User) 
class UserProfileInline(admin.StackedInline): 
    model = UserProfile 

class UserProfileAdmin(UserAdmin): 
    inlines = [UserProfileInline] 

admin.site.register(User, UserProfileAdmin) 

@receiver(post_save, sender=User) 
def create_profile(sender, instance, created, **kwargs): 
    """Create a matching profile whenever a user object is created.""" 
    if created: 
     profile, new = UserProfile.objects.get_or_create(user=instance) 

Mais quand j'ajouter un nouvel utilisateur et sélectionnez une ville que je reçois cette erreur: IntegrityError dans/admin/auth/user/add/ (1062, "Dupliquer l'entrée '3' pour la clé 'id_utilisateur" ")

Quel est le problème avec mon code? Si je ne sélectionne aucune ville, l'utilisateur est ajouté correctement. D'une certaine manière, l'utilisateur est ajouté à UserProfile plus d'une fois.

Répondre

20

J'ai eu ce même problème récemment. Cela prend tout son sens quand on y pense. Lorsque vous enregistrez un formulaire avec des inlines dans l'admin, il enregistre d'abord le modèle principal, puis procède à l'enregistrement de chaque en-ligne. Quand il enregistre le modèle, votre signal post_save est déclenché et un UserProfile est créé pour correspondre, mais maintenant il est temps de sauvegarder les inlines. Le UserProfile inline est considéré comme nouveau, car il n'existait pas auparavant (il n'a pas de valeur PK), il essaie donc de sauvegarder en tant que UserProfile entièrement nouveau et différent et vous obtenez cette erreur d'intégrité pour violation de la contrainte unique. La solution est simple. Il suffit de passer outre UserProfile.save:

def save(self, *args, **kwargs): 
    if not self.pk: 
     try: 
      p = UserProfile.objects.get(user=self.user) 
      self.pk = p.pk 
     except UserProfile.DoesNotExist: 
      pass 

    super(UserProfile, self).save(*args, **kwargs) 

Essentiellement, il vérifie simplement s'il y a un UserProfile existant pour l'utilisateur en question. Si c'est le cas, il place le pk de ce UserProfile sur celui-ci afin que Django fasse une mise à jour au lieu d'un create.

+0

Merci, maintenant ça marche! Il était si simple btw: D – robos85

+0

Merci! J'ai eu le même problème. Cette solution a du sens et a parfaitement fonctionné. – adam

+2

Excellemment expliqué. Peut-être que la documentation de Django sur le stockage de données utilisateur supplémentaires devrait être mise à jour pour le mentionner car l'exemple présenté dans la documentation échouera à fonctionner autrement. – CadentOrange

Questions connexes