SQLAlchemy 无法找到与非特权用户的外键关系

发布于 2024-10-17 17:02:16 字数 3134 浏览 2 评论 0原文

我在 PostgreSQL 8.4 数据库中有一个 Django 创建的表,其中一个表“扩展”另一个表。一张表 (FooPayment) 具有主键,该主键引用另一张表 (Payment)。在 SQL 中,它看起来像这样:

CREATE TABLE foo.payments_payment
(
  id integer NOT NULL DEFAULT nextval('payments_payment_id_seq'::regclass),
  user_id integer NOT NULL,
  ...
  CONSTRAINT payments_payment_pkey PRIMARY KEY (id),
  CONSTRAINT payments_payment_user_id_fkey FOREIGN KEY (user_id)
      REFERENCES auth.auth_user (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED
)

CREATE TABLE foo.payments_foopayment
(
  payment_ptr_id integer NOT NULL,
  ...
  CONSTRAINT payments_foopayment_pkey PRIMARY KEY (payment_ptr_id),
  CONSTRAINT payments_foopayment_payment_ptr_id_fkey FOREIGN KEY (payment_ptr_id)
      REFERENCES foo.payments_payment (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED,
  ...
)

但是,由于各种原因,我没有使用 Django ORM,并且我正在尝试从 SQLAlchemy 访问表(我使用的是版本 0.6.6,与 pip 一起安装) ):

# Base = declarative_base()
...

class Payment(Base):
    __tablename__ = 'payments_payment'
    __table_args__ = {'schema': 'foo', 'autoload': True}
    user = relation(User, backref='payments')

class FooPayment(Payment):
    __tablename__ = 'payments_foopayment'
    __table_args__ = {'schema': 'foo', 'autoload': True}

当我以超级用户身份执行此操作时,一切正常。当我作为低特权用户连接时,出现异常:

Traceback (most recent call last):
  File "./test.py", line 3, in <module>
    from foos import models
  File "./foos/models.py", line 127, in <module>
    class FooPayment(Payment):
  File "lib/python2.6/site-packages/sqlalchemy/ext/declarative.py", line 1167, in __init__
    _as_declarative(cls, classname, cls.__dict__)
  File "lib/python2.6/site-packages/sqlalchemy/ext/declarative.py", line 1099, in _as_declarative
    ignore_nonexistent_tables=True)
  File "lib/python2.6/site-packages/sqlalchemy/sql/util.py", line 260, in join_condition
    "between '%s' and '%s'.%s" % (a.description, b.description, hint))
sqlalchemy.exc.ArgumentError: Can't find any foreign key relationships between 'payments_payment' and 'payments_foopayment'.

当我作为低特权用户与 PgAdmin3 连接时,我在 GUI 中看到了关系。我还可以用这个语句看到它,SQLAlchemy 本身发出问题:

SELECT conname, pg_catalog.pg_get_constraintdef(oid, true) as condef
    FROM  pg_catalog.pg_constraint r
    WHERE r.conrelid = 16234 AND r.contype = 'f'
    ORDER BY 1

它正确返回一行,包含

"payments_foopayment_payment_ptr_id_fkey"; "FOREIGN KEY (payment_ptr_id) REFERENCES payments_payment(id) DEFERRABLE INITIALLY DEFERRED"

至于数据库权限,payments_ payment 和payments_foo payment 都是GRANT 编辑了SELECTUPDATE。我尝试暂时授予他们所有权限,但没有成功。如果这很重要,则会为 SELECTUSAGE 授予序列 payment_ payment_id_seq 。显然,模式 foo 已针对 USAGE 进行了 GRANT 编辑。

我应该如何在Python中手动定义关系,或者在数据库端做一些事情,以便内省对非特权用户起作用?

关于调试问题的提示也非常受欢迎,因为我完全迷失在 SA 内部。

I have a Django-created tables in PostgreSQL 8.4 database, where one table "extends" another. One table (FooPayment) has primary key, which references another table (Payment). In SQL it looks like this:

