www.youtube.com/watch?v=MRWFg30FmZQ&list=PL-51WBLyFTg2vW-_6XBoUpE7vpmoR3ztO&index=12
이 포스팅은 다음과 같은 youtube 영상을 따라하면서 배운 내용과 학습 내용을 담은 것이다:
- Dennis Ivy - Inline Formsets | Django Framework (3.0) Crash Course Tutorials (pt 10)
현재 Django Version 3.1.2 를 쓰고 있다. Django는 파이썬을 쓰는 오픈소스 웹 프레임워크이다. 웹사이트 만들때 주로 쓰며, 간단한 것이 큰 특징이다.
※ Formsets로 Multiple Form 구현하기
현재 우리가 Order을 넣을때, 한번에 하나의 Order 밖에 넣을 수 없다고 뜬다. 하지만 이제 우리는 위에 나와 있는 사진 처럼, 한 Customer당 여러개의 Order를 동시에 넣고 싶다. 이를 위해서는 formset이라는 것을 사용한다.
# views.py
...
from django.forms import inlineformset_factory
...
def createOrder(request, pk_createOrder):
OrderFormSet = inlineformset_factory(Customer, Order, fields=('product', 'status'))
customer = Customer.objects.get(id=pk_createOrder)
formset = OrderFormSet(instance=customer)
if request.method == "POST":
formset = OrderFormSet(request.POST, instance=customer)
if formset.is_valid():
formset.save()
return redirect('/')
context = {'form':formset}
return render(request, 'accounts/order_form.html', context)
다음을 views.py에 추가해준다. 여기서 inlineformset_factory는 앞서 말했듯이, 하나의 form에 여러개의 form을 이용해서 데이터를 전달하려고 할때 사용하는 것이다.
OrderFormSet = inlineformset_factory(Customer, Order, fields=('products', 'status'))에서 Customer이 Parent, Order이 Child, fields는 Child model에서 어떤 objects들을 통과시킬지 보여주는 것이다. Order model에서 보면 'product'와 'status'를 가지고 있으므로 이 둘을 통과시키면 위에 제시한 사진에서 볼 수 있듯이 product 와 status가 보인다.
<!-- order_form.html -->
{% extends 'accounts/main.html' %}
{% load static %}
{% block content %}
<div class="row">
<div class="col-md-6">
<div class="card card-body">
<form action="" method="POST">
{% csrf_token %}
{{ form.management_form }}
{% for field in form %}
{{field}}
<hr>
{% endfor %}
<input type="submit" name="Submit">
</form>
</div>
</div>
</div>
{% endblock%}
order_form.html은 위와 같이 변경시켜준다. 여기서 {{ form.manage.ment_form }}은 필수이다. 이유는 formset을 이용하여 여러가지의 form을 전달시킬 경우, 일반적인 form을 이용하여 데이터를 전달하는 것과는 다른 방식으로 데이터가 전달되는데, formset을 이용하여 전달하기 위해서는 저러한 management_form이 필요하다.
management_form을 넣지 않고 form을 제출하면 위와 같은 에러가 난다.
# urls.py
...
path('create_order/<str:pk_createOrder>/', views.createOrder, name="create_order"),
...
urls.py도 위와 같이 변경시켜주면 끝난다.
※ 이미 존재하는 Order는 안 보이게 하기
첫번째 사진에는 그 특정 Customer이 지금까지 시켰던 Order들이 나열되어 있다 (맨 위의 사진.) 하지만 우리는 새로운 Order를 넣는 것이기 때문에 이미 존재하던 Order들은 필요 없고 새로운 Order만 넣으려고 한다. 이럴 떄는 views.py의 createOrder를 다음과 같이 바꿔주면 된다:
# views.py
...
def createOrder(request, pk_createOrder):
OrderFormSet = inlineformset_factory(Customer, Order, fields=('product', 'status'))
customer = Customer.objects.get(id=pk_createOrder)
formset = OrderFormSet(queryset=Order.objects.none(), instance=customer)
if request.method == "POST":
formset = OrderFormSet(request.POST, instance=customer)
if formset.is_valid():
formset.save()
return redirect('/')
context = {'form':formset}
return render(request, 'accounts/order_form.html', context)
...
formset = OrderFormSet(queryset=Order.objects.none(), instance=customer) 에서 밑줄 친 부분을 새로 추가한거다. 간단하게 말해 OrderFormSet에 queryset을 통과시킬때, 이미 존재하던 Order.objects는 통과시키지 말라고 명령하는 것이라고 보면 된다.
또한, 이전 포스팅에서 보여줬던 것과는 달리, 이렇게 하면 Customer의 이름이 form에 명시되지 않는데, 이유는 이전에는 initial={'customer':customer}의 방식으로 초기값을 통과시켜준 반면에, 이제는 instance=customer로 customer값을 통과시키기 때문에 그럴 필요가 없다 (어차피 그런 방식으로 통과시키면 에러(KeyError)가 난다)