[점프 투 플라스크] sqlite 데이터 불러오고 쓰기 응용 중 에러 발생해요 ㅠ

안녕하세요
덕분에 거의 초보단계임에도 플라스크를 이용한 테스트 서비스를 거의 구현하였는데요,
최종적으로 크롤링으로 얻은 데이터를 sqlite에 있나 확인하고
- 있다면 기존 데이터를 불러오고 없다면 새로 데이터베이스에 저장하는 기능에서 막혔습니다.

sqlite데이터는 잘 생성되었구요..

다음과 같이 작성한 코드에서 어디가 틀렸는지 알려주실 수 있으면 감사하겠습니다.

@bp.route("/search", methods = ["POST", "GET"])  
def search():
    if request.method == "POST":
        keyword = request.form.get("new_keyword")
        # print("post", keyword) 잘 작동하고 있어요. 
    else:
        keyword = request.args.get("new_keyword")  # ok 
        # print("get", keyword) 여기도 잘 작동합니다. 

    name = Restaurants.name

    if keyword == None:  
            return redirect("/")  

    elif db.session.query(name).filter(name == keyword).first() != None:
            results = db.session.query(name).filter(name == keyword).all()
            first_address = results[0]['address']

    else: 
            naver = naver_search_restaurant(keyword)
            mango = mango_search_restaurant(keyword) 
            results = naver + mango 
            first_address = results[0]['address']
            # 여기까진 잘 되는 것 콘솔에 출력해서 확인했는데요. 이 아래 데이터 쓰는 곳에서 문제가 생기는 것 같습니다 ㅠ 
            for result in results: 
                    db.session.add(result)
                    db.session.commit() 
    return render_template("search.html", keyword=keyword, results=results, address=first_address)

[콘솔에 출력된 results, first_address 예시]
[{'source': '네이버', 'date': '2023-04-07 18:15:18', 'keyword': '베트남이랑 ', 'name': '베트남이랑', 'type': '베트남음식', 'score': '4.44', 'review': '1578', 'address': '서울 서초구 서초대로77길 15 대경빌딩 지하 1층', 'telephone': '02-1544-3734', 'link': 'https://map.naver.com/v5/entry/place/1890424195?lng=127.0260866&lat=37.49893589999999&placePath=%2Fhome&entry=plt'}, {'source': '망고플레이트', 'date': '2023-04-07 18:15:22', 'keyword': '베트남이랑 ', 'name': '베트남이랑', 'type': '베트남 음식', 'score': '3.9', 'review': '119', 'address': '서울특별시 마포구 월드컵북로2길 97 2F', 'telephone': '02-334-9777', 'link': 'https://www.mangoplate.com/restaurants/aS0tIsyF7jUR'}] 서울 서초구 서초대로77길 15 대경빌딩 지하 1층

[데이터 에러 메세지]
(...생략)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/sqlalchemy/orm/session.py", line 3321, in add
raise exc.UnmappedInstanceError(instance) from err
sqlalchemy.orm.exc.UnmappedInstanceError: Class 'builtins.dict' is not mapped

도와주세요 ㅠ

올리브 1116

M 2023년 4월 7일 6:20 오후

목록으로
3개의 답변이 있습니다. 1 / 1 Page

안녕하세요.
개인적인 생각 몇자 적습니다 :)

우선 대략 3가지 이슈가 있는데요.
첫번째는 db.session.add(result) 입니다.

SQLAlchemy에서는 ORM 객체를 데이터베이스에
저장하는 방법이 많지만 크게 2가지 있습니다.
.add(orm_object).add_all(list_of_orm_object) 입니다.

현재 결과는 딕셔너리들이 리스트에 들어 있습니다.
즉 2개 이상(복수) 개의 객체를 데이터베이스에 저장하실려고 한다면,
db.session.all(result)가 아니라 db.sesssion.add_all(result)를 써야합니다.

두번째는 db.session.all(), db.session.add_all()에 들어가는 인자입니다.
.add_all()을 사용할 경우 ORM 객체가 리스트형태로 담겨져 있어야합니다.
근데 글쓴이님의 result는 ORM 객체가 리스트형태로 담겨진게 아니라
딕셔너리가 리스트형태로 담겨져 있습니다.

그래서 방법은 리스트안에 딕셔너리 하나하나를 Restaurant의 객체로 만들거나
SQLAlchemy core bulk insert 등의 기능을 사용해야합니다.

전체코드가 없어서 틀릴수도 있지만 가령 이런식으로 해야 합니다.

orm_object_list = []
for shop in result:
    orm_object_list.append(Restaurant(**shop))

db.session.add_all(orm_object_list)

세번째는 여담이지만 .filter(name == keyword).all() 이 맞는 문법인지 모르겟습니다.
SQLAlchemy 버전이 몇인지 모르겠으나 제가 알기론 .filter(key=value) 형식으로 ==
이 아닌 =이 되어야 될거 같은데요...

이상입니다. 다른 고수분들께서 답변 달아주실 겁니다.

결국 핵심은 딕셔너리 리스트로는 ORM을 통해 DB에 삽입이 안됩니다.
딕셔너리 리스트를 ORM 객체가 담긴 리스트로 바꿔서 add_all을 하거나
딕셔너리 리스트를 그대로 사용하면서 session에서 bulk insert로 넣어야 될것 같습니다.

이해가 안되신다면... github repo나 디스코드 등 제가 코드를 직접 manipulate 할수 있는
상황을 제공해주신다면... 더 설명드리겠습니다. (틀린 설명일 수 있습니다. :)
그럼 keep hacking!

로디

2023년 4월 11일 10:41 오전

로디님,
덕분에 힌트를 얻어서 sqlite에 검색결과를 저장하는 것은 다음과 같이 해결했어요.

for result in results:
        result_db = Restaurants(**result)
        db.session.add(result_db)
db.session.commit() 

아직 기존 DB에 있으면 해당 내용을 불러오는 기능은 잘 작동을 안 하지만 좀 더 고민해 보면 될 것 같습니다.

너무너무 감사합니다.

올리브

M 2023년 4월 12일 2:15 오후

🙏🙏🙏

filter 부분은 제가 완전히 틀렸네요.
filterfilter_by를 헷갈렸습니다. 제가 본문에 말한 부분은 filter_by(key=value) 입니다. :)

로디

M 2023년 4월 12일 4:59 오후