Configuration / insertion dans une base de données plusieurs à plusieurs avec Python, SQLALchemy, Sqlite

StackOverflow https://stackoverflow.com/questions/1403084

Question

J'apprends le python et, en tant que premier projet, je m'occupe des flux RSS Twitter, j'analyse les données et les insère dans une base de données sqlite. J'ai réussi à analyser chaque entrée de fil dans une variable contenu (par exemple, "Vous devriez acheter plus bas ..."), une variable url (par exemple, u ' http://bit.ly/HbFwL ') et une liste hashtag (par exemple, #stocks', u '# stockmarket', u '# finance', u '# argent', u '# mkt']). J'ai également réussi à insérer ces trois informations dans trois colonnes distinctes dans un sqlite "RSSEntries". table, où chaque ligne est une entrée / tweet rss différente.

Cependant, je souhaite configurer une base de données dans laquelle il existe une relation plusieurs à plusieurs entre les entrées de flux RSS individuelles (c'est-à-dire les tweets individuels) et les hashtags associés à chaque entrée. J'ai donc configuré les tables suivantes avec sqlalchemy (la première table ne comprend que les URL de flux RSS des Twitterers que je veux télécharger et analyser):

RSSFeeds = schema.Table('feeds', metadata,
    schema.Column('id', types.Integer, 
        schema.Sequence('feeds_seq_id', optional=True), primary_key=True),
    schema.Column('url', types.VARCHAR(1000), default=u''),
)

RSSEntries = schema.Table('entries', metadata,
    schema.Column('id', types.Integer, 
        schema.Sequence('entries_seq_id', optional=True), primary_key=True),
    schema.Column('feed_id', types.Integer, schema.ForeignKey('feeds.id')),
    schema.Column('short_url', types.VARCHAR(1000), default=u''),
    schema.Column('content', types.Text(), nullable=False),
    schema.Column('hashtags', types.Unicode(255)),
)

tag_table = schema.Table('tag', metadata,
    schema.Column('id', types.Integer,
       schema.Sequence('tag_seq_id', optional=True), primary_key=True),
    schema.Column('tagname', types.Unicode(20), nullable=False, unique=True)
)

entrytag_table = schema.Table('entrytag', metadata,
    schema.Column('id', types.Integer,
        schema.Sequence('entrytag_seq_id', optional=True), primary_key=True),
    schema.Column('entryid', types.Integer, schema.ForeignKey('entries.id')),
    schema.Column('tagid', types.Integer, schema.ForeignKey('tag.id')),
)

Jusqu'à présent, je n'ai réussi à saisir que les trois informations principales dans le tableau RSSEntries à l'aide du code suivant (en abrégé, où ...)

engine = create_engine('sqlite:///test.sqlite', echo=True)
conn = engine.connect()
.........
conn.execute('INSERT INTO entries (feed_id, short_url, content, hashtags) VALUES 
    (?,?,?,?)', (id, tinyurl, content, hashtags))

Maintenant, voici l'énorme question. Comment insérer les données dans les tables feedtag et tagname ? C’est pour moi un véritable point de blocage, car pour commencer, la variable hasthag est actuellement une liste et chaque entrée de fil peut contenir un nombre compris entre 0 et 6 hashtags. Je sais comment insérer la liste entière dans une seule colonne, mais pas comment insérer uniquement les éléments de la liste dans des colonnes séparées (ou, dans cet exemple, des lignes). Un point de blocage plus important est la question générale de savoir comment insérer les hashtags individuels dans la table tagname quand une tagname peut être utilisée dans de nombreuses entrées de fil différentes, puis comment avoir les "associations". apparaissent correctement dans le tableau feedtag .

En résumé, je sais exactement à quoi doit ressembler chacune des tables, mais je ne sais pas comment écrire le code pour insérer les données dans la tagname et les tables feedtag . L'ensemble "plusieurs à plusieurs" la mise en place est nouvelle pour moi.

Je pourrais vraiment utiliser votre aide pour cela. Merci d'avance pour toute suggestion.

-Greg

P.S. - Modifier : grâce aux excellentes suggestions de Ants Aasma, j'ai pu presque faire en sorte que tout fonctionne. Plus précisément, les premier et deuxième blocs de code suggérés fonctionnent désormais correctement, mais je ne parviens pas à implémenter le troisième bloc de code. Je reçois l'erreur suivante:

Traceback (most recent call last):
  File "RSS_sqlalchemy.py", line 242, in <module>
    store_feed_items(id, entries)
  File "RSS_sqlalchemy.py", line 196, in store_feed_items
    [{'feedid': entry_id, 'tagid': tag_ids[tag]} for tag in hashtags2])
NameError: global name 'entry_id' is not defined

Ensuite, parce que je ne pouvais pas dire où Ants Aasma a obtenu le "entry_id". En partie, j’ai essayé de le remplacer par "entrées.id", pensant que cela pourrait insérer le "id". à partir des " entrées " table. Cependant, dans ce cas, j'obtiens cette erreur:

Traceback (most recent call last):
  File "RSS_sqlalchemy.py", line 242, in <module>
    store_feed_items(id, entries)
  File "RSS_sqlalchemy.py", line 196, in store_feed_items
    [{'feedid': entries.id, 'tagid': tag_ids[tag]} for tag in hashtags2])
AttributeError: 'list' object has no attribute 'id'

Je ne sais pas trop où se trouve le problème et je ne comprends pas vraiment où se trouve "& entry; id_entrée". partie s’intègre, j’ai donc collé ci-dessous l’ensemble de mes "insertions" pertinentes. code. Quelqu'un peut m'aider à voir ce qui ne va pas? Notez que je viens également de remarquer que j'appelais de manière incorrecte ma dernière table "feedtag_table". au lieu de " entrytag_table " Cela ne correspondait pas à mon objectif initial, consistant à associer les entrées de flux individuel à des hashtags, plutôt que des flux à des hashtags. J'ai depuis corrigé le code ci-dessus.

feeds = conn.execute('SELECT id, url FROM feeds').fetchall()

def store_feed_items(id, items):
    """ Takes a feed_id and a list of items and stored them in the DB """
    for entry in items:
        conn.execute('SELECT id from entries WHERE short_url=?', (entry.link,))
        s = unicode(entry.summary) 
        test = s.split()
        tinyurl2 = [i for i in test if i.startswith('http://')]
        hashtags2 = [i for i in s.split() if i.startswith('#')]
        content2 = ' '.join(i for i in s.split() if i not in tinyurl2+hashtags2)
        content = unicode(content2)
        tinyurl = unicode(tinyurl2)
        hashtags = unicode (hashtags2)
        date = strftime("%Y-%m-%d %H:%M:%S",entry.updated_parsed)

        conn.execute(RSSEntries.insert(), {'feed_id': id, 'short_url': tinyurl,
            'content': content, 'hashtags': hashtags, 'date': date})    

        tags = tag_table
        tag_id_query = select([tags.c.tagname, tags.c.id], tags.c.tagname.in_(hashtags))
        tag_ids = dict(conn.execute(tag_id_query).fetchall())
        for tag in hashtags:
            if tag not in tag_ids:
                result = conn.execute(tags.insert(), {'tagname': tag})
                tag_ids[tag] = result.last_inserted_ids()[0]

        conn.execute(entrytag_table.insert(),
            [{'feedid': id, 'tagid': tag_ids[tag]} for tag in hashtags2])
Était-ce utile?

La solution

Tout d'abord, vous devez utiliser le générateur SQL SQLAlchemy pour les insertions afin de donner à SQLAlcehemy un meilleur aperçu de ce que vous faites.

 result = conn.execute(RSSEntries.insert(), {'feed_id': id, 'short_url': tinyurl,
        'content': content, 'hashtags': hashtags, 'date': date})
 entry_id = result.last_insert_ids()[0]

Pour insérer des associations de balises dans votre schéma, vous devez d'abord rechercher vos identificateurs de balises et créer ceux qui n'existent pas:

tags = tag_table
tag_id_query = select([tags.c.tagname, tags.c.id], tags.c.tagname.in_(hashtags))
tag_ids = dict(conn.execute(tag_id_query).fetchall())
for tag in hashtags:
    if tag not in tag_ids:
        result = conn.execute(tags.insert(), {'tagname': tag})
        tag_ids[tag] = result.last_inserted_ids()[0]

Ensuite, insérez simplement les identifiants associés dans la feedtag_table . Vous pouvez utiliser le support executemany en transmettant une liste de dict à la méthode execute.

conn.execute(feedtag_table.insert(),
    [{'feedid': entry_id, 'tagid': tag_ids[tag]} for tag in hashtags])
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top