본문 바로가기
Coding/Python

MongoEngine ReferenceField without ObjectId

by Hide­ 2019. 3. 26.
반응형

기존에 MongoEngine을 사용할 때 Embedded와 Reference방식에 대한 비교를 진행하는 글을 포스팅 했었다. (https://hides.tistory.com/1015) 현재 Reference 방식을 사용하여 도큐먼트를 모델링하던 중 ObjectId를 사용하지않고 원하는 필드를 사용하는 방법에 대해 궁금해졌고 그 방법에 대해 포스팅한다. 먼저 아래와 같이 User, Item 두개의 모델을 정의한다.


from mongoengine import *

connect('test')


class User(Document):
user_id = StringField()
user_pw = StringField()


class Item(Document):
user_id = ReferenceField(User, required=True)
description = StringField()


Item 모델의 user_id부분에 User를 걸어줬다. 여기에서 Referenced한 User를 담아주려면 다음과 같은 방법으로 진행해줘야 했다.


user = User.objects(user_id='hide').first()
item = Item.objects(description='item1').first()
print(item.user_id)


위와 같은 형태로 코드를 작성하고 실행시켜보면 User모델이 나오는것을 확인할 수 있다. 이는 Item 모델의 user_id가 ReferenceField이기 때문에 User모델 객체를 넘겨주면 자동으로 해당 객체의 ObjectId를 저장하기 때문이다. 그리고 해당 값을 통해 실제 User 도큐먼트에 일치하는 값을 찾아주는 형태이다. 보통의 경우에는 위와 같이 작업해도 큰 문제가 없지만 나는 현재 MySQL에 저장되어있던 값들을 MongoDB로 마이그레이션하는 과정 중 인데, ObjectId가 따로 존재하지 않기에 문제가 발생했다. 따라서 User 모델의 ObjectId가 아닌 user_id를 넣어주고 싶었다. 따라서 아래와 같은 코드를 작성해봤다.


User(user_id='hide', user_pw='123').save()
user = User.objects(user_id='hide').first()
Item(user_id=user.user_id, description='item1').save()

item = Item.objects(description='item1').first()
print(item.user_id)


처음 코드와 다른 점이 있다면, user객체를 가져와서 그대로 넣어주는것이 아닌 user_id값을 넣어줬다. 하지만 이대로 실행시켜보면 아래와 같은 오류가 발생한다.


mongoengine.errors.DoesNotExist: Trying to dereference unknown document DBRef('user', 'hide')


생각해보니 ReferenceField로 특정 모델을 넘겨주면 해당 모델에서 Primary한 키를 자동으로 찾아서 해당 필드를 통해 검색을 진행하는 형태일텐데 나는 기존 Primary key는 자동으로 생성되는 _id에 걸려있기 때문에 제대로 동작을 하지 않는것이 아닐까라는 생각이 들었다.


class User(Document):
user_id = StringField(primary_key=True)
user_pw = StringField()


class Item(Document):
user_id = ReferenceField(User, required=True)
description = StringField()


if __name__ == '__main__':
User(
user_id='hide',
user_pw='123'
).save()
user = User.objects(user_id='hide').first()
Item(
user_id=user.user_id,
description='item1'
).save()

user = User.objects(user_id='hide').first()
item = Item.objects(description='item1').first()
print(item.user_id.user_pw)


전과 달라진 점이 있다면 User모델의 user_id 부분에 primary_key=True 속성을 걸어준 것이다. 위처럼 작성하면 Item의 user_id는 ReferenceField이므로 저장된 값을 참조 대상 모델의 Primary key값을 통해 자동으로 연결시킨다.