Форум программистов, компьютерный форум, киберфорум
Python: Web
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.83/6: Рейтинг темы: голосов - 6, средняя оценка - 4.83
5 / 5 / 0
Регистрация: 25.09.2017
Сообщений: 569

Особенности ограниченности SQLAlchemy по проверке внешних ключей

27.11.2023, 17:26. Показов 1417. Ответов 4
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Есть таблица Info в которой есть три связанных между собой внешних ключа (city_id, substate_id, country_id) <-> (City, Substate, Country), и бывают ситуации, когда необязательно чтобы все эти внешние ключи были заполнены. Но для таблицы Info требуется проверить, чтобы внешний ключ city_id, ссылающийся на город info.city_id = city.id соответствовал тому, что и значение внешнего ключа такого города city.substate_id было таким же как и значение внешнего ключа, ссылающийся на регион info.substate_id = substate.id при операции записи.

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
from sqlalchemy import (
    ARRAY, Boolean, Column, DateTime, CheckConstraint, Enum, ForeignKey, ForeignKeyConstraint, Integer, JSON, MetaData, String, 
    UniqueConstraint, Table
)
 
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
 
Base = declarative_base()
 
class Country(Base):
    __tablename__ = "Country"
    id = Column(Integer, primary_key=True)
    title = Column(String(60), unique=True, nullable=False)
    substates = relationship("SubState", back_populates="substate")
    profiles = relationship("Info", back_populates="country")
 
class Substate(Base,):
    __tablename__ = "Substate"
    id = Column(Integer, primary_key=True)
    title = Column(String(200), nullable=False)
    country_id = Column(ForeignKey("Country.id"))
    country = relationship("Country", back_populates="cities")
    cities = relationship("City", back_populates="substate")
 
class City(Base):
    __tablename__ = "City"
    id = Column(Integer, primary_key=True)
    title = Column(String(200), nullable=False)
    substate_id = Column(ForeignKey("Substate.id"))
    substate = relationship("Substate", back_populates="cities")
 
class Info(Base):
    __tablename__ = "Info"
    __table_args__ = (
        CheckConstraint(
            '''city_id IS NULL OR city_id IN (SELECT id FROM City WHERE City.substate_id IN 
               (SELECT id FROM Substate WHERE Substate.id = substate_id AND Substate.country_id = country_id))
            ''',
            name="city_id_fk"
        ),
        CheckConstraint(
            '''substate_id IS NULL OR substate_id IN (
                SELECT id FROM Substate WHERE Substate.id = substate_id AND Substate.country_id = country_id
            )
            ''',
            name="substate_id_fk"
        )
    )
    id = Column(Integer, primary_key=True)
    title = Column(String(200), nullable=False)
    city_id = Column(ForeignKey("City.id"), nullable=True)
    substate_id = Column(ForeignKey("Substate.id"), nullable=True)
    country_id = Column(ForeignKey("Country.id"), nullable=True)
    countries = relationship("Country", back_populates="profiles")
Так и знал, что какой-то геморрой вылезет с проверкой с помощью CheckConstraint - столкнулся с ограничениями:

Bash
1
2
 sqlalchemy.exc.NotSupportedError: (psycopg2.errors.FeatureNotSupported) cannot use subquery in check constraint
