ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Django] 장고 SQLite DB 사용관련 팁
    django study 2020. 12. 19. 17:21

     

    장고(Django)에서 SQLite DB 사용에 대한 내용이다.

     

    어려워서 짱박아둔책, 좋은책이라고는 한다.

     

    Two Scopes of Django책의 78페이지에는 다음과 같이 말하고있다.

    7.5 필수불가결한 상황이 아니라면 로우SQL은 지양하자.

    사실 우리가 쓰는 쿼리의 대부분은 단순한 것들이다. ORM이라는 관계형 매핑은 매우 높은 생산성을 제공하는데 우리가 처리하는 다양한 환경에서의 단순한 쿼리 작성뿐만이 아니라 모델에 대한 접근과 업데이트를 할때 유효성 검사와 보안을 제공하기 때문이다. 따라서 이용하려는 쿼리를 ORM으로 표현할 수 있다면 반드시 ORM을 이용하기 바란다.

     또한 개발하는 장고앱이 서드파티패키지로 릴리스된다고 할때, 로우 SQL을 이용하는 바람에 앱의 이식성이 떨어지는 경우가 생길 수 있다는 것을 명심하자.

     마지막으로 흔한 경우는 아니지만 다른 환경의 데이터베이스로 데이터를 마이그레이션해야 하는 경우 특정 데이터베이스에 종속된 기능을 SQL 쿼리를 가지고 작성했다면 이는 데이터베이스 마이그레이션 과정에서 매우 복잡한 문제로 대두될 것이다.

     그렇다면 어떤 경우에 로우 SQL을 써야할까? 로우 SQL을 직접이용함으로써 파이썬 코드나 ORM을 통해 생성된 코드가 월등히 간결해지고 단축되는 경우에만 이용하자. 예를 들어 큰 데이터세트에 적용되는 다수의 쿼리세트가 연동되는 경우라면 로우 SQL을 이용함으로써 더욱 효과적으로 처리하는 방법을 찾을 수 있을것이다.


    팁1, ORM을 사용하지 않고 query문으로 DB를 사용하는 예시

    <참고>

    https://www.py4e.com/html3/15-database

    https://wikidocs.net/12454

    https://docs.djangoproject.com/en/2.2/topics/db/sql/


    다음과 같이 일반적으로 python에서 사용하듯이 이용 가능하다.

     

    1. Create connection
    2. Create cursor
    3. Create Query string
    4. Execute the query
    5. Commit to the query
    6. Close the cursor
    7. Close the connection

     

    장고 DB의 path와 DB내 생성된 table을 직접 지정해야 한다.

    아래 예시는 tablename이라는 테이블에 단일 레코드(col1과 col2의 값)을 넣는 구문이다. 

     

    import sqlite3
    
    conn= sqlite3.connect('./db.sqlite3')
    cur = conn.cursor()
    query= "INSERT INTO tablename (col1, col2) VALUES (?, ?)"
    cursor.execute(query, (value1, value2))
    conn.commit()
    cur.close()
    conn.close()

     

    아래 예시는 excutemany 함수를 사용하여 dataframe 값을 한꺼번에 넣는 예제이다.

    다수의 레코드를 for문 실행없이 한번에 넣는다.

     

    with sqlite3.connect('./db.sqlite3') as conn:
        cur = conn.cursor()
        query = "insert into tablename(col1, col2) values(?, ?, ?)"
        cur.executemany(query, df.values.tolist())
        conn.commit()

     

    django 제공 함수를 사용하여 이런식으로도 접근가능하다.

    django의 default DB에 접근하여 student라는 table에  특정이름의 몸무게값을 업데이트

     

    from django.db import connections
    
    conn = connections['default']
    sql_query = "UPDATE student SET weight = 80 WHERE name = %s " % (name)
    with conn.cursor() as cur:
        cur.execute(sql_query)

     

     

    팁2, DB에 값저장시 중복된 값을 처리하는 방법 (단일 field)


    date(날짜)가 중복되지 않아야하면 model class의 field함수내 unique=True로 지정한다.

     

    class KospiPredict(models.Model):
        date = models.DateField("날짜", max_length=10, null=False, unique=True)
        close = models.FloatField("종가", null=True)
        open = models.FloatField("시가", null=True)

     

    그리고 DB값 저장시 중복이 발생했을때(unique error) 다음과 같이 try-except구문을 사용하여 처리한다.

    IntegrityError를 import 해야하며 except에서 상황에 따라 replace 하거나 ignore할수있다.

     

    from django.db import IntegrityError
        for i in range(df_result_all.shape[0]):
            try:
                KospiPredict(
                    date = df_result_all.iloc[i,0],
                    close = df_result_all.iloc[i,1],
                    open = df_result_all.iloc[i,2]).save()
            except IntegrityError: 
                obj = KospiPredict.objects.get(date = df_result_all.iloc[i,0])
                if obj.date == now_time: # 오늘날짜 replace
                    obj.close = df_result_all.iloc[i,1]
                    obj.open = df_result_all.iloc[i,2]
                    obj.save()
                else: # 그 외 중복 ignore
                    continue

     

     

    팁3, DB에 값저장시 중복된 값을 처리하는 방법 (여러개 field)

    <참고> https://www.py4e.com/html3/15-database


    date(날짜)와 code(코드)의 쌍이 중복되지 않아야하면 model class내 unique_together 을 아래와 같이 지정한다.

    class Kospi(models.Model):
        date = models.DateField("날짜", max_length=10, null=False)
        code = models.CharField("코드", max_length=10, null=False)
        close = models.FloatField("종가", null=True)
        class Meta:
           unique_together = (('date', 'code'),)  # 쌍으로 unique 설정

    상기 팁2의 예제와 다르게 ORM을 사용하지 않고 raw query를 이용해보자.

    query문에서 상황에 따라 insert or replace into ~ 또는 insert or ignore into ~ 을 사용한다.

    팁2의 예제에서 ORM방식으로 for문을 사용하는것보다 executemany를 사용하는것이 처리속도가 빠르다.

    with sqlite3.connect('./db.sqlite3') as conn:
        cur = conn.cursor()
        query = "insert or replace into myapp_kospi(date, code, close) values(?, ?, ?)"
        cur.executemany(query, df.values.tolist())
        conn.commit()

     

     

    팁4 , DB를 초기화하는 방법

    <참고>

    https://stackoverflow.com/questions/14589634/how-to-reset-the-sequence-for-ids-on-postgresql-tables/14589682#14589682

    https://stackoverflow.com/questions/43663588/executing-djangos-sqlsequencereset-code-from-within-python

    https://docs.djangoproject.com/en/2.2/topics/db/sql/


    DB의 특정테이블을 초기화, 즉 레코드를 모두 지우고 싶다면?

    레코드를 지워도 id는 0으로 초기화되지 않기 때문에 아래와 같이 직접수정해야한다.

    sequence_reset_sql 라는 함수도 있지만 동작하지 않음.

     

    from nameapp.models import StockTable
    from django.db import connections
    
        # 기존 테이블의 내용을 삭제
        # StockTable record delete all
        StockTable.objects.all().delete()
    
        # 해당 테이블의 sql_sequence를 reset
        # StockTable sql_sequence reset
        conn = connections['default']
        sql_query = "UPDATE sqlite_sequence SET seq = '0' WHERE name = 'nameapp_stocktable';"
        with conn.cursor() as cur:
            cur.execute(sql_query)

     

     

    팁5. 장고에서 크롤링 사용하여 데이터 수집

    <참고> https://beomi.github.io/2017/03/01/HowToMakeWebCrawler-Save-with-Django/


    크롤링으로 수집한 데이터를 장고DB내에 저장할때 세팅하는 방법 참고

    외부파일에서 장고관련 함수를 사용하고 싶을때, 예를들어 폴더 최상위에 있는 backup_db_copy.py를 단독실행하여 DB에 ORM으로 접근하여 뭔가 일을 처리하고 싶을때 코드 최상단에 아래와 같이 세팅을 해두면 장고에서 제공하는 함수들을 사용할 수 있다.

     

    import os
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test_server.settings")
    import django
    django.setup()

     

    마침.

     

    댓글

Designed by Tistory.