relationship.backref 또는 relationship.back_populates로 설정할 수 있다
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String)
addresses = relationship("Address", backref="user")
class Address(Base):
__tablename__ = 'address'
id = Column(Integer, primary_key=True)
email = Column(String)
user_id = Column(Integer, ForeignKey('user.id'))
모델을 위에 처럼 만들면 backref로 인해 User.address, Address.user로 참조할 수 있게된다. backref와 back_populates는 같은 역할을 하고 위 모델을 back_populates를 사용해 작성하면 아래와 같다.
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String)
addresses = relationship("Address", back_populates="user")
class Address(Base):
__tablename__ = 'address'
id = Column(Integer, primary_key=True)
email = Column(String)
user_id = Column(Integer, ForeignKey('user.id'))
user = relationship("User", back_populates="addresses")
부모를 참조하는 외부키를 자식 테이블에 둔다. 이후 relationship(backref)를 명시한다.
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
children = relationship("Child", backref="parent")
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('parent.id'))
공식문서들에는 backref를 부모에 명시하는 걸로 나오지만 자식에 명시해도 상관없는 것 같다.
스칼라 애트리뷰트를 가진 양방향 릴레이션십이다.
스칼라 애트리뷰트로 설정하려면 uselist=False 플래그를 사용해야한다. 아래 모델은 M:1 모델을 1:1로 바꾼 모습이다.
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
child_id = Column(Integer, ForeignKey('child.id'))
child = relationship("Child", backref=backref("parent", uselist=False))
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
두 테이블 사이에 association 테이블을 추가한다. association 테이블은 양쪽의 외부키를 애트리뷰트로 갖는다.
relationship()에 secondary 플래그로 association 테이블임을 명시한다.
association_table = Table('association', Base.metadata,
Column('left_id', Integer, ForeignKey('left.id')),
Column('right_id', Integer, ForeignKey('right.id'))
)
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship(
"Child",
secondary=association_table,
back_populates="parents")
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
parents = relationship(
"Parent",
secondary=association_table,
back_populates="children")
backref를 사용하면 아래와 같다
association_table = Table('association', Base.metadata,
Column('left_id', Integer, ForeignKey('left.id')),
Column('right_id', Integer, ForeignKey('right.id'))
)
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Child",
secondary=association_table,
backref="parents")
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
SQLite는 Alter문을 지원하지 않아, batch 연산을 이용해야한다.
ALTER : 테이블 스키마를 수정할 수 있는 문법. SQLite는 지원하지않아 새로운 테이블을 만들고 기존 데이터를 옮기고 기존 테이블을 지우는 연산을 해야한다. Alembic은 이를 위해 batch 연산을 제공한다.
아래처럼 마이그레트를 동작하게 할 때 render_as_batch=True 플래그를 설정해 마이그레이션시 batch를 사용해 자동생성하도록 할 수 있다.
if app.config['SQLALCHEMY_DATABASE_URI'].startswith("sqlite"):
migrate.init_app(app, db, render_as_batch=True)
init_app()은 플라스크 표준으로 익스텐션을 실행할 때 사용된다.
<form action="{{ url_for('post.upload_post') }}" enctype="multipart/form-data" method="post">
<input type="file" name="file" multiple>
<input type="submit" value="업로드">
</form>
# config.py
UPLOAD_FOLDER = os.path.join(BASE_DIR, 'flatagram/static/img/post/')
ALLOWED_EXTENSIONS = {'jpg', 'jpeg', 'gif', 'png'}
그리고 파일 이름을 받으면 허용된 파일 형식인지 확인해 True/False로 반환하는 함수를 작성한다
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in current_app.config['ALLOWED_EXTENSIONS']
@bp.route('/upload/', methods=('GET', 'POST'))
def upload_post():
if request.method == 'POST':
# check if post request has file part
if 'file' not in request.files:
flash('No file part')
return redirect(url_for('post.upload_post'))
file = request.files['file']
# if user does not select file, browser also
# submit an empty part without filename
if file.filename == '':
flash('No selected file')
return redirect(url_for('post.upload_post'))
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(current_app.config['UPLOAD_FOLDER'], filename))
return redirect(url_for('main.home'))
return render_template('/posts/post_upload.html')
https://flask.palletsprojects.com/en/1.1.x/patterns/fileuploads/
감사합니다. 많은 도움이 되었습니다.