bestsource

SQL 화학: 계단식 삭제

bestsource 2023. 6. 23. 22:18
반응형

SQL 화학: 계단식 삭제

된 것 정상적으로 작동하기 수 입니다. 은 SQLChemy와 함께 왜냐하면 정상적으로 작동하기 위해 간단한 캐스케이드 삭제를 받을 수 없기 때문입니다. 부모 요소가 삭제된 경우, 아이들은 계속합니다.null외제 키

여기에 간결한 테스트 사례를 넣었습니다.

from sqlalchemy import Column, Integer, ForeignKey
from sqlalchemy.orm import relationship

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Parent(Base):
    __tablename__ = "parent"
    id = Column(Integer, primary_key = True)

class Child(Base):
    __tablename__ = "child"
    id = Column(Integer, primary_key = True)
    parentid = Column(Integer, ForeignKey(Parent.id))
    parent = relationship(Parent, cascade = "all,delete", backref = "children")

engine = create_engine("sqlite:///:memory:")
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)

session = Session()

parent = Parent()
parent.children.append(Child())
parent.children.append(Child())
parent.children.append(Child())

session.add(parent)
session.commit()

print "Before delete, children = {0}".format(session.query(Child).count())
print "Before delete, parent = {0}".format(session.query(Parent).count())

session.delete(parent)
session.commit()

print "After delete, children = {0}".format(session.query(Child).count())
print "After delete parent = {0}".format(session.query(Parent).count())

session.close()

출력:

Before delete, children = 3
Before delete, parent = 1
After delete, children = 3
After delete parent = 0

부모와 자식 사이에는 단순한 일대일 관계가 있습니다.이 스크립트는 상위 항목을 만들고 하위 항목 3개를 추가한 다음 커밋합니다.그런 다음 부모는 삭제되지만 자식은 유지됩니다. 왜죠?하위 항목을 캐스케이드 삭제하려면 어떻게 해야 합니까?

가 sqlalchemy를 고려한다는 입니다.Child부모로서, 그것이 당신이 당신의 관계를 정의한 곳이기 때문입니다(물론 당신이 그것을 "자녀"라고 불렀다는 것은 신경쓰지 않습니다).

정경우는에 하면,Parent수업 대신에, 그것은 작동할 것입니다:

children = relationship("Child", cascade="all,delete", backref="parent")

(고참)"Child"문자열로: 선언 스타일을 사용할 때 허용되므로 아직 정의되지 않은 클래스를 참조할 수 있습니다.)

추할수있다니습을 추가하는 것이 좋을 .delete-orphan또한▁(한 ()delete됩니다.delete-orphan또한 상위 항목이 삭제되지 않은 경우에도 상위 항목에서 "삭제"된 하위 항목을 삭제합니다.

편집: 방금 알게 되었습니다: 만약 당신이 정말로 관계를 정의하고 싶다면.Childclass, 당신은 그렇게 할 수 있지만, 당신은 backref에 캐스케이드를 정의해야 할 것입니다. (backref를 명시적으로 생성함으로써) 다음과 같이.

parent = relationship(Parent, backref=backref("children", cascade="all,delete"))

)로 표시됩니다.from sqlalchemy.orm import backref)

꽤 오래된 게시물이지만, 저는 이 문제에 한두 시간을 소비했습니다. 그래서 저는 제 발견을 공유하고 싶었습니다. 특히 나열된 다른 의견들 중 일부가 완전히 옳지 않기 때문입니다.

TL;DR

테이블로 합니다.ondelete='CASCADE':

parent_id = db.Column(db.Integer, db.ForeignKey('parent.id', ondelete='CASCADE'))

그리고 다음 관계 중 하나:

상위 테이블에 있는 내용:

children = db.relationship('Child', backref='parent', passive_deletes=True)

또는 하위 테이블에 있는 이것:

parent = db.relationship('Parent', backref=backref('children', passive_deletes=True))

세부 사항

먼저, 허용된 답변에 불구하고 부모/자녀 관계는 다음을 사용하여 설정되지 않습니다.relationship을 사용하여 설정됩니다.ForeignKey다음을 입력할 수 있습니다.relationship부모 테이블 또는 자식 테이블 중 하나에서 사용할 수 있습니다. 아이들의 표에는 , 은 하만지, 분어테에서블이이합, ▁the합다▁to▁although를 사용해야 .backref키워드 인수에 추가된 함수입니다.

