leaking이란건 정말 아름다운 거군요..
쓰기 싫은 수 였는데 결국 이럴줄 알았지.
그
성별이라 프로젝트 생산성에 논의하는 대화의 값어치가 없던가요?팀장님.. 너 생각하는게 거기서 거기던데 제발 사회물 먹은 짬바 좀 작작 무시해...
기록을 위한 기록 Error Handling 하며 알게된 flask 지식 update
project/
│
├── utils/
│ ├── __init__.py
│ ├── api_error_handlers.py
│ ├── constants.py
│ ├── email_utils.py
│ └── logger.py
│ └── string_utils.py
│ └── db_error_handlers.py
├── migrations/
├── models/
├── api/
├── static/
├── script/
├── db/
├── api/
│ ├──services/
│ ├──routers/
├── utils/
├── extensions.py
├── init.py
├── app.py
└── config.py
Benefits of Application Factory Pattern:
Avoids Circular Imports: By initializing extensions within the factory and importing modules inside functions or routes, you prevent modules from importing each other at the top level.
Flexibility: Allows creating multiple instances of the app with different configurations, useful for testing and different environments.
Better Organization: Encourages modular code organization, making it easier to manage large projects.
fk_db_db키
, constrain key 가 NULL 인 경우 에러가 잦았음 - 어떤 메소드는 인식하고/ 어떤 메소드는 인식못하고 이걸 분간하는게 쉽지 않음.
- sql문 안쓰고 싶어서 쓴 건데 migrate 도중 실패시 결국 sql문 써야한다는 모순이 있음
- alembic 문법 쓰느니 sqlite3에서 drop 쓰는게 훨씬 빠르던데요?
- 데이터 양에 비해 갈수록 너무 과한 설정이라는 생각이..
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
db = SQLAlchemy(app)
migrate = Migrate(app, db)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
# 마이그레이션 초기화 (처음 한 번만)
$ flask db init
# 마이그레이션 스크립트 생성
$ flask db migrate -m "Create users table"
# 마이그레이션 적용
$ flask db upgrade
# before
app.route('/update_user', methods=['POST'])
def update_user():
data = request.json
user_id = data['id']
favorite_numbers = json.dumps(data['favorite_numbers']) # 리스트를 문자열로 변환
conn = sqlite3.connect('database.db')
cursor = conn.cursor()
cursor.execute("UPDATE users SET favorite_numbers=? WHERE id=?", (favorite_numbers, user_id))
conn.commit()
conn.close()
return jsonify({"message": "User updated successfully"})
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
favorite_numbers = db.Column(MutableList.as_mutable(JSON))
# 데이터베이스 초기화 (애플리케이션 시작 시 한 번만 실행)
db.create_all()
@app.route('/user/<int:user_id>', methods=['GET'])
def get_user_with_list(user_id):
user = User.query.get(user_id)
if user:
user_dict = {
"id": user.id,
"name": user.name,
"favorite_numbers": user.favorite_numbers # SQLAlchemy가 자동으로 JSON을 리스트로 변환
}
return jsonify(user_dict)
else:
return jsonify({"error": "User not found"}), 404
@app.route('/user/<int:user_id>', methods=['PUT'])
def update_user(user_id):
user = User.query.get(user_id)
if not user:
return jsonify({"error": "User not found"}), 404
data = request.json
user.name = data.get('name', user.name)
user.favorite_numbers = data.get('favorite_numbers', user.favorite_numbers)
db.session.commit()
return jsonify({"message": "User updated successfully", "user": {
"id": user.id,
"name": user.name,
"favorite_numbers": user.favorite_numbers
}})
@app.route('/user', methods=['POST'])
def create_user():
data = request.json
new_user = User(name=data['name'], favorite_numbers=data['favorite_numbers'])
db.session.add(new_user)
db.session.commit()
return jsonify({
"message": "User created successfully",
"user": {
"id": new_user.id,
"name": new_user.name,
"favorite_numbers": new_user.favorite_numbers
}
}), 201
if __name__ == '__main__':
app.run(debug=True)
# after
@app.route('/user', methods=['POST'])
def create_user():
data = request.json
new_user = User(name=data['name'], favorite_numbers=data['favorite_numbers'])
db.session.add(new_user)
db.session.commit()
return jsonify({
"id": new_user.id,
"name": new_user.name,
"favorite_numbers": new_user.favorite_numbers
}), 201
세팅이 복잡해지면 코드가 간결화되어지는 장점은 있었음
# 모델 클래스 정의
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
def __init__(self, name, email):
self.name = name
self.email = email
# Marshmallow 스키마 정의
class UserSchema(Schema):
id = fields.Int(dump_only=True)
name = fields.Str(required=True)
email = fields.Email(required=True)
@post_load
def make_user(self, data, **kwargs):
return User(**data)
# 스키마 인스턴스 생성
user_schema = UserSchema()
users_schema = UserSchema(many=True)
# RESTful API 엔드포인트
@app.route('/users', methods=['POST'])
def create_user():
json_data = request.get_json()
if not json_data:
return jsonify({"message": "No input data provided"}), 400
# 데이터 검증 및 역직렬화
try:
user = user_schema.load(json_data)
except ValidationError as err:
return jsonify(err.messages), 422
# 데이터베이스에 저장
db.session.add(user)
db.session.commit()
# 생성된 사용자 정보 반환
result = user_schema.dump(user)
return jsonify({"message": "User created successfully", "user": result}), 201
@app.route('/users', methods=['GET'])
def get_users():
users = User.query.all()
result = users_schema.dump(users)
return jsonify(result), 200
니들은 중첩 스키마 동적 데이터 이딴거 쓰지마라... 쓰지말라면 쓰지마...
class AddressSchema(Schema):
street = fields.Str()
city = fields.Str()
class UserSchema(Schema):
name = fields.Str()
address = fields.Nested(AddressSchema)
나의 경우
: API가 다양한 형태의 데이터를 반환해야 할 때 사용나의 경우 2
: 특정 필드의 데이터를 검증이나 변환 없이 그대로 통과시키고 싶을 때 from marshmallow import Schema, fields
class DynamicSchema(Schema):
id = fields.Int()
data = fields.Raw()
class NestedDataSchema(Schema):
id = fields.Int()
name = fields.Str()
complex_data = fields.Raw() # 복잡하고 가변적인 중첩 데이터
내가 생각한 로직이지만 정말 심하게 오버한 고오급 로오직이였음
from marshmallow import Schema, fields
class UserSchema(Schema):
name = fields.Str(required=True)
age = fields.Int(strict=True, validate=lambda n: 18 <= n <= 100)
email = fields.Email()
class UserSchema(Schema):
name = fields.Str(required=True)
password = fields.Str(required=True)
@validates('password')
def validate_password(self, value):
if len(value) < 6:
raise ValidationError("Password must be at least 6 characters long.")
- 데이터가 스키마의 필드로 로드되기 전에 실행됩니다.
- marshmallow 필드 변환전 데이터정제/ 데이터 형식 변환(~key)/Valdiation check
- 주로 입력 데이터를 정제하거나 형식을 변경하는 데 사용됩니다.
- 데이터가 스키마의 필드로 로드된 후에 실행됩니다.
- 주로 최종 객체를 생성하거나 추가적인 비즈니스 로직을 적용하는 데 사용됩니다.
- python obejct 생성/ 비즈니스 로직 추가 / 관련 object처리
@validates('age')
def validate_age(self, value):
if value < 0:
raise ValidationError("Age must be positive.")
@pre_dump
def remove_sensitive_data(self, data, **kwargs):
data.pop('password', None)
return data
@post_dump
def add_envelope(self, data, **kwargs):
return {'user': data}
Client req > pre_load > marshmallow field > post_load> python dict/object > sqlite3 save