class ProductDetail(DetailView):
template_name = 'product_detail.html'
queryset = Product.objects.all() # 모든 제품에서 하나씩 꺼내서 쓸 것이기에 queryset을 쓴다.
context_object_name = 'product'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['form'] = OrderForm(self.request) # orderform에서 받은 값을 'form'으로 템플릿에 전달한다.
return context # view 함수이기 때문에 request가 있으니 폼을 생성할 때 request를 전달한다.
디테일 페이지에서 주문하기 버튼을 만들고 수량을 폼으로 받을 것이기 때문에 product-views에 작성한다.
get_context_data
메소드는 OrderForm에서 받아온 값을 context['form']에 저장해서 템플릿에 보낸다.
self.request
도 폼에 실어 보낸다.
class RegisterForm(forms.Form):
def __init__(self, request, *args, **kwargs):
super().__init__(*args, **kwargs) # form에 request를 전달 받기 위해 만들었다.
self.request = request
quantity = forms.IntegerField(
error_messages={
'required' : '수량을 입력해주세요.'
}, label='수량'
)
product = forms.IntegerField(
error_messages={
'required' : '상품가격을 입력해주세요.'
}, label='상품가격', widget=forms.HiddenInput # 화면에는 보이지 않는 인풋
)
def clean(self):
cleaned_data = super().clean()
quantity = cleaned_data.get('quantity')
product = cleaned_data.get('product')
fcuser = self.request.session.get('user')
if quantity and product and fcuser:
with transaction.atomic():
prod = Product.objects.get(pk=product)
order = Order(
quantity=quantity,
product=prod, # 주문한 상품의 객체를 주문모델의 상품필드에 저장
fcuser=Fcuser.objects.get(email=fcuser)
)
order.save()
prod.stock -= quantity
prod.save()
else:
self.product = product
self.add_error('quantity', '수량을 입력해주세요')
self.add_error('product', '값을 입력해주세요.')
__init__
메소드를 상속받아서 폼에 request를 전달받는다.
product
필드에 widget=forms.HiddenInput
은 인풋태그를 감춘다.
transaction
데이터베이스의 상태를 변화시키기 위한 작업의 단위를 의미한다. 트랜잭션은 간단히 말해서 작업의 완전성을 보장해주는 것이다. 트랜잭션의 작업 셋을 완벽하게 처리하지 못할 경우 다시 원상태로 복구해서 작업 셋의 일부만 적용되는 것을 막기 위한 기능이다.
<form method="POST" action="/order/create/">
{% csrf_token %}
{% for field in form %}
<div class="form-group">
{% ifnotequal field.name 'product' %}
<label for="{{ field.id_for_label }}">{{field.label}}</label>
{% endifnotequal %}
<input
type="{{ field.field.widget.input_type }}"
class="form-control"
id="{{ field.id_for_label}}"
placeholder="{{ field.label }}"
name="{{field.name}}"
value="{% ifequal field.name 'product' %}{{product.id}}{% endifequal %}"
/>
</div>
{% if field.errors %}
<span style="color: red">{{ field.errors }}</span>
{% endif %} {% endfor %}
<button type="submit" class="btn btn-primary">주문하기</button>
</form>
</li>
<li class="list-group-item">가격: {{ product.price|intcomma }} 원</li>
class OrderCreate(FormView):
form_class = RegisterForm
success_url = '/product/'
def form_invalid(self, form):
return redirect('/product/' + str(form.product)) # forms.py에서 self.product = product product는 html태그에서 value값이 id값이므로 숫자가 넘어간다.
def get_form_kwargs(self, **kwargs): # 폼을 생성할 떄 어떤 인자값을 전달해줄지 알려주는 함수
kw = super().get_form_kwargs(**kwargs)
kw.update({
'request': self.request # 기존에 생성된 인자값에 request를 추가해준다.
})
return kw
class OrderList(ListView):
template_name = 'order.html'
context_object_name = 'order_list'
def get_queryset(self, **kwargs): # queryset을 오버라이딩해서 로그인한 사람의 정보만 표시하게 한다.
queryset = Order.objects.filter(fcuser__email=self.request.session.get('user'))
return queryset
OrderCreate
return redirect('/product/' + str(form.product))
입력값이 안채워지면 상품 상세페이지로 옮겨간다.
POST방식일 때(이전에 디테일페이지에 폼은 GET방식이다.)
폼에서 보낼 때 request가 있는데 view에서 처리를 하는데 상속받은 FormView에 request가 없으므로 get_form_kwargs
메소드로 생성을 해준다.
OrderList
{% extends "base.html" %}
{% load humanize %}
{% block contents %}
<div class="row mt-5">
<div class="col-12">
<table class="table table-light">
<thead class="thead-light">
<tr>
<th scope="col">#</th>
<th scope="col">상품명</th>
<th scope="col">수량</th>
<th scope="col">주문날짜</th>
</tr>
</thead>
<tbody class="text-dark">
{% for order in order_list %}
<tr>
<th scope="row">{{ order.id }}</th>
<th>{{ order.product }}</th>
<th>{{ order.quantity|intcomma }} 개</th>
<th>{{ order.register_date|date:'Y-m-d H:i' }}</th>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}
context_object_name
을 order_list로 설정했기 때문에 for문을 저렇게 작성했다.