1

I have declared models which have many to many relationships with each other. Below is the module code:

import sqlalchemy
from sqlalchemy.ext import declarative


class Base(object):
    pass


DeclarativeBase = declarative.declarative_base(cls=Base)


repositories_tags = sqlalchemy.Table(
    'repositories_tags',
    DeclarativeBase.metadata,
    sqlalchemy.Column(
        'tag_id',
        sqlalchemy.Integer,
        sqlalchemy.ForeignKey('tags.id_')
    ),
    sqlalchemy.Column(
        'repo_id',
        sqlalchemy.Integer,
        sqlalchemy.ForeignKey('repositories.id_')
    ),
)


class Tag(DeclarativeBase):
    __tablename__ = 'tags'

    id_ = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True)
    name = sqlalchemy.Column(sqlalchemy.String(30), unique=True)
    popularity = sqlalchemy.Column(sqlalchemy.Integer, default=lambda: 0)
    association = sqlalchemy.orm.relationship(
        'Repository',
        secondary=repositories_tags,
        backref=sqlalchemy.orm.backref('labels', lazy='dynamic')
    )


class Repository(DeclarativeBase):
    __tablename__ = 'repositories'

    id_ = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True)
    name = sqlalchemy.Column(sqlalchemy.String(100), unique=True)
    description = sqlalchemy.Column(sqlalchemy.String(500))
    downloads = sqlalchemy.Column(sqlalchemy.Integer, default=lambda: 0)
    uri = sqlalchemy.Column(sqlalchemy.String(500), unique=True)

Here Tags and Repository models have many to many relationship with each other. I need to retrieve repositories associated with given tags. One of last failed try is:

tag_ids = session.query(_models.Tag.id_)\
.filter(_models.Tag.name.in_(tag_names))\
.order_by(_models.Tag.popularity.desc())\
.all()

# unable to write the following esp. the filter part
session.query(_models.labels)\
.filter(_models.labels.tag_id.in_(tag_ids))\
.order_by(_models.Repository.downloads.desc())\
.all()

How can I get the repositories with specified tags. Any help would be much appreciated.

Below is the sample data:

Tags table:

+-----+------+------------+
| id_ | name | popularity |
+-----+------+------------+
|   1 | tag1 |          4 |
|   2 | tag2 |          6 |
|   3 | tag3 |          9 |
|   4 | tag4 |          3 |
|   5 | tag5 |          3 |
+-----+------+------------+

Repositories table:

+-----+---------+------------------+-----------+-------------------+
| id_ | name    | description      | downloads | uri               |
+-----+---------+------------------+-----------+-------------------+
|   1 | repo1   | Basic repo.      |       342 | alembic-base.v1   |
|   2 | repo2   | Test repo.       |       250 | usd_dev.v2        |
|   3 | repo3   | Some other repo. |       410 | usd_dev.anim      |
|   4 | repo4   | Fourth repo.     |       500 | nodes_editor.v1   |
|   5 | repo5   | Last one.        |       467 | usd_standalone.v1 |
+-----+---------+------------------+-----------+-------------------+

Labels table:

+--------+---------+
| tag_id | repo_id |
+--------+---------+
|      3 |       1 |
|      2 |       1 |
|      1 |       2 |
|      2 |       2 |
|      1 |       3 |
|      4 |       4 |
|      2 |       4 |
|      5 |       5 |
|      2 |       5 |
+--------+---------+

Query I am looking for is:

SELECT repositories.*
  FROM repositories
  JOIN labels
    ON repositories.id_ = labels.repo_id
  JOIN tags
    ON tags.id_ = labels.tag_id
 WHERE tags.name in ("tag1", "tag2");
Program Questions
  • 440
  • 2
  • 6
  • 20
  • Just to be clear: must a label have all the tags, or just any? – Ilja Everilä Feb 01 '18 at 21:42
  • Sorry for not being clear, `labels` table contain all the tags and corresponding repositories. – Program Questions Feb 02 '18 at 04:54
  • Got confused by `_models.labels` since you've not included a definition for it, but do you in fact mean `_models.Repository.labels`? And what I was trying to ask is: are you trying to find those Repositories that have **all** the given tags (identified by your `tag_names` / `tag_ids`), or just any tag from that set? So if `tag_names` contained `['foo', 'bar']`, would Repositories with *foo* **only** be ok? If not, you need relational division. This is btw the reason why you should include *sample data* and *expected output* (compared to actual) in the question. – Ilja Everilä Feb 02 '18 at 07:34
  • My apologies, I've added the sample data and required query too. Hope it's clear now :) – Program Questions Feb 03 '18 at 04:20
  • > are you trying to find those Repositories that have all the given tags. Yes. > since you've not included a definition for it, but do you in fact mean _models.Repository.labels. repositories_tags is the labels table. Yes it is `_models.Repository.labels` – Program Questions Feb 03 '18 at 04:21
  • The query you've shown as an example of what you're after will fetch repositories that have any of the listed tags, not all. Thank you for the additions, they did clear things up quite a bit. Please review the proposed dupe target if you find it useful. – Ilja Everilä Feb 03 '18 at 05:13
  • Thank you, yes that solution worked. :) Thanks again ! – Program Questions Feb 03 '18 at 19:00

0 Answers0