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

Bibliothèques tierces Python Discussion :

[sqlalchemy] Création d'une relation Many to Many avec attributs


Sujet :

Bibliothèques tierces Python

  1. #1
    Candidat au Club
    Profil pro
    Inscrit en
    Août 2010
    Messages
    4
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2010
    Messages : 4
    Points : 2
    Points
    2
    Par défaut [sqlalchemy] Création d'une relation Many to Many avec attributs
    Bonjour à tous !
    J'essaye d'intégrer un SGBDR à une application qui utilise actuellement le module pickle.
    Les besoins de l'application ayant évolué mais restant toujours assez restreint, je me suis tourné vers sqlite que je connais déjà un petit peu.

    Pour la première fois, j'ai regardé du côté des ORM et j'ai retenu sqlalchemy qui à l'air abouti et complet.

    Je me heurte par contre à un gros problème. Je n'arrive pas à 'mapper' 2 de mes classes en Many to Many avec un attribut en plus des deux clefs étrangères dans la table associative.

    Un peu de code pour expliquer mieux :

    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
     
    from sqlalchemy import *
    from sqlalchemy import  create_engine
    from sqlalchemy.orm import *
    from sqlalchemy.orm.collections import *
    from sqlalchemy.ext.associationproxy import *
    from sqlalchemy.ext.declarative import *
     
    engine = create_engine('sqlite:///test.db', echo=True)
    Base = declarative_base(bind=engine)
    metadata = Base.metadata
     
     
    # association table
    post_keywords = Table('post_keywords', metadata,
        Column('post_id', Integer, ForeignKey('posts.id')),
        Column('keyword_id', Integer, ForeignKey('keywords.id'))
    )
     
    class User(Base):
        __tablename__ = 'users'
     
        id = Column(Integer, primary_key=True)
        name = Column(String)
        fullname = Column(String)
        password = Column(String)
     
        def __init__(self, name, fullname, password):
            self.name = name
            self.fullname = fullname
            self.password = password
     
     
    class BlogPost(Base):
        __tablename__ = 'posts'
     
        id = Column(Integer, primary_key=True)
        user_id = Column(Integer, ForeignKey('users.id'))
        author = relation(User, backref=backref('posts', lazy='dynamic'))
        body = Column(Text)
     
        # many to many 
        keywords = relation('Keyword', secondary=post_keywords, backref='posts')
     
        def __init__(self, body, author):
            self.author = author
            self.body = body
     
    class Keyword(Base):
        __tablename__ = 'keywords'
     
        id = Column(Integer, primary_key=True)
        keyword = Column(String(50), nullable=False, unique=True)
     
        def __init__(self, keyword):
            self.keyword = keyword
     
    metadata.create_all(engine) 
     
    Session = sessionmaker(bind=engine)
    session = Session()
    ed_user = User('zed', 'Zed Bows', 'password')
    jo_user = User('jo', 'Jo lbdeo', 'pwd')
    session.add(ed_user)
    session.add(jo_user)
     
     
    key1 = Keyword("keyword1")
    key2 = Keyword("keyword2")
    session.add(key1)
    session.add(key2)
     
     
    post1 = BlogPost("bodybodydbodezfdkjsdflksfdbody", jo_user)
    post1.keywords.append(key1)
    post1.keywords.append(key2)
    session.add(post1)
     
    session.commit()
    Je voudrais donc rajouter à ce code, la possibilité d'avoir une colonne dans la table "post_keywords" qui ne soit pas une clef étrangère mais un simple attribut.

    Ça fait un moment que je suis bloqué avec ce mistère et j'espère que quelqu'un ici présent trouvera une réponse à mon problème

    Bonne soirée !

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Salut,

    Je voudrais donc rajouter à ce code, la possibilité d'avoir une colonne dans la table "post_keywords" qui ne soit pas une clef étrangère mais un simple attribut.
    Après avoir modifié le code ainsi:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    post_keywords = Table('post_keywords', metadata,
        Column('post_id', Integer, ForeignKey('posts.id')),
        Column('keyword_id', Integer, ForeignKey('keywords.id')),
        Column('added', String()),
    )
    Il ne fonctionne pas plus mal qu'avant.
    Donc, c'est quoi le problème?
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  3. #3
    Candidat au Club
    Profil pro
    Inscrit en
    Août 2010
    Messages
    4
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2010
    Messages : 4
    Points : 2
    Points
    2
    Par défaut
    'Lut !

    @wiztricks: Oui c'est l'idée. En fait je ne l'ai pas précisé dans mon post de départ, mais c'est exactement ça que je veux faire. À part que, de cette manière là, on a bien une colonne qui est rajoutée dans le schéma sql mais elle ne correspond à aucune variable.
    Je voudrais mapper cette nouvelle colonne avec un attribut d'instance d'une de mes classes (User par exemple)

    Merci en tout cas pour cette réponse rapide

    Bonne soirée

  4. #4
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Salut

    Citation Envoyé par joubu Voir le message
    Je voudrais mappée cette nouvelle colonne avec un attribut d'instance d'une de mes classes (User par exemple)
    "Mapper" avec un ORM tel que SQLAlchemy est loin d'être "context free".
    A défaut de cas d'utilisation plus précis, quel défaut trouvez vous à utiliser les "property" (ou descriptors) de Python pour ajouter la colonne à User qui fasse le boulot "dessous".
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  5. #5
    Candidat au Club
    Profil pro
    Inscrit en
    Août 2010
    Messages
    4
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2010
    Messages : 4
    Points : 2
    Points
    2
    Par défaut
    Bonjour !
    "Mapper" avec un ORM tel que SQLAlchemy est loin d'être "context free".
    Oui effectivement, en plus il était un peu tard hier et il est possible que je n'ai pas été très compréhensible...

    Mon cas d'utilisation :
    Le programme est un jeu de cartes. Une partie contient n joueurs et chaque joueur peut jouer (ou avoir joué) dans m parties.

    Lorsque j'instancie un joueur, il faut que je spécifie quel numéro il a dans la partie (ça correspond en fait à l'ordre du jeu, le joueur avec un num = 1 jouera en premier).
    Par contre, en BDD, ce numéro n'a rien à faire dans la table 'players' mais bien dans la table d'association parties_players.


    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
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
     
    from sqlalchemy import *
    from sqlalchemy import  create_engine
    from sqlalchemy.orm import *
    from sqlalchemy.orm.collections import *
    from sqlalchemy.ext.associationproxy import *
    from sqlalchemy.ext.declarative import *
     
    engine = create_engine('sqlite:///test.db', echo=True)
    Base = declarative_base(bind=engine)
    metadata = Base.metadata
     
     
    parties_players = Table('parties_players', metadata,
        Column('partie_id', Integer, ForeignKey('parties.id')),
        Column('player_id', Integer, ForeignKey('players.id')),
        Column('num', Integer)
    )
     
    class Player(Base):
        __tablename__ = 'players'
     
        id = Column(Integer, primary_key=True)
        name = Column(String(), nullable=False, unique=True)
     
        def __init__(self, name):
            self.name = name
            self.num = None
     
    class Partie(Base):
        __tablename__ = 'parties'
     
        id = Column(Integer, primary_key=True)
        preneur_id = Column(Integer, ForeignKey('players.id'))
        preneur = relation(Player, backref=backref('preneur', lazy='dynamic'))
        name = Column(String(), nullable=False)
     
        # many to many 
        players = relation('Player', secondary=parties_players, backref='players')
     
        def __init__(self, name, players):
            self.name = name
            self.players = players
            self.preneur = None
     
    metadata.create_all(engine) 
     
    Session = sessionmaker(bind=engine)
    session = Session()
    p1 = Player('toto')
    p2 = Player('tata')
    players = [p1, p2]
     
    for i in range(1,  len(players) + 1):
        players[i-1].num = i
     
    session.add(p1)
    session.add(p2)
     
    partie = Partie("Partie 1", players)
    partie.preneur = p1
     
    session.add(partie)
     
    session.commit()
    J'aimerais donc que l'attribut Player.num corresponde à la colonne parties_players.num. Car actuellement la colonne est bien créée mais est vide.

    A défaut de cas d'utilisation plus précis, quel défaut trouvez vous à utiliser les "property" (ou descriptors) de Python pour ajouter la colonne à User qui fasse le boulot "dessous".
    Je ne vois pas comment faire avec les property mais si tu penses que ça peux répondre à mon problème, pourrais tu me donner un petit exemple?

    Voilà, google ne me donne pas énormément d'exemples sur ce cas précis et surtout lorsque je veux utiliser la méthode déclarative de sqlalchemy.

    Bonne journée à tous

  6. #6
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 776
    Points
    36 776
    Par défaut
    Salut,

    Argh... la declarative interface de SQLAlchemy est toujours aussi beurk.

    Personnellement, j'utilise plutôt Elixir pour faire du 'vite'/déclaratif et SQLAlchemy 'natif' pour le reste: donc non seulement je suis un peu sec mais en plus 'bof' et manque de temps font que çà restera comme çà.

    La solution via SQLAlchemy 'natif' est documentée ici: associationproxy


    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  7. #7
    Candidat au Club
    Profil pro
    Inscrit en
    Août 2010
    Messages
    4
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2010
    Messages : 4
    Points : 2
    Points
    2
    Par défaut
    'Lut !

    Ok pour la solution native, j'étais déjà tombé dessus. Je ne connais sqlachemy que depuis 3 jours et il me semblait plus intéressant et propre d'utiliser l'interface déclarative.
    J'avais lu en vitesse qu'il existait elixir mais sans regarder de près je m'étais dis que ça ne devait que rajouter une couche supplémentaire.
    Je viens de tomber sur un tuto plutôt bien fait (http://elixir.ematia.de/trac/wiki/TutorialDivingIn) et une discussion qui réponds à ma question d'origine mais en utilisant elixir sur les groupes google.

    Je vais donc arrêter de m'obstiner à utiliser la manière déclarative de sqlalchemy (en natif) et utiliser la couche elixir.

    Merci en tout cas à toi, wiztricks pour tes réponses.
    Je passe en résolu même si j'ai toujours pas ma réponse de base, mais elle devient inutile !

    Bonne soirée !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 5
    Dernier message: 10/08/2014, 17h25
  2. Comment récupérer une liste avec une relation one to many ?
    Par tomlaurent dans le forum Hibernate
    Réponses: 1
    Dernier message: 07/11/2011, 07h16
  3. Problème lors d'un delete avec une relation one-to-many
    Par el_harrathi dans le forum Développement Web en Java
    Réponses: 2
    Dernier message: 01/11/2011, 15h01
  4. Réponses: 30
    Dernier message: 17/05/2011, 12h25
  5. Réponses: 4
    Dernier message: 18/06/2007, 08h30

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