CREATE TABLE foo.payments_payment
(
  id integer NOT NULL DEFAULT nextval('payments_payment_id_seq'::regclass),
  user_id integer NOT NULL,
  ...
  CONSTRAINT payments_payment_pkey PRIMARY KEY (id),
  CONSTRAINT payments_payment_user_id_fkey FOREIGN KEY (user_id)
      REFERENCES auth.auth_user (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED
)

CREATE TABLE foo.payments_foopayment
(
  payment_ptr_id integer NOT NULL,
  ...
  CONSTRAINT payments_foopayment_pkey PRIMARY KEY (payment_ptr_id),
  CONSTRAINT payments_foopayment_payment_ptr_id_fkey FOREIGN KEY (payment_ptr_id)
      REFERENCES foo.payments_payment (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED,
  ...
)

However, I've not to use Django ORM for various reasons, and I'm trying to access the tables from SQLAlchemy (I'm using version 0.6.6, as was installed with pip):

# Base = declarative_base()
...

class Payment(Base):
    __tablename__ = 'payments_payment'
    __table_args__ = {'schema': 'foo', 'autoload': True}
    user = relation(User, backref='payments')

class FooPayment(Payment):
    __tablename__ = 'payments_foopayment'
    __table_args__ = {'schema': 'foo', 'autoload': True}

When I'm doing this as superuser, everything works. When I'm connecting as low-privileged user I get an exception:

Traceback (most recent call last):
  File "./test.py", line 3, in <module>
    from foos import models
  File "./foos/models.py", line 127, in <module>
    class FooPayment(Payment):
  File "lib/python2.6/site-packages/sqlalchemy/ext/declarative.py", line 1167, in __init__
    _as_declarative(cls, classname, cls.__dict__)
  File "lib/python2.6/site-packages/sqlalchemy/ext/declarative.py", line 1099, in _as_declarative
    ignore_nonexistent_tables=True)
  File "lib/python2.6/site-packages/sqlalchemy/sql/util.py", line 260, in join_condition
    "between '%s' and '%s'.%s" % (a.description, b.description, hint))
sqlalchemy.exc.ArgumentError: Can't find any foreign key relationships between 'payments_payment' and 'payments_foopayment'.

When I'm connecting as this low-privileged user with PgAdmin3, I see the relationship in GUI. I can also see it with this statement, SQLAlchemy issues itself:

SELECT conname, pg_catalog.pg_get_constraintdef(oid, true) as condef
    FROM  pg_catalog.pg_constraint r
    WHERE r.conrelid = 16234 AND r.contype = 'f'
    ORDER BY 1

Which properly returns a row, containing

"payments_foopayment_payment_ptr_id_fkey"; "FOREIGN KEY (payment_ptr_id) REFERENCES payments_payment(id) DEFERRABLE INITIALLY DEFERRED"

As for database permissions, both payments_payment and payments_foopayment are GRANTed SELECT and UPDATE. I've tried temporarily granting all permissions on them, without any success. If this matters, a seqence payments_payment_id_seq is GRANTed for SELECT and USAGE. Obviously, schema foo is GRANTed for USAGE.

How should I either define the relationship manually in Python, or do something on DB side, so introspection would work for non-privileged user?

Hints on debugging the problem are also very much welcomed, as I'm completely lost in SA internals.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

送你一个梦 2024-10-24 17:02:16

您可以记录 SQLAlchemy 的查询,并比较不同用户发生的情况。

import logging

# Early in your main()
logging.basicConfig()
logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
logging.getLogger('sqlalchemy.orm').setLevel(logging.INFO)

logging.DEBUG 还记录响应数据。

据我所知,为了反射,SQLAlchemy 使用表 OID 并查询 pg_catalog;你举了一个例子。代码位于SQLAlchemy.dialects.postgresql.base中。

如果自动加载的权限让您感到悲伤,您可以在代码中声明如下所示的关系:

class FooPayment(Payment):
    payment_ptr_id = Column(Integer, ForeignKey(Payment.id), primary_key=True)
    payment = relationship(
                  Payment, foreign_keys=[payment_ptr_id], backref='foo_payment')

You can log the queries of SQLAlchemy, and compare what happens with different users.

import logging

# Early in your main()
logging.basicConfig()
logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
logging.getLogger('sqlalchemy.orm').setLevel(logging.INFO)

logging.DEBUG logs the response data as well.

As far as I can tell, for reflection, SQLAlchemy uses table OIDs and queries pg_catalog; you gave an example. The code is in SQLAlchemy.dialects.postgresql.base.

If the permissions for autoload give you grief, you can declare the relationship in code with something like this:

class FooPayment(Payment):
    payment_ptr_id = Column(Integer, ForeignKey(Payment.id), primary_key=True)
    payment = relationship(
                  Payment, foreign_keys=[payment_ptr_id], backref='foo_payment')
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文