옵션 1(기본 설정)

둘째, SqlAlchemy는 두 가지 종류의 계단식을 지원합니다.첫 번째, 그리고 제가 추천하는 것은 데이터베이스에 내장되어 있으며 일반적으로 외부 키 선언에 대한 제약 조건의 형태를 취합니다.포스트그레에서SQL 다음과 같이 표시됩니다.

CONSTRAINT child_parent_id_fkey FOREIGN KEY (parent_id)
REFERENCES parent_table(id) MATCH SIMPLE
ON DELETE CASCADE

즉, 레코드를 삭제할 때parent_table의 해 당 행 하는 모든 행이child_table데이터베이스에 의해 삭제됩니다.빠르고 안정적이며 아마도 최선의 선택일 것입니다.에서 SqlAlchemy로 합니다.ForeignKey다음과 같이(하위 테이블 정의의 일부):

parent_id = db.Column(db.Integer, db.ForeignKey('parent.id', ondelete='CASCADE'))
parent = db.relationship('Parent', backref=backref('children', passive_deletes=True))

ondelete='CASCADE'는 다을생부는니다를 만드는 입니다.ON DELETE CASCADE테이블 위에.

알았어. 거짓말하지 마.

여기에 중요한 경고가 있습니다.내가 어떻게 가지고 있는지 주목하세요.relationship으로 됩니다.passive_deletes=True만약 당신이 그것을 가지고 있지 않다면, 모든 것이 작동하지 않을 것입니다.이는 기본적으로 상위 레코드 SqlAlchemy를 삭제할 때 매우 이상한 작업을 수행하기 때문입니다.의 외부 키를 하든위행의외키부다를설정음다니로 합니다.NULL 그서행삭경에서 하면,parent_tableid으로 5, 그면기으실행됩다니로본적러5,다▁basically▁execute▁5▁will니를 실행합니다.

UPDATE child_table SET parent_id = NULL WHERE parent_id = 5

왜 이걸 원하시는지 모르겠어요많은 데이터베이스 엔진에서 유효한 외부 키를 설정할 수 있다면 놀랄 것입니다.NULL고아를 만드는 것.안 좋은 생각인 것 같은데, 사용 사례가 있을지도 몰라요.이 을 수행하도록 가 SqlAlchemy를 하여 하위 할 수 .ON DELETE CASCADE당신이 설정한 것.이는 삭제할 하위 행을 알기 위해 이러한 외부 키를 사용하기 때문입니다. "SqlAlchemy"로 NULL데이터베이스에서 삭제할 수 없습니다.passive_deletes=True를 방지합니다.NULL외제 열쇠를 꺼내는 것.

수동 삭제에 대한 자세한 내용은 SqlAlchemy 문서를 참조하십시오.

옵션 2

다른 방법으로는 SqlAlchemy에서 이 작업을 수행할 수 있습니다.이 설정은 다음을 사용하여 설정됩니다.cascaderelationship상위 테이블에 정의된 관계는 다음과 같습니다.

children = relationship('Child', cascade='all,delete', backref='parent')

관계가 하위에 있는 경우 다음과 같이 수행합니다.

parent = relationship('Parent', backref=backref('children', cascade='all,delete'))

다시 말하지만, 이 아이는 아이이기 때문에 당신은 다음과 같은 방법으로 전화해야 합니다.backref캐스케이드 데이터를 거기에 넣는 것입니다.

이렇게 하면 상위 행을 삭제할 때 SqlAlchemy는 하위 행을 정리할 수 있도록 실제로 delete 문을 실행합니다.이 방법은 이 데이터베이스가 처리하도록 하는 것만큼 효율적이지 않을 수 있으므로 권장하지 않습니다.

다음은 지원하는 계단식 기능에 대한 SqlAlchemy 문서입니다.

은 @steven의 @steven을 통해 삭제할 때.session.delete()내 경우엔 그런 일이 절대 일어나지 않습니다.나는 대부분의 경우를 통해 삭제한다는 것을 알아차렸습니다.session.query().filter().delete()(메모리에 요소를 넣지 않고 DB에서 직접 삭제합니다.) 방법을 이법용사 sqlalchemy'scascade='all, delete'작동하지 않습니다.하지만 다음과 같은 해결책이 있습니다.ON DELETE CASCADE모든 데이터베이스가 지원하는 것은 아닙니다.

