IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Ruby on Rails Discussion :

polymorphism ou self-referentioal-joins


Sujet :

Ruby on Rails

  1. #1
    Membre averti Avatar de Javix
    Inscrit en
    Juin 2007
    Messages
    531
    Détails du profil
    Informations forums :
    Inscription : Juin 2007
    Messages : 531
    Points : 353
    Points
    353
    Par défaut polymorphism ou self-referentioal-joins
    Salut à tous! J'au un peu du mal à résoudre un cas de modélisation. je ne sais pas quoi choisir pour m'en sortir.
    Je m'explique.
    Il y a un Projet à faire qui est caractérisé comme suivant:

    Client pour lequel on va faire un projet:
    - nom du client
    - prénom du client
    - téléphone du client
    - mail
    - département
    - coordinator,
    etc.

    Dans chaque projet peuvent être appliqués un plusieurs Externes qui ont les mêmes champs que Client et un plusieurs Départements de l'entreprise où chaque Département a les mêmes champs que Client. Les Externes et Départements, eux aussi, peuvent participer dans un ou plusieurs Projets.

    Qu'est-ce qui mieux pour ne pas se répéter et ne pas créer les mêmes tables pour Externes, Départements et Clients, 'polymorphic associations' ou 'self-referential joins' vu que la relation Externes<=>Projets et Départements<=>Projets sont 'many-to-many', et Client<=>Projets comme 'one-to-many'. Je suis un peu perdu entre les deux. Merci pour vos conseils.

  2. #2
    Membre régulier
    Profil pro
    Inscrit en
    Avril 2006
    Messages
    121
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2006
    Messages : 121
    Points : 92
    Points
    92
    Par défaut
    Salut,

    Je n'ai pas tout bien compris dans tes explications... Tout dépend de ce que tu veux faire de ces données avec ton application, il y a plusieurs possibilités d'autant plus que tu ne donnes pas beaucoup d'informations.

    D'après ce que tu écris je verrais bien 3 tables :
    PROJETS.
    PERSONNES, contenant des clients, des salariés de l'entreprise, des "externes". Ajouter dans ce cas un champ pour différencier ces trois types de personne.
    DEPARTEMENT, pas forcémment utile si on part du principe qu'un salarié de l'entreprise n'appartient qu'à un et un seul département.

    Relation n:n entre PROJETS et PERSONNES
    Relation 1:n ou n:n entre PERSONNES et DEPARTEMENTS

  3. #3
    Membre averti Avatar de Javix
    Inscrit en
    Juin 2007
    Messages
    531
    Détails du profil
    Informations forums :
    Inscription : Juin 2007
    Messages : 531
    Points : 353
    Points
    353
    Par défaut
    Oui, j'essaye d'utiliser STI (single-table-inheritance) comme ça dans mes modèles:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
     
    class Project < ActiveRecord::Base
      has_many :developments
      has_many :participants, :through=>:developments
     
    def clients
        arr = []
        participants.each do |p|
          arr << p if p.is_a?(Client)
        end
        return arr
      end
    end
     
    class Development < ActiveRecord::Base
      belongs_to :project
      belongs_to :participant
    end
     
    class Participant < ActiveRecord::Base
      has_many :developments
      has_many :projects, :through=>:developments
     
    end
     
    class Client < Participant
    end
    class Extern < Participant
    end
    Et quand j'essay (dans la console) de faire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
    prj = Project.create(:desc=>'web')
    => #<Project id: 1, desc: "web", created_at: "2009-02-03 12:57:30", updated_at: "2009-02-03 12:57:30">
    cl = Client.create(:title=>'Mr', :name=>'toto')
     
    => #<Client id: 1, type: "Client", title: "Mr", name: "toto", created_at: "2009-02-03 12:58:59", updated_at: "2009-02-03 12:58:59">
     
    >> prj.clients
    prj.clients
     
    NoMethodError: You have a nil object when you didn't expect it!
     
    You might have expected an instance of Array.
     
    The error occurred while evaluating nil.include?
    Pourquoi Rails ne reconnait pas l'appel de la méthode prj.clients ?

  4. #4
    Membre averti Avatar de Javix
    Inscrit en
    Juin 2007
    Messages
    531
    Détails du profil
    Informations forums :
    Inscription : Juin 2007
    Messages : 531
    Points : 353
    Points
    353
    Par défaut
    Bon j'ai trouvé. Il fallait ajouter 'self' dans la méthode 'clients':
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    def clients
        arr = []
        self.participants.each do |p|
          arr << p if p.is_a?(Client)
        end
        return arr
      end
    Mais comment ajouter un ou plusieurs clients au même projet?
    J'ai créé un 'setter' dans le modèle Project:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    def clients=(data)
        self.clients.concat(data.split)
      end
    Mais ça ne fonctionne pas pour les raisons suivantes:
    - data que je passe à la méthode peut être un object Client (c'est pour ça que je le split pour obtenir an Array) ou un array des objets Client. Comment les distinguer?

  5. #5
    Membre averti Avatar de Javix
    Inscrit en
    Juin 2007
    Messages
    531
    Détails du profil
    Informations forums :
    Inscription : Juin 2007
    Messages : 531
    Points : 353
    Points
    353
    Par défaut
    J'ai modifié la méthode 'clients=:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
     
     def clients=(data)
        arr=[]
        unless data.is_a? Array
          arr = data.split
        else
          arr = data.to_a
        end
     
        if self.clients
          self.clients.concat(arr)
        else
          self.clients << arr
        end    
      end
    Mais là Rails n'est pas content en m'envoyant l'erreur:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    prj.clients = cli
     
    NoMethodError: Attempt to call private method
     
            from c:/ruby/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/attribute_methods.rb:236:in `method_missing'
     
    ...../app/models/project.rb:16:in `clients='
     
            from (irb):4

  6. #6
    Membre averti Avatar de Javix
    Inscrit en
    Juin 2007
    Messages
    531
    Détails du profil
    Informations forums :
    Inscription : Juin 2007
    Messages : 531
    Points : 353
    Points
    353
    Par défaut
    Quel con je suis, - j'appelle la méthode 'split sur un objet 'Client'

  7. #7
    Membre averti Avatar de Javix
    Inscrit en
    Juin 2007
    Messages
    531
    Détails du profil
    Informations forums :
    Inscription : Juin 2007
    Messages : 531
    Points : 353
    Points
    353
    Par défaut
    Finalement il n'y plus d'erreurs après que j'ai modifié la méthode clients=
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
     def clients=(data)
        arr=[]
        unless data.is_a? Array
          arr << data
        else
          arr = data.to_a
        end    
        return self.clients.concat(arr)
      end
    Mais il n'y aucun nouveau record dans la table developments créé. Une idée?

  8. #8
    Membre éprouvé

    Profil pro
    Inscrit en
    Mai 2005
    Messages
    657
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2005
    Messages : 657
    Points : 910
    Points
    910
    Par défaut
    Finalement il n'y plus d'erreurs après que j'ai modifié la méthode clients=
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     def clients=(data)
        arr=[]
        unless data.is_a? Array
          arr << data
        else
          arr = data.to_a
        end    
        return self.clients.concat(arr)
      end
    Alors là je dis chapeau
    Il y a tellement d'incohérences dans ton code que je ne sais pas par où commencer.

    1) arr = data.to_a : si data est effectivement un Array, lui envoyer to_a n'a strictement aucune utilité.
    2) Les 6 premieres lignes de ta méthode pourraient se résumer à une seule : arr = data.to_a (ou arr = data.is_a?(Array) ? data : [data] pour ne pas lancer de warning).
    3) return est implicite sur la dernière ligne d'une methode
    4) concat renvoie la concaténation de 2 tableaux, mais ne les modifie pas, pas étonnant que tu ne voies rien changer dans la base de données
    5) si ta méthode avait effectivement fonctionnée, clients= aurait ajouté des clients, si il n'y a rien qui te choque dans un objet qui se comporte comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    >> obj.toto
    ['a', 'b']
    >> obj.toto = ['c', 'd']
    >> obj.toto
    ['a', 'b', 'c', 'd']

    Je pense que tu devrais commencer par réfléchir à ce que dont tu as vraiment besoin.
    Sauf si j'ai loupé quelque chose, ceci devrait fonctionner :
    Pour prj.clients, ça devrait être aussi possible avec un named_scope mais si ta méthode fonctionne c'est peut-être même plus simple

  9. #9
    Membre averti Avatar de Javix
    Inscrit en
    Juin 2007
    Messages
    531
    Détails du profil
    Informations forums :
    Inscription : Juin 2007
    Messages : 531
    Points : 353
    Points
    353
    Par défaut
    merci pour tes éclaircissements.
    D'abord j'ai modifié la méthode clients=:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    def clients=(data)
        if data.is_a? Array
          data
        else
          data.to_a
        end
    end
    Voici ce que ça a donné:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     
    Loading development environment (Rails 2.2.2)
     
    >> p =Project.find(1)
    p =Project.find(1)
     
    => #<Project id: 1, desc: "web", created_at: "2009-02-03 12:57:30", updated_at: "2009-02-03 12:57:30">
     
    >> client = Client.find(1)
    client = Client.find(1)
    ...app/models/project.rb:18: warning: default `to_a' will be obsolete
     
    p.clients=client
     
    => #<Client id: 1, type: "Client", title: "Mr", name: "toto", created_at: "2009-02-03 12:58:59", updated_at: "2009-02-03 12:58:59">
     
    >> p.clients
    p.clients
     
    => []
    En suit, j'ai juste (comme tu l'a indiqué précédemment) testé ça:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    arr = ['A', 'B']
    arr = ['C', 'D']
    puts arr.inspect
    Le résultat évidemment est:
    et non pas
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    ["A", "B", "C", "D"]
    Qu'est-ce j'ai encore mal compris?

  10. #10
    Membre averti Avatar de Javix
    Inscrit en
    Juin 2007
    Messages
    531
    Détails du profil
    Informations forums :
    Inscription : Juin 2007
    Messages : 531
    Points : 353
    Points
    353
    Par défaut
    J'ai besoin d'associer un ou plusieurs clients à un ou plusieurs projets.
    J'ai loupé un truc, - il fallait faire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    project.participants << client
    au lieu de :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    project.clients = client
    Ca a l'aire de fonctionner.Merci
    Mais si j'essaye:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    project.clients=client
     
    TypeError: class or module required
     
            from C:...app/models/project.rb:15:in `is_a?'
     
            from C:.../app/models/project.rb:15:in `clients='

Discussions similaires

  1. Faire une relation many-to-many sur une seule table (self-join)
    Par mpepito dans le forum Ruby on Rails
    Réponses: 3
    Dernier message: 11/08/2014, 13h24
  2. self join foireux
    Par LeHibou2 dans le forum Requêtes
    Réponses: 4
    Dernier message: 02/01/2013, 10h35
  3. Pas de JOIN sous Oracle (vraiment dommage...)
    Par Isildur dans le forum Langage SQL
    Réponses: 7
    Dernier message: 15/03/2007, 11h28
  4. Export d'une vue avec LEFT JOIN
    Par schnourf dans le forum MS SQL Server
    Réponses: 3
    Dernier message: 22/05/2003, 13h57
  5. PB avec "self" dans une dll
    Par DiJiRiDouS dans le forum Langage
    Réponses: 2
    Dernier message: 21/02/2003, 09h32

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo