유저테이블이랑, 게시글 테이블이 있다고 가정해보자.
게시글 테이블은 id, user_id, content로 이루어져있다고 가정하자.
아무 설정도 하지 않는다면 user를 삭제하려고 한다면 아마 삭제되지 않을 것이다. 이는 user테이블과 연결된 게시글 테이블에 아직 데이터가 남아있기 때문이다. 이럴 때 어떻게 해라 라고 제약조건을 걸 수 있다. CASCADE 제약조건에 대해 알아보자.
on delete, on update에 설정할 수 있는 제약조건이 여럿 있다. 이 중 restrict, set null, set default는 이름만 봐도 대충 알 수 있을 것이다. 하지만 우리는 cascade를 사용할 것이다. 이게 뭘까?
1. RESTRICT : 개체를 변경/삭제할 때 다른 개체가 변경/삭제할 개체를 참조하고 있을 경우 변경/삭제가 취소됩니다.(제한)
2. CASCADE : 개체를 변경/삭제할 때 다른 개체가 변경/삭제할 개체를 참조하고 있을 경우 함께 변경/삭제됩니다.
3. NO ACTION : MYSQL에서는 RESTRICT와 동일합니다.
4. SET NULL : 개체를 변경/삭제할 때 다른 개체가 변경/삭제할 개체를 참조하고 있을 경우 참조하고 있는 값은 NULL로 세팅됩니다.
라고 한다.
즉 cascade는 삭제하면 같이 삭제하고, 업데이트하면 같이 업데이트하게끔 설정해주는 것이다. 사실 이것은 이미 오래전에 알고 있었다. 하지만, sqlalchemy에서 이 설정을 해놔도, 전혀 제대로 삭제되지 않았었다. 이번 기회에 왜 이런것인지 한번 찾아보았다. 며칠 전에 짠 공지부분으로 설명하겠다.
class Announcement(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String(50), nullable=False)
content = db.Column(db.Text, nullable=True)
class AnnouncementUrl(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
announcement_id = db.Column(db.Integer, db.ForeignKey('announcement.id', ondelete='CASCADE'), nullable=False)
announcement = db.relationship('Announcement', backref=db.backref('urls'))
url = db.Column(db.Text, nullable=False)
Announcement : 공지, AnnouncementUrl : 공지에 첨부할 url
원래는 모델을 이렇게 작성했었다. ondelete='CASCADE'로 작성하여 설정을 제대로 해준 줄 알았다. 하지만 데이터를 삭제하려고 하면 아래와 같은 에러가 뜨곤 했다.
AssertionError: Dependency rule tried to blank-out primary key column 'announcement_url.announcement_id' on instance '<AnnouncementUrl at xxx>'
즉, announcement를 삭제하려니까 announcement_url의 announcement_id 열이 자꾸 null(blank-out)된다는 의미이다. 제대로 삭제가 안되는 것이다.
이에 대한 해결책은 간단하다. 모델의 relationship에도 cascade라고 명시해주면 된다.
class Announcement(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title = db.Column(db.String(50), nullable=False)
content = db.Column(db.Text, nullable=True)
class AnnouncementUrl(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
announcement_id = db.Column(db.Integer, db.ForeignKey('announcement.id', ondelete='CASCADE'), nullable=False)
announcement = db.relationship('Announcement', backref=db.backref('urls', cascade='delete'))
url = db.Column(db.Text, nullable=False)
이렇게 해주면 잘 작동이 된다.
'개발' 카테고리의 다른 글
[flask] 로그 출력 (0) | 2023.08.21 |
---|---|
[sql, sqlalchemy] 연결된 두 테이블 cascade 설정하기 - 2 (uselist 설정) (2) | 2023.08.10 |
[sql, sqlalchemy] 특정 조건으로 order_by 정렬하기 - case문 (0) | 2023.08.03 |
git 커밋 기록 초기화 (0) | 2023.08.02 |
datagrip으로 table(혹은 data) export/import하는 법 (1) | 2023.08.01 |