Foreign key from inside JSONB field

649 views
Skip to first unread message

João Miguel Neves

unread,
Oct 28, 2019, 4:44:57 AM10/28/19
to sqlal...@googlegroups.com
Hi,

I'm not sure this is supported or intended to work, but I'm trying to use a value inside a JSONB field in a relationship.
   
class Department(DeclarativeBase):
  __tablename__ = "department"
  department_id = Column(Integer, autoincrement=True, primary_key=True)

class Project(DeclarativeBase):
    __tablename__ = "project"

    project_id = Column(Integer, autoincrement=True, primary_key=True)
    meta = Column(MutableDict.as_mutable(JSONB), nullable=False, default={})
    department = relation(
        "Department",
        viewonly=True,
        primaryjoin="foreign(Department.department_id) == Project.meta['department_id'].astext.cast(Numeric)",
    )



When I try to access project.department I get the following error:

ProgrammingError: (psycopg2.errors.AmbiguousFunction) operator is not unique: unknown ->> unknown
LINE 3: ...vite": true, "send_project_record_invite": true}' ->> 'depar...
                                                             ^
HINT:  Could not choose a best candidate operator. You might need to add explicit type casts.

Is there a way to do this wth relationship? Fair enough if not supported, I can work around it with hybrid properties or column properties, was just trying a different way.

Thanks for a great library,
João

Mike Bayer

unread,
Oct 28, 2019, 12:33:02 PM10/28/19
to noreply-spamdigest via sqlalchemy
It might not work great with more complex usage so may not be worth it, however, the lazyload can succeed here with some more explicit casts, also the json attribute is the "foreign" part here because it's the part that references something else.

Here's your POC

from sqlalchemy import cast
from sqlalchemy import Column
from sqlalchemy import create_engine
from sqlalchemy import Integer
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.mutable import MutableDict
from sqlalchemy.orm import foreign
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Session

Base = declarative_base()


class Department(Base):
    __tablename__ = "department"
    department_id = Column(Integer, autoincrement=True, primary_key=True)


class Project(Base):
    __tablename__ = "project"

    project_id = Column(Integer, autoincrement=True, primary_key=True)
    meta = Column(MutableDict.as_mutable(JSONB), nullable=False, default={})
    department = relationship(
        "Department",
        viewonly=True,
        primaryjoin=lambda: Department.department_id
        == foreign(
            cast(Project.meta, JSONB)["department_id"].astext.cast(Integer)
        ),
    )


e = create_engine("postgresql://scott:tiger@localhost/test", echo=True)
Base.metadata.drop_all(e)
Base.metadata.create_all(e)

s = Session(e)

d1 = Department(department_id=1)
s.add(d1)
s.add(Project(meta={"department_id": 1}))
s.commit()

p1 = s.query(Project).first()
assert p1.department is d1
--
SQLAlchemy -
The Python SQL Toolkit and Object Relational Mapper
 
 
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+...@googlegroups.com.

João Miguel Neves

unread,
Oct 28, 2019, 4:26:17 PM10/28/19
to sqlal...@googlegroups.com
Brilliant, wasn't aware I could pass a function to primary join.

Thank you very much (for this, sqlalchemy and your amazing support),
João 

Reply all
Reply to author
Forward
0 new messages