Обнаружение ссылок на объект SQLAlchemy - PullRequest
8 голосов
/ 17 февраля 2010

У меня есть много классов моделей с соотношениями между ними с интерфейсом CRUD для редактирования. Проблема в том, что некоторые объекты не могут быть удалены, поскольку есть другие объекты, ссылающиеся на них. Иногда я могу настроить правило ON DELETE для обработки этого случая, но в большинстве случаев я не хочу автоматического удаления связанных объектов, пока они не будут связаны вручную. В любом случае, я хотел бы представить редактору список объектов, относящихся к текущему, и выделить те, которые препятствуют его удалению из-за ограничения FOREIGN KEY. Есть ли готовое решение для автоматического обнаружения рефереров?

Обновление

Задание кажется довольно распространенным (например, django ORM показывает все зависимости), поэтому мне интересно, что решения пока нет.

Предлагаются два направления:

  1. Перечислите все отношения текущего объекта и просмотрите их backref. Но нет гарантии, что все отношения имеют backref определение. Более того, в некоторых случаях backref не имеет смысла. Хотя я могу определить это везде, мне не нравится делать это так, и это ненадежно.
  2. (предложено van и stephan) Проверьте все таблицы объекта MetaData и соберите зависимости из их свойства foreign_keys (например, код sqlalchemy_schemadisplay можно использовать, благодаря комментариям Стефана). Это позволит перехватывать все зависимости между таблицами , но мне нужны зависимости между модельными классами . Некоторые внешние ключи определены в промежуточных таблицах и не имеют моделей, соответствующих им (используется как secondary в отношениях). Конечно, я могу пойти дальше и найти подходящую модель (мне еще предстоит найти способ сделать это), но она выглядит слишком сложной.

Решение

Ниже приведен метод класса базовой модели (разработанный для декларативного расширения), который я использую в качестве решения. Он не идеален и не отвечает всем моим требованиям, но работает для текущего состояния моего проекта. Результат собран в виде словаря словарей, поэтому я могу показать их сгруппированными по объектам и их свойствам. Я еще не решил, будет ли это хорошей идеей, поскольку список судей иногда огромен, и я вынужден ограничить его некоторым разумным числом.

def _get_referers(self):
    db = object_session(self)
    cls, ident = identity_key(instance=self)
    medatada = cls.__table__.metadata
    result = {}
    # _mapped_models is my extension. It is collected by metaclass, so I didn't
    # look for other ways to find all model classes.
    for other_class in medatada._mapped_models:
        queries = {}
        for prop in class_mapper(other_class).iterate_properties:
            if not (isinstance(prop, PropertyLoader) and \
                    issubclass(cls, prop.mapper.class_)):
                continue
            query = db.query(prop.parent)
            comp = prop.comparator
            if prop.uselist:
                query = query.filter(comp.contains(self))
            else:
                query = query.filter(comp==self)
            count = query.count()
            if count:
                queries[prop] = (count, query)
        if queries:
            result[other_class] = queries
    return result

Спасибо всем, кто помог мне, особенно Стефану и Вану.

Ответы [ 3 ]

6 голосов
/ 17 февраля 2010

SQL: Я абсолютно не согласен с S.Lott ' answer . Я не знаю о готовом решении, но определенно возможно , чтобы обнаружить все таблицы, которые имеют ограничения ForeignKey для данной таблицы. Нужно правильно использовать INFORMATION_SCHEMA представления, такие как REFERENTIAL_CONSTRAINTS, KEY_COLUMN_USAGE, TABLE_CONSTRAINTS и т. Д. См. Пример SQL Server . С некоторыми ограничениями и расширениями большинство версий новых реляционных баз данных поддерживают стандарт INFORMATION_SCHEMA. Когда у вас есть вся информация о FK и объект (строка) в таблице, необходимо выполнить несколько операторов SELECT, чтобы получить все остальные строки в других таблицах, которые ссылаются на данную строку, и предотвратить ее удаление.

SqlAlchemy: Как отметил stephan в своем комментарии, если вы используете orm с backref для отношений, тогда вам будет довольно легко получить список из родительских объектов, которые сохраняют ссылку на объект, который вы пытаетесь удалить, потому что эти объекты в основном являются сопоставленными свойствами вашего объекта (child1.Parent).

Если вы работаете с Table объектами sql alchemy (или не всегда используете backref для отношений), то вам придется получить значения foreign_keys для всех таблиц, а затем для всех этих ForeignKey Вызовите метод references(...), предоставив таблицу в качестве параметра. Таким образом, вы найдете все FK (и таблицы), которые имеют ссылку на таблицу, к которой относится ваш объект. Затем вы можете запросить все объекты, которые сохраняют ссылку на ваш объект, создав запрос для каждого из этих FK.

1 голос
/ 17 февраля 2010

Как правило, нет способа "обнаружить" все ссылок в реляционной базе данных.

В некоторых базах данных они могут использовать декларативную ссылочную целостность в форме явного внешнегоОграничения Key или Check.

Но для этого нет необходимости.Он может быть неполным или непоследовательным.

Любой запрос может включать отношение FK, которое не объявлено.Без юниверса всех запросов вы не можете знать отношения, которые используются, но не объявлены.

Чтобы вообще найти "источники ссылок", вы должны знать структуру базы данных и иметь все запросы.

0 голосов
/ 17 февраля 2010

Для каждого класса модели вы можете легко увидеть, являются ли все его отношения «один ко многим» пустыми, просто запросив список в каждом случае и посмотрев, сколько записей в нем содержится. (Возможно, есть и более эффективный способ, реализованный в терминах COUNT.) Если есть какие-либо внешние ключи, относящиеся к объекту, и у вас правильно установлены отношения объекта, то хотя бы один из этих списков будет ненулевым в длину.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...