모델링에서 짚고넘어가야 할 부분 위주로 알아보자
모델링은 크게 seller, product, event로 나뉜다.
셀러 : 어드민 페이지 서비스를 이용하는 주체
상품 : seller에게 소속된 상품
기획전 : 일종의 promotion으로 서비스 자체적으로 여러가지 promotion을 만들 수 있다. ex) promotion 세일 상품, 쿠폰제공 등
어드민 페이지의 특성상 특정 시점에 대한 이력을 관리할 필요가 있다(사용자가 특정시점의 상태를 요구하는 경우가 종종 발생한다고함) 그래서 중요한 데이터의 변경사항을 관리하는 이력관리 테이블을 만들어서 특정시점의 버전을 row로 가진다(선분이력)
셀러 정보의 경우 3개의 테이블이 연동되어있다.
accounts : 유저의 아이디와 패스워드를 관리하는 테이블이다
seller_accounts : 유저 중에서 셀러의 번호만 관리하는 테이블이다.
seller_infos : 셀러의 이력관리용 테이블로 하나의 셀러가 여러개의 이력을 가질 수 있다.
셀러타입과 상품종류가 연동된다. 셀러타입(쇼핑몰, 마켓, 로드샵, 디자이너브랜드, 제너럴브랜드, 내셔널브랜드, 뷰티), 상품종류(트랜드, 브랜드, 뷰티). 즉, 셀러가 만들어지면 셀러타입이 정해지고 그 타입에 따라 부여할 수 있는 상품의 종류가 정해지게 된다. ex) 셀러타입이 쇼핑몰인 셀러는 트랜드상품만 등록할 수 있다.
트랜드 : 쇼핑몰, 마켓, 로드샵
브랜드 : 디자이너브랜드, 제네럴브랜드, 내셔널브랜드
뷰티 : 뷰티
셀러의 경우 권한 타입별로 접근할 수 있는 페이지가 다르다. 마스터권한을 가진 유저의 경우 전체 셀러의 정보를 열람 하고 셀러의 상태를 관리할 수 있고, 기획전을 등록하고 관리할 수 있다. 일반 셀러의 경우 가입 후 상태가 입점대기에서 입점이 되어야 권한이 필요한 페이지에 접근가능하고 상품등록이 가능하다. 이러한 권한에 대한 것은 accounts 테이블에서 관리한다.
상품 이미지의 경우 하나의 사진을 등록하면 3가지 사이즈(소, 중, 대)가 필요하기 때문에 리사이즈 후 s3에 업로다한 url을 데이터베이스에 저장하기 때문에 정규화시킨다.
- 셀러 리스트 검색
- restful api 형태에 맞게 이미지 업로더 수정
- 셀러 리스트 Excel 다운로드
- 스칼라 서브쿼리를 통한 셀러상품 Count
- 셀러 상태 수정시 SELECT INSERT
이렇게 5개의 기억에 남는 기능과 코드에 대해서 알아보겠다.
[1] select_seller_list_statement = '''
SELECT
seller_account_id,
accounts.login_id,
name_en,
name_kr
FROM seller_infos
right JOIN seller_accounts ON seller_accounts.seller_account_no = seller_infos.seller_account_id
LEFT JOIN accounts ON seller_accounts.account_id = accounts.account_no
LEFT JOIN seller_statuses ON seller_infos.seller_status_id = seller_statuses.status_no
LEFT JOIN seller_types ON seller_infos.seller_type_id = seller_types.seller_type_no
LEFT JOIN manager_infos on manager_infos.seller_info_id = seller_infos.seller_info_no
WHERE seller_infos.close_time = '2037-12-31 23:59:59.0'
AND accounts.is_deleted = 0
AND seller_accounts.is_deleted = 0
[2] AND manager_infos.ranking = 1
'''
# 쿼리파라미터에 키워드가 들어왔는지 확인하고 위에서 정의해준 명령문에 쿼리를 추가해줌.
[3] if valid_param.get('seller_account_no', None):
select_seller_list_statement += " AND seller_accounts.seller_account_no = %(seller_account_no)s"
filter_query_values_count_statement += " AND seller_accounts.seller_account_no = %(seller_account_no)s"
if valid_param.get('login_id', None):
select_seller_list_statement += " AND accounts.login_id = %(login_id)s"
filter_query_values_count_statement += " AND accounts.login_id = %(login_id)s"
# 셀러 한글명 같은 경우는 키워드로 들어온 값을 포함하는 모든 셀러를 검색해야 하기 때문에 like 문을 사용한다.
name_kr = valid_param.get('name_kr', None)
if valid_param.get('name_kr', None):
valid_param['name_kr'] = '%'+name_kr+'%'
select_seller_list_statement += " AND name_kr LIKE %(name_kr)s"
filter_query_values_count_statement += " AND name_kr LIKE %(name_kr)s"
[1] 우선 sql 명령문을 제일 위에서 정의해준다.
[3] 만약 쿼리파라미터로 검색키워드가 들어온다면 sql 명령문에 조건을 추가해준다. 결론적으로 데이터베이스에 sql명령문이 실행될 때는 [2] 뒤에 조건이 추가되어 명령문이 실행된다.
def change_seller_info(*args):
# 이미지 업로드 함수를 호출해서 이미지를 업로드하고 url 을 딕셔너리로 가져옴.
[1] image_upload = ImageUpload()
seller_image = image_upload.upload_images(request)
[2] if (400 in seller_image) or (500 in seller_image):
return seller_image
# validation 확인이 된 data 를 account_info 로 재정의
[3] account_info = {
'auth_type_id': g.account_info['auth_type_id'],
'decorator_account_no': g.account_info['account_no'],
'parameter_account_no': args[0],
[4] 'profile_image_url': seller_image.get('seller_profile_image', None),
'certificate_image_url': seller_image.get('certificate_image', None),
'online_business_image_url': seller_image.get('online_business_image', None),
'background_image_url': seller_image.get('background_image', None)
}
[1] 우선 이미지 업로더 클래스의 인스턴스를 만들어주고 이미지 업로드를 하는 매서드를 실행해서 결과를 변수에 담는다.
[2] 이미지 업로더가 있는 모듈에서 리턴값이 400이나 500이 있으면 그 리턴값을 그대로 클라이언트에게 리턴한다.
[3] 이미지 업로드 실행결과(이미지 url의 dictionary형태)에서 value만 가져온다. 없으면 None을 가져와서 저장한다.
# 쿼리파라미터에 excel 키가 1로 들어오면 엑셀파일을 만듦.
[1] if valid_param['excel'] == 1:
[2] s3 = get_s3_connection()
# 엑셀파일로 만들경우 페이지네이션 적용을 받지않고 검색 적용만 받기 때문에 페이지네이션 부분 쿼리를 제거해준다.
[3] replaced_statement = select_seller_list_statement.replace('ORDER BY seller_account_id DESC LIMIT %(limit)s OFFSET %(offset)s', '')
[4] db_cursor.execute(replaced_statement, valid_param)
seller_info = db_cursor.fetchall()
# pandas 데이터 프레임을 만들기 위한 column 과 value 정리
[5] seller_list_dict = {
'셀러번호': [seller['seller_account_id'] for seller in seller_info],
'관리자계정ID': [seller['login_id'] for seller in seller_info],
'셀러영문명': [seller['name_en'] for seller in seller_info],
'셀러한글명': [seller['name_kr'] for seller in seller_info],
'브랜디회원번호': [seller['brandi_app_user_id'] for seller in seller_info],
'담당자명': [seller['manager_name'] for seller in seller_info],
'담당자전화번호': [seller['manager_contact_number'] for seller in seller_info],
'판매구분': [seller['seller_type_name'] for seller in seller_info],
'상품개수': [seller['product_count'] for seller in seller_info],
'셀러URL': [seller['site_url'] for seller in seller_info],
'셀러등록일': [seller['created_at'] for seller in seller_info],
'승인여부': [seller['seller_status'] for seller in seller_info]
}
# 데이터베이스의 데이터를 기반으로 한 딕셔너리를 판다스 데이터 프레임으로 만들어줌.
[6] df = pd.DataFrame(data=seller_list_dict)
# 첫번제 인덱스의 컬럼명을 지정해주고, 번호가 1부터 시작하도록 한다.
[7] df.index.name = '번호'
df.index += 1
# 파일이름과 파일경로를 정의해줌.
[9] file_name = f'{self.gen_random_name()}.xlsx'
file = f'../{file_name}'
# 파일을 엑셀파일로 변환해서 로컬에 저장
[10] df.to_excel(file, encoding='utf8')
# 로컬에 저장된 파일을 s3에 업로드. 업로드 할 때 실패할 것을 고려하여 try-except 사용.
try:
[11] s3.upload_file(file, "brandi-intern", file_name)
except Exception as e:
print(f'error: {e}')
return jsonify({'message': 'S3_UPLOAD_FAIL'}), 500
# s3에 올라간 파일을 다운받는 url
[12] file_url = f'https://brandi-intern.s3.ap-northeast-2.amazonaws.com/{file_name}'
# s3에 올라간 후에 로컬에 있는 파일 삭제
[13] os.remove(file)
[14] return jsonify({'file_url': file_url}), 200
[1] 쿼리파라미터에 excel=1이 들어오면 셀러정보를 데이터베이스에 가져온 결과를 엑셀파일로 만들어 리턴한다.
[2] 우선 s3에 업로드를 하고 다운로드 url을 리턴해야하기 때문에 s3 커넥션을 열어준다.
[3] 위에서 정의했던(검색키워드가 포함된) sql명령문에서 정렬과 페이지네이션 부분만 제거한 sql명령문을 만들어주고 [4]에서 데이터베스에서 셀러정보들을 가져온다.
[5] 가져온 셀러정보를 엑셀파일로 만들기 위해서 dictionary자료형으로 만들어준다. list comprehension을 사용하여 데이터를 리스트로 만들어준다.
[6] 위에서 만들어준 dictionary를 data변수로 지정하고 판다스 데이터프레임을 만들어서 인스턴스로 만든다.
[7] 인덱스 이름과 인덱스 번호를 지정해주지않으면 인덱스이름은 없고, 번호는 0부터 시작하기 때문에 미리 지정해준다.
[9] 파일이름과 파일경로를 지정해주고 [10]에서 판다스 데이터프레임을 엑셀파일로 만든다.
[11] 로컬에 저장된 엑셀파일을 s3에 업로드해준다.
[12] 업로드된 url을 정의해주고 [13]에서 로컬에 저장된 파일을 삭제한다.
[14] 다운로드 url을 리턴한다.
첫번째로는 django 프래임워크를 벗어나 새롭고 가벼운 프래임워크를 사용하면서 Python과 객체지향 언어에 대한 이해도가 올라갔다고 느꼈다. 그래서 다른 객체지향 언어를 배우고싶다고 느꼇고, Flask와 같은 가벼운 프레임워크를 사용해보면서 다른 가벼운 프레임워크를 사용해보고싶다고 느꼈다(예를들면 Node.js). 그리고 좀더 발전하면 프레임워크 자체를 만들어보는 것도 언어와 웹에 대한 이해를 높이는데 도움이 될 것이라고 생각했다.
두번째로는 현업에서 사용하는 데이터베이스 모델링을 배울 수 있어서 좋았으며 실재 서비스를 하기위해서는 유지보수를 생각하고 시스템에 대한 설계를 한다는 것을 경험으로 깨닫게 되었다.