Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Django 1.5.x] ManyToManyField self referencing

le liens semble "à l'envers"...

    1 juillet 2013 à 13:27:48

    Bonjour,

    J'ai un léger soucis avec mon code : j'ai un model qui a un lien manytomany sur lui même, avec une table intermédiaire. Jusque là tout va bien, j'arrive à ajouter / supprimer des éléments. 

    Le problème c'est que, si j'essaie d'utiliser l'attributs manytomany, (qui s'appelle "parents"), je ne vais pas avoir la liste des parents, mais celle des fils.

    Exemple : 

    >>> skill = Skill.objects.get(item__id=3307)
    >>> skill
    <Skill: Large Hybrid Turret (Rank 5; Per/Wil)>
    >>> skill.parents.all()
    [<Skill: Capital Hybrid Turret (Rank 7; Per/Wil)>, <Skill: Large Blaster Specialization (Rank 8; Per/Wil)>, <Skill: Large Railgun Specialization (Rank 8; Per/Wil)>]
    >>> skill.children.all()
    [<Skill: Medium Hybrid Turret (Rank 3; Per/Wil)>, <Skill: Gunnery (Rank 1; Per/Wil)>]
    

    Ici parents et children sont inversé au niveau des valeurs. Pourtant ma table intermédiaire est bien correcte en base (child_skill_id=3307 dans l'exemple ci-dessus) :

    mysql> SELECT i.name, sp.* 
        -> FROM `thing_skillparent` sp
        -> join thing_item i
        -> on i.id = sp.parent_skill_id
        -> where child_skill_id = 3307;
    +----------------------+-----------------+----------------+-------+
    | name                 | parent_skill_id | child_skill_id | level |
    +----------------------+-----------------+----------------+-------+
    | Medium Hybrid Turret |            3304 |           3307 |     3 |
    | Gunnery              |            3300 |           3307 |     5 |
    +----------------------+-----------------+----------------+-------+
    


    Du coup je me trouve un peu bloqué... :/ car mes fonctions "get_parents" et "get_children" ne fonctionnent pas (filtres incorrectes vu que l'attributs "parents" semblent être inversé avec son "related_name"...

    Voici les models que j'utilise : 

    Skill : 

    # ---------------------------------------------------------------------------
    # Skills
    class Skill(models.Model):
        CHARISMA_ATTRIBUTE = 164
        INTELLIGENCE_ATTRIBUTE = 165
        MEMORY_ATTRIBUTE = 166
        PERCEPTION_ATTRIBUTE = 167
        WILLPOWER_ATTRIBUTE = 168
        ATTRIBUTE_CHOICES = (
            (CHARISMA_ATTRIBUTE, 'Cha'),
            (INTELLIGENCE_ATTRIBUTE, 'Int'),
            (MEMORY_ATTRIBUTE, 'Mem'),
            (PERCEPTION_ATTRIBUTE, 'Per'),
            (WILLPOWER_ATTRIBUTE, 'Wil'),
        )
    
        ATTRIBUTE_MAP = {
            CHARISMA_ATTRIBUTE: ('cha_attribute', 'cha_bonus'),
            INTELLIGENCE_ATTRIBUTE: ('int_attribute', 'int_bonus'),
            MEMORY_ATTRIBUTE: ('mem_attribute', 'mem_bonus'),
            PERCEPTION_ATTRIBUTE: ('per_attribute', 'per_bonus'),
            WILLPOWER_ATTRIBUTE: ('wil_attribute', 'wil_bonus'),
        }
    
        item = models.OneToOneField(Item, primary_key=True)
        rank = models.SmallIntegerField()
        description = models.TextField()
        primary_attribute = models.SmallIntegerField(choices=ATTRIBUTE_CHOICES)
        secondary_attribute = models.SmallIntegerField(choices=ATTRIBUTE_CHOICES)
    
        parents = models.ManyToManyField('self'
                                        , blank=True
                                        , null=True
                                        , through="SkillParent"
                                        , related_name="children"
                                        , symmetrical=False)
        
        def __unicode__(self):
            return '%s (Rank %d; %s/%s)' % (self.item.name, self.rank, self.get_primary_attribute_display(),
                self.get_secondary_attribute_display())
        
        def __html__(self):
            return "<strong>Primary:</strong> %s / <strong>Secondary</strong>: %s<br><br>%s" % (
                self.get_primary_attribute_display(),
                self.get_secondary_attribute_display(),
                self.description.replace('\n', '<br>'),
            )
    
        def add_parent(self, skill, level):
            parent_skill, created = SkillParent.objects.get_or_create(
                child_skill=self,
                parent_skill=skill,
                level=level)
            return parent_skill
        
        def remove_parent(self, skill):
            SkillParent.objects.filter(
                child_skill=self,
                parent_skill=skill).delete()
            return
        
        def clean_parents(self):
            SkillParent.objects.filter(
                child_skill=self).delete()
            return
    
        def get_skill_parent(self):
            return SkillParent.objects.filter(
                child_skill=self)
                
        def get_parents(self):
            return self.parents.filter(
                parent_skill__parent_skill = self)
    
        def get_children(self):
            return self.children.filter(
                child_skill__child_skill = self)

    SkillParent 

    #----------------------------------------------------------------------
    #- Skill Parent
    class SkillParent(models.Model):   
        parent_skill = models.ForeignKey('Skill', related_name="child_skill")
        child_skill  = models.ForeignKey('Skill', related_name="parent_skill")
        level        = models.SmallIntegerField()
    
        def get_roman_level(self):
            return ['', 'I', 'II', 'III', 'IV', 'V'][self.level]

    Item quant à lui n'est qu'un model avec un id et un nom.

    Je vous remercie d'avance pour votre aide.

    -
    Edité par Kyria 1 juillet 2013 à 13:31:19

    • Partager sur Facebook
    • Partager sur Twitter
      2 juillet 2013 à 10:57:19

      Personne n'a d'idée ou d'axes de recherche ?

      J'ai bien cherché côté doc django pour la définition de la relation ManyToMany, mais je n'ai pas trouvé comment dire "dans quel sens" on doit faire le lien..

      (j'avoue que je ne comprend même pas comment il fait pour dire à quel champ de la classe SkillParent il se rattache pour déterminer le sens de la relation.)

      En fait, en y réfléchissant, j'ai l'impression d'avoir déclaré deux fois ma relation ManyToMany : 

      - une fois avec la propriété "parents" dans Skill()

      - une fois avec les foreign keys dans SkillPlan()

      Est-il judicieux de garder les deux ? Je pense que non vu que de mon point de vue, si j'ai le parent sans la propriété "level" contenu dans SkillPlan, ça n'a aucun sens en terme de donnée. Sachant que si j'utilise la méthode "get_skill_parent()", je n'ai pas de problème de "sens" (j'ai bien les bonnes infos où il faut)

      Ma question du coup c'est : est-ce que je peux enlever la propriété "parents" de la class Skill ? (si oui, ça résous mon problème en fait)

      Par contre ça m'intéresserait d'avoir plus de précisions sur les ManyToMany (autre que la doc django qui reste assez vague pour tout les problème de hiérarchie) si quelqu'un en a.

      -
      Edité par Kyria 2 juillet 2013 à 10:58:20

      • Partager sur Facebook
      • Partager sur Twitter

      [Django 1.5.x] ManyToManyField self referencing

      × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
      × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
      • Editeur
      • Markdown