티스토리 뷰
성경 API, 성경 데이터 이것저것 찾아보다가 SQL 스크립트가 공유되고 있는 것을 발견!
- 개역한글 SQL 공유해주신 블로그 (저작권 이슈 없음!!)
- 개역개정 SQL 공유해주신 사이트 (저작권 이슈 확인 안됨)
확장자는 sql이고
열어보면 TABLE 만들고 레코드 insert 하는 SQL문들이 잔뜩 있다.
✔️ 이 스크립트로 나의 장고 DB에 데이터를 추가하고
✔️ 이 형식에 맞춰서 장고 모델을 만들고 ORM 방식으로 get 요청을 처리하는 것을 해볼 것이다.
[1] SQL 스크립트를 돌려서 DB에 데이터 추가하기
1️⃣ executescript (파이썬 공식 문서) / How to execute an external SQL file using sqlite3 in Python (블로그)
2️⃣ Performing raw SQL queries (장고 공식 문서)
이렇게 두개를 알아야한다.
장고 ORM을 쓸 때, 뒷단에서는 SQL문으로 처리되고 있는 것을 알았지만
직접 SQL문을 쓸 수 있게도 해주다니 좋다 ^__^
bible_api 라는 장고프로젝트 안에 아까 다운받은 sql 파일을 넣어주고
run_sql.py라는 파일을 만들어주고 아래와 같이 코드를 작성해줬다.
from django.db import connection
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'bible_api.settings')
def my_custom_sql(sql_file_name):
with connection.cursor() as cursor:
sql_file = open(sql_file_name)
sql_as_string = sql_file.read()
cursor.executescript(sql_as_string)
my_custom_sql("bible_korHRV.sql")
그리고 이 파일을 돌리면
python run_sql.py
장고 프로젝트 안의 DB에 데이터가 추가된다.
장고 프로젝트안에 있는 db.sqlite3 파일을 'DB Browser for SQLite' 라는 프로그램에 끌어다두면
이런 테이블이 추가되었고 레코드들도 추가된 것을 볼 수 있다.
[2] ORM 방식으로 전환하기
이렇게 추가된 데이터를 ORM 방식으로 쓸 수 있게 해보자 (SQL문은 더 이상 쓰고 싶지않다)
1️⃣ Integrating Django with a legacy database (장고 공식문서)
2️⃣ Model Meta options (장고 공식문서)
이 두 문서를 봐줘야한다.
장고는 기존 데이터베이스를 보고 모델을 자동생성해주는 inspectdb라는 유틸리티를 제공해준다.
이 명령어를 돌리면 콘솔에서 자동생성되는 모델이 출력되고
python manage.py inspectdb
이 명령어를 돌리면 models.py 라는 파일로도 만들어준다 (파일안에는 자동생성된 모델이 담겨있음)
python manage.py inspectdb > models.py
참고로 두 명령어 모두,
장고에 기본적으로 있는 테이블들까지도 모델로 auto-genereate 해준다.
(참고로 모델은 테이블에, 필드는 칼럼에 매핑됩니다.)
아무튼 첫번째 명령어를 돌려서
콘솔에 이렇게 출력된 것을 복사하여
models.py에 붙여넣기 하고 조금 바꿔줬다.
from django.db import models
class BibleKorhrv(models.Model):
book = models.IntegerField()
chapter = models.IntegerField()
verse = models.IntegerField()
content = models.TextField()
fake_id = models.IntegerField(primary_key=True)
class Meta:
managed = True
db_table = 'bible_korHRV'
constraints = [
models.UniqueConstraint(fields=['book', 'chapter', 'verse'], name='unique')
]
# 1. 타입수정
우선 원래 sql문과 타입이 다르게 추정된 것을 맞춰주었다.
이렇게 3개 NUMERIC인데 TEXT로 추정되었기 때문이다.
# 2. UniqueConstraint
UniqueConstraint 을 추가해준 것은
원래 sql문에서 book, chapter, verse 의 조합을 PRIMARY KEY로 쓰고 있어서
일단 모델에도 반영해줬다.
하지만 UniqueConstraint가 primary key를 대체해주지는 않는다. (참고: unique_together does not replace primary key )
대신 비슷한 효과로 UniqueConstraint 로 지정된 필드의 조합과 똑같은 조합이 INSERT 되려고 할 때 Unique 에러를 내준다.
중복되는 primary key가 INSERT 되려고 할 때와 똑같은 에러이다. (참고: https://araikuma.tistory.com/692)
많이 찾아봤지만 장고에는 하나의 필드가 아니라 여러 필드를 조합해서 primary key로 쓸 수 있는 방법은 없는 것 같다.
# 3. fake_id 필드 추가
장고의 모델에는 기본적으로 id 필드가 생긴다. (auto-incrementing primary key)
마이그레이션 파일을 보면 id 필드가 생겨있고
UniqueConstraint가 있음에도 id필드가 primary_key로 되어있다.
(위에서 말한 UniqueConstraint가 primary key 대체가 아닌 것 같다는 근거 중 하나)
만약 모델을 정의할 때 custom primary key를 정의해주면 (그 방법은 해당 필드에 primary_key=True 를 명시하면 됨)
id필드는 자동으로 안생긴다.
id 값을 고유값으로 안써줄 것이기 때문에 id 필드가 자동으로 생기는 것을 원치 않는다.
그래서 fake_id 라는 필드를 추가했다.
DB에는 fake_id 라는 필드가 없으니 추가해줘야한다.
run_sql.py 로 이동하여 아래 코드를 추가해준다.
def make_fake_id_column(table_name):
with connection.cursor() as cursor:
column_name = 'fake_id'
sqlite_script = f'''
ALTER TABLE {table_name} ADD COLUMN {column_name} INT;
UPDATE {table_name} SET {column_name}=0;
'''
cursor.executescript(sqlite_script)
make_fake_id_column("bible_korHRV")
돌려주어서 DB에 필드 & 값이 추가되게 해준다.
python run_sql.py
참고로 아래 스크립트로 fake_id가 아니라
auto increment로 동작하는 id 필드를 추가하고
레코드 순서대로 id 필드에 값을 다르게 넣어주고 싶었으나..
mysql에서는 SET을 단독으로 사용가능한데
sqlite 에서는 안된다고 하여 하지 못했다.. 🥲
my_sql_script = f'''
ALTER TABLE {table_name} DROP {column_name};
ALTER TABLE {table_name} ADD COLUMN {column_name} INT AUTO_INCREMENT;
SET @cnt = 0;
UPDATE {table_name} SET {column_name}=@cnt:=@cnt+1;
'''
# 4. 마이그레이션
마이그레이션 해준다.
python manage.py makemigrations
python manage.py migrate
[3] 잘되는 지 확인
앱에 아주 간단하게 코드를 만들고 (예외처리 모두 생략)
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index')
]
from . import models
from django.http import JsonResponse
def index(request):
book = request.GET['book']
chapter = request.GET['chapter']
verse = request.GET['verse']
content = models.BibleKorhrv.objects.get(book=book, chapter=chapter, verse=verse).content
return JsonResponse({'content': content})
서버를 돌린 후
python manage.py runserver
테스트를 해보고 잘 동작하는 지 확인한다.
[ 결론 ]
장고의 특성 상
SQL 스크립트 중에서도 하나의 필드만 Primary Key로 쓰는 SQL 문을 선택하면 더 편하다.
바로 이런 것...!!
장고 모델에서 idx 필드를 primary_key=True 로 해주면 완전 간단, 깔끔..
하지만 이 파일은 저작권 때문에 쓰기가 좀 그렇다,,,
이번에는 저작권 이슈 없는 저 파일을 쓸 수 밖게 없었긴 한데
다음에도 이런 작업을 해야하면 sql 스크립트를 잘 고를테다 🥸
[ 추가 ]
fake_id 이긴 하지만 primary key가 모두 0인 게 찝찝했던 나는
결국 shell을 돌리고 각 object마다 fake_id를 다른 값으로 넣어줬다.
python manage.py shell
>>> from bible.models import BibleKorhrv
>>> for index, objet in enumerate(BibleKorhrv.objects.all()):
new = BibleKorhrv(book=object.book, chapter=object.chapter, verse=object.verse, content=object.content, fake_id=index+1)
object.delete()
new.save()
원래 delete & create가 아니라 update를 해주려고 했지만, update를 하면 unique 에러가 나서 update로 해주지 못했다.
흠... update가 아니라 새로 insert하는 것처럼 여겨서 에러가 나는 것 같은데, 정확한 원인은 파악하지 못했다.
(처음부터 모델을 만들고 데이터를 쌓은 게 아니라 쌓인 데이터로부터 모델을 만들어서 뭔가 다른 점이 있는 듯 하다..)
>>> from bible.models import BibleKorhrv
>>> for index, objet in enumerate(BibleKorhrv.objects.all()):
object.fake_id = index + 1
object.save()
'🐍 > Django' 카테고리의 다른 글
[Django] 장고 Admin 사이트에 검색 기능 추가하기 (0) | 2021.08.19 |
---|---|
[Heroku] Migrate Django SQLite to PostgreSQL (0) | 2021.08.01 |
[Heroku] PostgreSQL DB Plan 업그레이드 (0) | 2021.05.12 |
[Django] 쿼리문 (retrieve) (0) | 2021.03.21 |
[Django] 쿼리문 (create, update, delete) (0) | 2021.03.19 |
- Total
- Today
- Yesterday
- 장고 Custom Management Command
- Django Firebase Cloud Messaging
- flutter dynamic link
- Flutter Clipboard
- 플러터 얼럿
- ribs
- github actions
- Watch App for iOS App vs Watch App
- cocoapod
- 구글 Geocoding API
- ipad multitasking
- flutter 앱 출시
- Flutter Text Gradient
- SerializerMethodField
- PencilKit
- Python Type Hint
- Dart Factory
- 장고 URL querystring
- METAL
- Django Heroku Scheduler
- Flutter Spacer
- DRF APIException
- flutter deep link
- Flutter 로딩
- Flutter getter setter
- 플러터 싱글톤
- flutter build mode
- drf custom error
- Django FCM
- Sketch 누끼
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |