How do I write a group_concat function in sqlalchemy?

Question:

I am trying to re-write this mysql query in SQLAlchemy:

Schema:

CREATE TABLE `posts` (
    `post_id` INT UNSIGNED PRIMARY AUTO_INCREMENT,
    `post_name` VARCHAR(255)
) Engine=InnoDB;

CREATE TABLE `post_tags` (
    `tag_id` INT UNSIGNED PRIMARY AUTO_INCREMENT,
    `tag_name` VARCHAR(255)
) Engine=InnoDB;

CREATE TABLE `post_tags_map` (
    `map_id` INT PRIMARY AUTO_INCREMENT,
    `post_id` INT NOT NULL,
    `tags_id` INT NOT NULL,
    FOREIGN KEY `post_id` REFERENCES `posts` (`post_id`),
    FOREIGN KEY `post_id` REFERENCES `post_tags` (`tag_id`)
) Engine=InnoDB;

Query:

SELECT 
    posts.*,
    GROUP_CONCAT( post_tags.tag_name order by post_tags.tag_name ) AS tags

  FROM posts
    LEFT JOIN posts_tags_map
      ON posts_tags_map.post_id = posts.post_id
    LEFT JOIN post_tags
      ON posts_tags_map.tags_id = posts_tags.tag_id

  WHERE posts.post_id = 1
  GROUP BY post_id

Here’s what I have, but I keep getting:

1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SEPARATOR /))

rows = DBSession.query(Posts,func.group_concat(Post_Tags.tag_name.op('SEPARATOR')(literal_column('/')))).outerjoin(PostsTagsMap,Posts.post_id==PostsTagsMap.post_id).outerjoin(Post_Tags,PostsTagsMap.tags_id==Post_Tags.tag_id).group_by(Posts.post_id)
Asked By: user776942

||

Answers:

Defining a custom function element seems the easiest way to take care of the group_concat method.

from sqlalchemy.sql import expression
import sqlalchemy
from sqlalchemy.ext import compiler


class group_concat(expression.FunctionElement):
    name = "group_concat"


@compiler.compiles(group_concat, 'mysql')
def _group_concat_mysql(element, compiler, **kw):
    if len(element.clauses) == 2:
        separator = compiler.process(element.clauses.clauses[1])
    else:
        separator = ','

    return 'GROUP_CONCAT(%s SEPARATOR %s)'.format(
        compiler.process(element.clauses.clauses[0]),
        separator,
    )

And use it something like this:

query = select([
    table.c.some_column,
    expression.label(
        'grouped column',
        group_concat(
            table.c.some_oter_column,
            ' separator ',
        ),
    ),
]).group_by(table.c.some_column)
Answered By: Wolph

Actually, you did the group_concat bit right:

DBSession.query(
   Posts, 
   func.group_concat(Post_Tags.tag_name.op('SEPARATOR')(literal_column('/'))))<...>

I am not sure what exactly is going on, as it seems your schema is not valid, your query has misspelled table names, and judging by the error, you tried to feed python code into mysql.

For those who, like me, found this page while googling, the code bit above should be helpful, also there is the source, apparently: https://groups.google.com/forum/#!msg/sqlalchemy/eNbOoRJ425s/bScfQVat15EJ

Answered By: letitbee

I finally get this working.

>>> str(func.group_concat(Keyword.text.op('separator')(text('","'))))
'group_concat(keywords.text separator ",")'
Answered By: rexsheng

Wasted a lot of time testing the above examples and other random sources to find out how to change the standard delimiting character "," in the func.group_concat() … only to then follow intuition and find out that the syntax works exactly as you would anticipate: pass your delimiting character as an argument.
Such as:

from sqlalchemy import func
#write the query: db.session.query(...
...func.group_concat(Table.column_name, "; ")

So, modifying a previous answer’s example:

DBSession.query(
   Posts, 
   func.group_concat(Post_Tags.tag_name, "/"))<...>

Note: this is with Python 3.5, SQLite 3, Flask-SQLAlchemy 2.3.2 (SQLAlchemy 1.0.13 installed)

Answered By: GAINZ

I tried literal_column(' = ') but MYSQL would parse it into separator = other than separator '=', so I add triple quotes before and after as literal_column("""' = '""") which finally works.

Hope this would help someone.

Answered By: Jared
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.