SQLAlchemy не может найти связь внешнего ключа с непривилегированным пользователем - PullRequest
1 голос
/ 17 февраля 2011

У меня есть таблицы, созданные Django в базе данных PostgreSQL 8.4, где одна таблица «расширяет» другую. Одна таблица (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_foopayment имеют значения GRANT ed SELECT и UPDATE. Я пытался временно предоставить все разрешения для них, но безуспешно. Если это имеет значение, последовательность payments_payment_id_seq равна GRANT ed для SELECT и USAGE. Очевидно, схема foo является GRANT ed для USAGE.

Как мне определить отношение вручную в Python или сделать что-то на стороне БД, чтобы интроспекция работала для непривилегированного пользователя?

Также очень приветствуются советы по отладке проблемы, так как я полностью заблудился во внутренностях SA.

1 Ответ

2 голосов
/ 21 февраля 2011

Вы можете регистрировать запросы 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')
...