class Child(Base):
    __tablename__ = "children"

    id = Column(Integer, primary_key=True)
    parent_id = Column(Integer, ForeignKey("parents.id", ondelete='CASCADE'))

class Parent(Base):
    __tablename__ = "parents"

    id = Column(Integer, primary_key=True)
    child = relationship(Child, backref="parent", passive_deletes=True)

Alex Okrushko의 대답은 거의 나에게 가장 효과적이었습니다.delete='에 사용됩니다.CASCADE' 및 passive_captive=진정한 결합.하지만 sqlite에 적용하기 위해 추가적인 작업을 해야 했습니다.

Base = declarative_base()
ROOM_TABLE = "roomdata"
FURNITURE_TABLE = "furnituredata"

class DBFurniture(Base):
    __tablename__ = FURNITURE_TABLE
    id = Column(Integer, primary_key=True)
    room_id = Column(Integer, ForeignKey('roomdata.id', ondelete='CASCADE'))


class DBRoom(Base):
    __tablename__ = ROOM_TABLE
    id = Column(Integer, primary_key=True)
    furniture = relationship("DBFurniture", backref="room", passive_deletes=True)

sqlite에서 작동하도록 이 코드를 추가해야 합니다.

from sqlalchemy import event
from sqlalchemy.engine import Engine
from sqlite3 import Connection as SQLite3Connection

@event.listens_for(Engine, "connect")
def _set_sqlite_pragma(dbapi_connection, connection_record):
    if isinstance(dbapi_connection, SQLite3Connection):
        cursor = dbapi_connection.cursor()
        cursor.execute("PRAGMA foreign_keys=ON;")
        cursor.close()

여기서 도난: SQL 화학 표현 언어SQLite가 삭제 캐스케이드에 있습니다.

테스트 시나리오에서와 같이 하위에 적용되는 것과 반대로 backref를 명시적으로 만들어야 한다는 점에서 Steven의 말이 맞습니다. 이렇게 하면 캐스케이드가 상위에 적용됩니다.

그러나 자식에 대한 관계를 정의해도 sqlalchemy가 자식을 부모로 간주하지는 않습니다.관계가 정의된 위치(자녀 또는 부모)는 중요하지 않으며, 부모와 자녀를 결정하는 두 테이블을 연결하는 외부 키입니다.

하지만 하나의 관습을 고수하는 것은 이치에 맞고, 스티븐의 반응을 바탕으로, 저는 부모님에 대한 제 모든 자녀 관계를 정의하고 있습니다.

스티븐의 대답은 확실합니다.저는 추가적인 의미를 지적하고 싶습니다.

을 사용하여relationship애플리케이션 계층(Flask)이 참조 무결성을 책임지도록 하는 것입니다.즉, 데이터베이스 유틸리티나 데이터베이스에 직접 연결하는 사용자와 같이 Flask를 통해 데이터베이스에 액세스하지 않는 다른 프로세스에서는 이러한 제약 조건이 발생하지 않으며 설계하기 위해 노력한 논리적 데이터 모델을 깨는 방식으로 데이터를 변경할 수 있습니다.

가능하면든지 능할때다사다니용합음을을 하세요.ForeignKeyAlex.d512에 의해 됩니다.DB 엔진은 제약 조건을 실제로 적용하는 데 매우 능숙하므로(불가피한 방식으로), 이는 데이터 무결성을 유지하기 위한 최고의 전략입니다.외부 키를 지원하지 않는 SQLite 버전과 같이 데이터베이스가 데이터 무결성을 처리할 수 없는 경우에만 애플리케이션에 의존해야 합니다.

관계 앱 을 가능하게 해야 할 , "-" " " " " " " " " 를 사용합니다.backref ForeignKey.

저는 문서 작성에도 어려움을 겪었지만, 문서 문자열 자체가 매뉴얼보다 더 쉬운 경향이 있다는 것을 발견했습니다.예를 들어 sqlalchemy.orm에서 관계를 가져오고 도움말(관계)을 수행하면 캐스케이드에 대해 지정할 수 있는 모든 옵션이 제공됩니다.을 위한 총알delete-orphan다음과 같이 말합니다.

