On Thu, Jan 24, 2019 at 9:30 AM Антонио Антуан <a.ch....@gmail.com> wrote:
>
> Greetings.
>
> I want to implement NestedType for the dialect of ClickHouse database.
>
> At the first sight it looks like copmosite type, but not not completely:
> 1. Each subtype considers like separate column
> 2. `NestedType` is just syntax sugar:
> CREATE TABLE test_table1 (
>     date Date,
>     object Nested (
>         type String,
>         content String
>
>     )
>
> )...
>
>
> CREATE TABLE test_table2 (
>     date Date,
>     object.type String,
>     object.content String
> )...
> Both of DDL's create tables with the same structure.
> 3. Queries can be done only using full format, like that:
> SELECT count(*), objects.type FROM test_table group by objects.type
> or
> SELECT count(*), test_table.objects.type FROM test_table group by 
> test_table.objects.type
>
>
>
> My question is how can I implement that behaviour? I've tried to adjust 
> sqlachemy_utils.CompositeType, but it looks like there are a lot of 
> differences.

I'm not familiar with CompositeType but the general approach here, if
you're looking for the expression behavior, is that your type also
needs to have a Comparator class that will implement that dotted
behavior.    Examples are at
https://docs.sqlalchemy.org/en/latest/core/custom_types.html#redefining-and-creating-new-operators
where you can see how to build out these behaviors though you will
likely need your operation to spit out a sub column('name', subtype)
object when someone calls upon "somecol.somefield".   This is not a
simple problem when not familiar with the API so POC is down below.


>
> Looks like I need to use separated DeclarativeMeta class and register each 
> member of nested field (with sqlalchemy.ext.declarative.base._add_attribute).

DeclarativeMeta has nothing to do with datatypes, nor does the ORM in
general.     A composite datatype is strictly Core stuff.



> Another problem: append parent column name to nested column name, because I 
> want to make that:
>
>
> class Test(Base):
>     __tablename__ = 'test'
>     object = Column(
>         Nested(
>              Column('type', String),
>              Column('content', String),
>         )
>
>     )

I'd advise using lower-case column:  "from sqlalchemy import column"
since these columns are not table-bound.

>
>
> Session.query(Test).filter(Test.object.type == 'type')...
> # generates that query...
> select ... from test where test.object.type = 'type'

sure here's a POC

from sqlalchemy import Column
from sqlalchemy import column
from sqlalchemy import Integer
from sqlalchemy import String
from sqlalchemy.ext.compiler import compiles
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import Session
from sqlalchemy.sql.elements import ColumnClause
from sqlalchemy.types import UserDefinedType


class NestedColumn(ColumnClause):
    """serves the role of the "nested" column in a nested type expression."""

    def __init__(self, parent, sub_column):
        self.parent = parent
        self.sub_column = sub_column
        super(NestedColumn, self).__init__(
            sub_column.name, sub_column.type, _selectable=self.parent.table
        )


@compiles(NestedColumn)
def _comp(element, compiler, **kw):
    return "%s.%s" % (
        compiler.process(element.parent),
        compiler.visit_column(element, include_table=False),
    )


class Nested(UserDefinedType):
    def __init__(self, *cols):
        self.cols = cols
        self._col_dict = {col.name: col for col in cols}

    class Comparator(UserDefinedType.Comparator):
        def __getattr__(self, key):
            # this is a particular convention for serving names that conflict
            # with existing elements on Column which you can choose
            # to replace with something else, see below
            str_key = key.rstrip("_")
            try:
                sub = self.type._col_dict[str_key]
            except KeyError:
                raise AttributeError(key)
            else:
                # probably want to cache these eventually
                return NestedColumn(self.expr, sub)

    comparator_factory = Comparator


Base = declarative_base()


class Test(Base):
    __tablename__ = "test"
    id = Column(Integer, primary_key=True)
    object = Column(Nested(column("type", String), column("content", String)))


s = Session()

# CONVENTION: suffix element with "_" if the element you are accessing is
# already an element on a Column object.    Better approach - have it be
# Test.object.elements.content , Test.object.elements.type, or
# Test.object['content'] Test.object['type'], or something like that
print(s.query(Test.object.content).filter(Test.object.type_ == "foo"))





>
> --
> SQLAlchemy -
> The Python SQL Toolkit and Object Relational Mapper
>
> http://www.sqlalchemy.org/
>
> To post example code, please provide an MCVE: Minimal, Complete, and 
> Verifiable Example. See http://stackoverflow.com/help/mcve for a full 
> description.
> ---
> You received this message because you are subscribed to the Google Groups 
> "sqlalchemy" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to sqlalchemy+unsubscr...@googlegroups.com.
> To post to this group, send email to sqlalchemy@googlegroups.com.
> Visit this group at https://groups.google.com/group/sqlalchemy.
> For more options, visit https://groups.google.com/d/optout.

-- 
SQLAlchemy - 
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to