django를 다루는 사람이라면 최소 한 번은 javascript에서 django 객체를 쓰고 싶은 순간이 있었을 겁니다.
먼저 간단한 예를 들어보겠습니다.
#models.py
class Post(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()
hit = models.IntergerField()
#views.py
def post_list(request):
context = {
"posts": Post.objects.all()
}
return render(request, "post_list.html", context)
위와 같이 django model 과 view 가 구성되어있을 때,
django template을 통해 javascript에서 값을 쓰고 싶다면,
가장 먼저 드는 생각은 아래와 같을 겁니다.
<!-- post_list.html (before render) -->
<script>
let posts = {{ posts }}
</script>
하지만 django template은 html 위에 값을 stringfy 시켜서 그대로 그리기 때문에,
아래와 같이 출력될 것입니다.
<!-- post_list.html (after render) -->
<script>
let posts = <QuerySet [<Post: Post object (1) ... >]>
</script>
이는 javascript 문법과 다르기 때문에 에러가 발생합니다.
이러한 오류를 피하고 django 객체를 javascript에서 사용할 수 있게 해주는 방법은 여러가지가 있지만 저는 아래의 두 방법을 이용합니다.
위의 오류를 피하기 위해서 아래와 같이 값을 모두 풀어주는 방법을 선택할 수 있습니다.
<!-- post_list.html (before render) -->
<script>
let posts = [
{% for post in posts %}
{
title: "{{ post.title }}",
content: "{{ post.content }}",
hit: {{ post.hit }}
},
{% endfor %}
]
</script>
결과적으로 아래처럼 랜더링 되기 때문에 javascript 문법에 부합합니다.
<!-- post_list.html (after render) -->
<script>
let posts = [
{
title: "일",
content: "첫 번째 글입니다.",
hit: 1
},
{
title: "이",
content: "두 번째 글입니다.",
hit: 0
},
{
title: "삼",
content: "세 번째 글입니다.",
hit: 0
},
{
title: "사",
content: "네 번째 글입니다.",
hit: 0
},
{
title: "오",
content: "다섯 번째 글입니다.",
hit: 2
},
]
</script>
위와 같은 방법은 django의 값을 쓸 수 있기는 하지만, 사용할 때마다 값을 풀어줘야 한다는 단점이 있습니다.
두 번째 해결책을 이용하기 위해서 view 와 model을 조금 수정하겠습니다.
#models.py
class Post(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()
hit = models.IntergerField()
def to_json(self):
return {
"title": self.title,
"content": self.content,
"hit": self.hit
}
#views.py
import json
def post_list(request):
posts = Post.objects.all()
context = {
"posts": posts,
"posts_js": json.dumps([post.json() for post in posts])
}
return render(request, "post_list.html", context)
model에서는 to_json()
이라는 메서드를 생성해서, dictionary 형태로 풀어주었습니다.
그리고 view에서 딕셔너리 형태로 만들어놓은 post 리스트를 json.dumps
로 stringfy 해주었습니다.
굳이 to_json()
이라는 임의의 메서드를 생성해서 django query object를 딕셔너리 형태로 바꿔주는 이유는,
딕셔너리는 json.dumps를 통해 json string 포멧으로 변환해줄 수 있기 때문입니다.
이렇게 변환된 값은 javascript에서 아주 간단하게 사용 가능합니다.
<!-- post_list.html -->
<script>
let posts = JSON.parse("{{ posts_js | escapejs }}")
</script>
json strinfy 되어있는 {{ post_js }}
를 javascript 에서 JSON.parse()
를 통해서 풀어주면서 javascript에서 사용할 수 있는 객체로 바꿔주는 것입니다.
이 때 조심해야 할 것은 | escapjs
를 뒤에 꼭 붙여줘야 합니다.
javascript 에서 string으로 입력된 문자열 중에 아래와 같은 문자 포함되어있으면 escape 를 하는 경우가 있기 때문입니다.
# javascript escaping character
{
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
'/': '/',
'`': '`',
'=': '='
}
이 밖에도 drf serializer 등등 여러 방법이 있을 수 있습니다.
위의 두 방법이 비교적 가장 간단하고, 저도 자주 쓰는 방법이기 때문에 소개해드렸습니다.
특히 현재 회사에서 django template에 vuejs를 붙여서 쓰고있기 때문에 두 번째 방법을 유용하게 쓰고있네요.
아무튼 이 글이 헤메고 있던 누군가에게 도움이 되기를 바랍니다.
감사합니다. 덕분에 막혔던 문제 잘 해결했습니다!