question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

"Dataclasses with Declarative Table" with inheritance not working as expected

See original GitHub issue

Describe the bug Using inheritance with dataclasses I get an error.

Expected behavior

The ability to use some base classes with the “Dataclasses with Declarative Table” approach.

To Reproduce

import dataclasses
from typing import TYPE_CHECKING

from sqlalchemy import Column, Integer, String, Table
from sqlalchemy.orm import registry

mapper_registry = registry()


# From Mike Bayer's "Building the app" talk, adapted to use dataclasses style mapping
# https://speakerdeck.com/zzzeek/building-the-app
@dataclasses.dataclass
class SurrogatePK:
    """
    A mixin that adds a surrogate integer 'primary key' column named ``id``
    to any declarative-mapped dataclasses.
    """

    if TYPE_CHECKING:
        # populated by the sqlalchemy at mapping time, defined here to help IDEs only
        __table__: Table

    __sa_dataclass_metadata_key__ = "sa"

    id: int = dataclasses.field(
        init=False,
        metadata={"sa": Column(Integer, primary_key=True)},
    )


# https://docs.sqlalchemy.org/en/14/orm/mapping_styles.html#example-two-dataclasses-with-declarative-table
@mapper_registry.mapped
@dataclasses.dataclass
class User(SurrogatePK):
    __tablename__ = "user"
    full_name: str = dataclasses.field(
        metadata={"sa": Column(String, primary_key=True)},
    )

Error

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-1-c9dca729fc03> in <module>
     32 @mapper_registry.mapped
     33 @dataclasses.dataclass
---> 34 class User(SurrogatePK):
     35     __tablename__ = "user"
     36     full_name: str = dataclasses.field(

~/Development/Personal/fastapi-postgres/.venv/lib/python3.9/site-packages/sqlalchemy/orm/decl_api.py in mapped(self, cls)
    570 
    571         """
--> 572         _as_declarative(self, cls, cls.__dict__)
    573         return cls
    574 

~/Development/Personal/fastapi-postgres/.venv/lib/python3.9/site-packages/sqlalchemy/orm/decl_base.py in _as_declarative(registry, cls, dict_)
    123     # args passed separately.
    124 
--> 125     return _MapperConfig.setup_mapping(registry, cls, dict_, None, {})
    126 
    127 

~/Development/Personal/fastapi-postgres/.venv/lib/python3.9/site-packages/sqlalchemy/orm/decl_base.py in setup_mapping(cls, registry, cls_, dict_, table, mapper_kw)
    174             cfg_cls = _ClassScanMapperConfig
    175 
--> 176         return cfg_cls(registry, cls_, dict_, table, mapper_kw)
    177 
    178     def __init__(self, registry, cls_):

~/Development/Personal/fastapi-postgres/.venv/lib/python3.9/site-packages/sqlalchemy/orm/decl_base.py in __init__(self, registry, cls_, dict_, table, mapper_kw)
    296         self._setup_declared_events()
    297 
--> 298         self._scan_attributes()
    299 
    300         with mapperlib._CONFIGURE_MUTEX:

~/Development/Personal/fastapi-postgres/.venv/lib/python3.9/site-packages/sqlalchemy/orm/decl_base.py in _scan_attributes(self)
    362 
    363             if not class_mapped and base is not cls:
--> 364                 self._produce_column_copies(attributes_for_class, base)
    365 
    366             for name, obj in attributes_for_class(base):

~/Development/Personal/fastapi-postgres/.venv/lib/python3.9/site-packages/sqlalchemy/orm/decl_base.py in _produce_column_copies(self, attributes_for_class, base)
    497         for name, obj in attributes_for_class(base):
    498             if isinstance(obj, Column):
--> 499                 if getattr(cls, name) is not obj:
    500                     # if column has been overridden
    501                     # (like by the InstrumentedAttribute of the

AttributeError: type object 'User' has no attribute 'id'

Versions.

  • OS: macOS 11.0.1
  • Python: 3.9
  • SQLAlchemy: 1.4.0b2.dev0dev 7bdb1f3
  • Database: PostgreSQL
  • DBAPI: asyncpg

Additional context

Have a nice day!

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:6 (5 by maintainers)

github_iconTop GitHub Comments

1reaction
zzzeekcommented, Jan 26, 2021

thanks for testing! thought I had merged it, merged now.

0reactions
michaeloliverxcommented, Jan 26, 2021

Actually I just tested with that patch file, can confirm it works now:

import dataclasses
from datetime import datetime
from typing import TYPE_CHECKING

from sqlalchemy import Column, DateTime, Integer, String, Table
from sqlalchemy.orm import registry

mapper_registry = registry()


@dataclasses.dataclass
class SQLAlchemyDataclass:
    if TYPE_CHECKING:
        __table__: Table
    __sa_dataclass_metadata_key__ = "sa"


@dataclasses.dataclass
class SurrogatePK:
    __sa_dataclass_metadata_key__ = "sa"
    id: int = dataclasses.field(
        init=False,
        metadata={"sa": Column(Integer, primary_key=True)},
    )


@dataclasses.dataclass
class TimeStampMixin:
    __sa_dataclass_metadata_key__ = "sa"
    created_at: datetime = dataclasses.field(
        init=False, metadata={"sa": Column(DateTime, default=datetime.utcnow)}
    )
    updated_at: datetime = dataclasses.field(
        init=False,
        metadata={
            "sa": Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
        },
    )


@mapper_registry.mapped
@dataclasses.dataclass
class User(SQLAlchemyDataclass, SurrogatePK, TimeStampMixin):
    __tablename__ = "user"

    auth0_id: str = dataclasses.field(
        metadata={"sa": Column(String, primary_key=True)},
    )


User(auth0_id="auth0-id")

Read more comments on GitHub >

github_iconTop Results From Across the Web

SqlAlchemy dataclass behavior and declarative mixins ...
I have a set of classes that I drafted as pure python dataclasses. Here's a stripped down example that behaves as I would...
Read more >
Mapping Class Inheritance Hierarchies
Mapping Class Inheritance Hierarchies¶. SQLAlchemy supports three forms of inheritance: single table inheritance, where several types of ...
Read more >
PEP 557 – Data Classes - Python Enhancement Proposals
Users of these classes are free to use inheritance and metaclasses without any interference from Data Classes. The decorated classes are truly “normal”...
Read more >
Raymond Hettinger - Dataclasses: The code generator to end ...
Speaker: Raymond HettingerThe PEP 557 dataclasses module is available ... This talk shows what problem the module solves, explains its key ...
Read more >
Jdbi 3 Developer Guide
Being able to express a query in raw SQL makes it possible for programmers and data engineers to speak the same language and...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found