상위 항목이 없는 자식 유형의 항목이 탐지되면 해당 항목을 삭제하도록 표시합니다.
이 옵션을 선택하면 부모 없이 자식 클래스의 보류 중인 항목이 지속되지 않습니다.

저는 당신의 문제가 부모-자녀 관계를 정의하는 문서의 방식에 더 있었다는 것을 알고 있습니다.하지만 캐스케이드 옵션에도 문제가 있는 것처럼 보였는데, 왜냐하면"all"를 포함합니다."delete"."delete-orphan" 않유옵다션니에 포함되지 입니다."all".

이 질문은 매우 오래된 질문임에도 불구하고 구글에서 검색하면 가장 먼저 뜨므로 다른 사람들이 말한 것과 합칠 수 있는 해결책을 게시할 것입니다(나는 여기에 있는 모든 답을 읽고도 몇 시간을 보냈습니다).

설명한 것처럼, Keys에 입니다. d512 설명것처럼다이한, 것외관것입니한키에국인두.모든 데이터베이스/엔진이 Foreign Key를 지원하는 것은 아닙니다.MySQL 데이터베이스를 실행하고 있습니다.오랫동안 조사한 결과 새 테이블을 만들 때 기본적으로 외부 키를 지원하지 않는 엔진(MyISAM)으로 설정됩니다.제가 해야 할 일은 그것을 추가하여 InnoDB로 설정하는 것이었습니다.mysql_engine='InnoDB'테이블을 정의할 때 사용합니다.프로젝트에서 저는 명령 매핑을 사용하고 있으며 다음과 같이 보입니다.

db.Table('child',
    Column('id', Integer, primary_key=True),
    # other columns
    Column('parent_id',
           ForeignKey('parent.id', ondelete="CASCADE")),
    mysql_engine='InnoDB')

Stevan의 대답은 완벽합니다.하지만 여전히 오류가 발생하고 있다면,그 위에 다른 가능한 시도는 다음과 같습니다.

http://vincentaudebert.github.io/python/sql/2015/10/09/cascade-delete-sqlalchemy/

링크에서 복사됨

모델에 캐스케이드 삭제를 지정한 경우에도 외부 키 종속성 문제가 발생할 경우 빠른 팁을 제공합니다.

하려면 SQL이 있어야 .cascade='all, delete'부모님 테이블에 있습니다.좋아요, 하지만 다음과 같은 것을 실행할 때:

session.query(models.yourmodule.YourParentTable).filter(conditions).delete()

실제로 하위 테이블에 사용된 외래 키에 대한 오류를 트리거합니다.

개체를 쿼리한 다음 삭제하는 데 사용한 솔루션:

session = models.DBSession()
your_db_object = session.query(models.yourmodule.YourParentTable).filter(conditions).first()
if your_db_object is not None:
    session.delete(your_db_object)

이렇게 하면 부모 레코드와 관련된 모든 자식 레코드가 삭제됩니다.

TLDR: 위의 해결책이 작동하지 않으면, 열에 nullable=False를 추가해 보십시오.

여기서 캐스케이드 기능을 사용하지 못할 수도 있는 일부 사람들을 위해 (훌륭한) 기존 솔루션으로 작업할 수 있는 작은 점을 추가하고 싶습니다.제 작업과 예제의 주요 차이점은 오토맵을 사용했다는 것입니다.이것이 캐스케이드 설정에 어떤 영향을 미칠지는 정확히 알 수 없지만, 제가 사용했다는 점에 주목하고 싶습니다.저도 SQLite 데이터베이스를 사용하고 있습니다.

여기에 설명된 모든 솔루션을 시도했지만 상위 행이 삭제될 때 하위 테이블의 행은 외부 키를 null로 계속 설정했습니다.저는 여기 있는 모든 해결책을 시도했지만 소용이 없었습니다.그러나 외부 키로 자식 열을 nullable = False로 설정하면 캐스케이드가 작동합니다.

하위 테이블에 다음을 추가했습니다.

Column('parent_id', Integer(), ForeignKey('parent.id', ondelete="CASCADE"), nullable=False)
Child.parent = relationship("parent", backref=backref("children", passive_deletes=True)

이 설정에서는 캐스케이드가 예상대로 작동했습니다.

언급URL : https://stackoverflow.com/questions/5033547/sqlalchemy-cascade-delete

반응형