web_1          | LINE 13: ...T city_id_fk CHECK (city_id IS NULL OR city_id IN (SELECT...
"Нельзя использовать вложенные запросы в check constraint"

FastApi вообще ничего не предоставляет из коробки. Какой-то казуальный фреймвёрк, если его можно так назвать. Как вот данные проверять, если такие ситуации возникают. В валидаторах на основе моделей pydantic нет доступа ко всем данным, поступающим из запроса. Это просто типизация данных, но можно сказать, расширенная проверка значения, но в пределах одного параметра (поля).

А как быть, если данные связаны между собой и одно из полей требуется заполнить в зависимости от значения другого. В DRF это всё реализовывается в сериализаторах.
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
27.11.2023, 17:26
Ответы с готовыми решениями:

Создание внешних ключей
Здравствуйте! Возможно, мой вопрос прозвучит по-детски, но я только начала знакомиться с PostgreSQL и не знаю, как в графическом клиенте...

Ошибки внешних ключей
Написал статью Ошибки внешних ключей: errno 150, MySQL error 1005 и другие, в которой перечислил все известные мне случаи возникновения...

Создание внешних ключей
Добрый день. Написал собственный редактор моделей сущность-связь, который должен генерировать sql скрипт(это задание у нас в универе...

4
 Аватар для kazak
3604 / 2744 / 356
Регистрация: 11.03.2009
Сообщений: 6,306
28.11.2023, 10:46
Цитата Сообщение от Alex_py Посмотреть сообщение
А как быть, если данные связаны между собой и одно из полей требуется заполнить в зависимости от значения другого. В DRF это всё реализовывается в сериализаторах.
Со сотороны БД используются тригеры, со стороны клиента у sqlalchemy есть свои инструменты https://uralbash.ru/articles/2... y_trigger/
1
5 / 5 / 0
Регистрация: 25.09.2017
Сообщений: 569
29.11.2023, 05:54  [ТС]
Не знаю, что здесь теперь "не нравится":

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from sqlalchemy import (
    Column, ForeignKey, UniqueConstraint, Table
)
 
from sqlalchemy.orm import backref, mapper, relationship
 
association_friends = Table(
    "Friends",
    metadata,
    Column("profile_id", ForeignKey("Profile.id"), primary_key=True),
    Column("friend_id", ForeignKey("Profile.id"), primary_key=True),
    UniqueConstraint("profile_id", "friend_id", name="friendship")
)
 
class VKProfile(Base, UpdateInterface):
 
    __tablename__ = "Profile"
    
    friends = relationship(
        "Profile", 
        secondary=association_friends, 
        primaryjoin=id==association_friends.c.profile_id, 
        secondaryjoin=id==association_friends.c.friend_id,
        backref="Profile"
    )


Делаю по документации https://docs.sqlalchemy.org/en... ny-to-many, но у меня просто таблица ссылается на саму себя.


Код из примера по ссылке выше:
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
association_table = Table(
    "association_table",
    Base.metadata,
    Column("left_id", ForeignKey("left_table.id"), primary_key=True),
    Column("right_id", ForeignKey("right_table.id"), primary_key=True),
)
 
 
class Parent(Base):
    __tablename__ = "left_table"
    id = Column(Integer, primary_key=True)
    children = relationship(
        "Child", secondary=association_table, back_populates="parents"
    )
 
 
class Child(Base):
    __tablename__ = "right_table"
    id = Column(Integer, primary_key=True)[B][/B]
    parents = relationship(
        "Parent", secondary=association_table, back_populates="children"
    )

Original exception was: Foreign key associated with column 'Friends.profile_id' could not find table 'Profile' with which to generate a foreign key to target column 'id'

Но если даже задавать ассоциативную таблицу после таблицы, на которую ссылается, то потеряет имя ассоциативной таблицы:

When initializing mapper mapped class Profile->Profile, expression 'Friends' failed to locate a name ("name 'Friends' is not defined"). If this is a class name, consider adding this relationship() to the <class 'app.db.Profile'> class after both dependent classes have been defined
0
5 / 5 / 0
Регистрация: 25.09.2017
Сообщений: 569
29.11.2023, 14:46  [ТС]
Избавился от ассоциативной таблицы, реализовал ассоциативный объект:

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Friends(Base):
    __tablename__ = "Friends"
    __table_args__ = (
        UniqueConstraint("profile_id", "friend_id"),
    )
    profile_id = Column(ForeignKey("Profile.id"), primary_key=True)
    friend_id = Column(ForeignKey("Profile.id"), primary_key=True)
    profile = relationship("Profile", back_populates="profile", foreign_keys=[profile_id])
    friend = relationship("Profile", back_populates="friends", foreign_keys=[friend_id])
 
class Profile(Base, UpdateInterface):
 
    __tablename__ = "Profile"
    
    profile = relationship("Friends", uselist=False, back_populates="profile", foreign_keys=[Friends.profile_id])
    friends = relationship("Friends", back_populates="friend", foreign_keys=[Friends.friend_id])
0
5 / 5 / 0
Регистрация: 25.09.2017
Сообщений: 569
01.12.2023, 23:36  [ТС]
Ссылаясь опять же на пример из официальной документации


Python
1
method sqlalchemy.orm.Query.filter_by(**kwargs)
https://docs.sqlalchemy.org/en... ery.filter

Я так не могу запросить объекты?:

Python
1
session.query(model).filter_by(**{pk_name: pk})
Почему в этом sql alchemy устроено всё так, что нужно тысячу раз вспоткнуться на одних и тех же вещах???
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
01.12.2023, 23:36
Помогаю со студенческими работами здесь

Список внешних ключей
Всем привет! Структура: TABLE domains ( id serial, level integer NOT NULL, name character varying(255), ...

Создание внешних ключей
Привет! написал запрос на создание внешних ключей и выводится такая ошибка В общем, мне необходим этот ключ, тк я их делаю исходя из...

Добавление внешних ключей
Добрый вечер. Интересует вот что, возможно ли с помощью интерфейса MySQL Workbench добавить внешние ключи в уже созданные таблицы или...

Создание внешних ключей
Здравствуйте! Пытаюсь разобраться в создании внешних ключей. Все вроде понятно. Есть две таблицы. Пишу запрос. Выдает ошибку. Подскажите,...

Расстановка первичных и внешних ключей
Подскажите, пожалуйста как правильно расставить внешние и первичные ключи таблицы Сделки 1.Наименование торговой сессии ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
5
Ответ Создать тему
Новые блоги и статьи
Автозаполнение реквизита при выборе элемента справочника
Maks 27.03.2026
Программный код из решения ниже на примере нетипового документа "ЗаявкаНаРемонтСпецтехники" разработанного в конфигурации КА2. При выборе "Спецтехники" (Тип Справочник. Спецтехника), заполняется. . .
Сумматор с применением элементов трёх состояний.
Hrethgir 26.03.2026
Тут. https:/ / fips. ru/ EGD/ ab3c85c8-836d-4866-871b-c2f0c5d77fbc Первый документ красиво выглядит, но без схемы. Это конечно не даёт никаких плюсов автору, но тем не менее. . . всё может быть. . .
Автозаполнение реквизитов при создании документа
Maks 26.03.2026
Программный код из решения ниже размещается в модуле объекта документа, в процедуре "ПриСозданииНаСервере". Алгоритм проверки заполнения реализован для исключения перезаписи значения реквизита,. . .
Команды формы и диалоговое окно
Maks 26.03.2026
1. Команда формы "ЗаполнитьЗапчасти". Программный код из решения ниже на примере нетипового документа "ЗаявкаНаРемонтСпецтехники" разработанного в конфигурации КА2. В качестве источника данных. . .
Кому нужен AOT?
DevAlt 26.03.2026
Решил сделать простой ланчер Написал заготовку: dotnet new console --aot -o UrlHandler var items = args. Split(":"); var tag = items; var id = items; var executable = args;. . .
Отправка уведомления на почту при создании или изменении элементов справочника
Maks 24.03.2026
Программная отправка письма электронной почты на примере типового справочника "Склады" в конфигурации БП3. Перед реализацией необходимо выполнить настройку системной учетной записи электронной. . .
модель ЗдравоСохранения 5. Меньше увольнений- больше дохода!
anaschu 24.03.2026
Теперь система здравосохранения уменьшает количество увольнений. 9TO2GP2bpX4 a42b81fb172ffc12ca589c7898261ccb/ https:/ / rutube. ru/ video/ a42b81fb172ffc12ca589c7898261ccb/ Слева синяя линия -. . .
Midnight Chicago Blues
kumehtar 24.03.2026
Такой Midnight Chicago Blues, знаешь?. . Когда вечерние улицы становятся ночными, а ты не можешь уснуть. Ты идёшь в любимый старый бар, и бармен наливает тебе виски. Ты смотришь на пролетающие. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru