Hello everyone, I’m a third-year student and I’m having a Django Web Development course. I’m being stuck with this issue and really need help from everyone: My form using modelform cannot save data into database.
Here is some of my files:
models.py:
class Order(models.Model):
Order_ID=models.CharField(primary_key=True, auto_created=True,max_length=50)
Order_Date=models.DateTimeField(auto_now_add=True,null=False)
class OrderStatus(models.TextChoices):
DON_HANG_MOI='ĐƠN HÀNG MỚI','Đơn hàng mới'
DA_XAC_NHAN='ĐÃ XÁC NHẬN ĐƠN HÀNG','Đã xác nhận'
DANG_VAN_CHUYEN='ĐANG VẬN CHUYỂN','Đang vận chuyển'
GIAO_THANH_CONG='GIAO THÀNH CÔNG','Giao thành công'
GIAO_KHONG_THANH_CONG='GIAO KHÔNG THÀNH CÔNG','Giao không thành công'
DA_HUY='ĐÃ HỦY','Đã hủy'
HOAN_TRA='HOÀN TRẢ','Hoàn trả'
TRA_HANG_THANH_CONG='TRẢ HÀNG THÀNH CÔNG','Trả hàng thành công'
CANCEL_REASONS = [
('update_address', 'Tôi muốn cập nhật địa chỉ/sđt giao hàng'),
('discount_code', 'Tôi muốn thêm/thay đổi mã giảm giá'),
('change_product', 'Tôi muốn thay đổi sản phẩm (màu sắc, số lượng,...)'),
('payment_issue', 'Thủ tục thanh toán rắc rối'),
('better_deal', 'Tôi tìm thấy chỗ mua khác tốt hơn (Rẻ hơn, giao nhanh hơn, uy tín hơn...)'),
('no_need', 'Tôi không có nhu cầu mua nữa'),
]
RETURN_REASONS = [
('missing_items', 'Nhận thiếu hàng'),
('damaged', 'Đơn hàng bị hư hỏng do vận chuyển'),
('no_longer_needed', 'Không còn nhu cầu sử dụng'),
('not_as_described', 'Sản phẩm khác với mô tả'),
('wrong_item', 'Sản phẩm nhận được không đúng'),
]
Order_Status=models.CharField(choices=OrderStatus.choices,max_length=50)
Total_Price=models.DecimalField(max_digits=15,decimal_places=2)
Discount=models.ForeignKey(Discount,on_delete=models.CASCADE,null=True, blank=True)
Customer=models.ForeignKey(Customer,on_delete=models.CASCADE)
Payment_Method=models.ForeignKey(PaymentMethod,on_delete=models.CASCADE)
Shipment_Method=models.ForeignKey(ShipmentMethod,on_delete=models.CASCADE)
Shipment_Status=models.ForeignKey(ShipmentStatus,on_delete=models.CASCADE,null=True, blank=True)
Payment_Status=models.ForeignKey(PaymentStatus,on_delete=models.CASCADE,null=True)
Delivery_Infor=models.ForeignKey(DeliveryInformation,on_delete=models.CASCADE)
Reason = models.CharField(
max_length=150,
choices=CANCEL_REASONS,
blank=True,
null=True
)
Refund_Date=models.DateField(null=True, blank=True)
Time_Refund_Money=models.DateTimeField(auto_now_add=True,null=True)
Cancel_Date = models.DateField(null=True, blank=True) # Ngày hủy
Return_Reason = models.CharField(
max_length=150,
choices=RETURN_REASONS,
blank=True,
null=True
)
Return_Date = models.DateField(null=True, blank=True) # Ngày hoàn
def calculate_total_price(self):
"""
Tính tổng tiền từ các OrderDetail liên kết với đơn hàng này.
"""
order_details = self.orderdetail.all() # related_name='orderdetail'
total = sum(detail.Subtotal for detail in order_details)
self.Total_Price = total
self.save()
return total
def __str__(self):
return str(self.Order_ID)
forms.py:
from django import forms
from Webtote.models import Order
class CancelOrderForm(forms.ModelForm):
class Meta:
model = Order
fields = ['Reason']
widgets = {
'Reason': forms.Select(
choices=Order.CANCEL_REASONS,
attrs={'class': 'form-control'}
),
}
class ReturnOrderForm(forms.ModelForm):
class Meta:
model = Order
fields = ['Return_Reason']
widgets = {
'Return_Reason': forms.Select(
choices=Order.RETURN_REASONS,
attrs={'class': 'form-control'}
),
}
views.py:
def order_detail(request, order_id):
order = get_object_or_404(Order, Order_ID=order_id)
form_cancel, form_return = None, None
if order.Order_Status == order.OrderStatus.DON_HANG_MOI:
if request.method == "POST" and "cancel-btn" in request.POST:
form_cancel = CancelOrderForm(request.POST, instance=order)
if form_cancel.is_valid():
form_cancel.save()
order.Order_Status = order.OrderStatus.DA_HUY
order.Cancel_Date = date.today() # Gán ngày hủy
order.save() # Lưu lại đối tượng sau khi cập nhật
messages.success(request, f"Order #{order_id} was successfully canceled.")
return redirect("order_detail", order_id=order_id)
else:
messages.error(request, "Failed to cancel the order. Please check your input.")
print(form_cancel.errors) # Kiểm tra lỗi ở đây
else:
form_cancel = CancelOrderForm(instance=order)
elif order.Order_Status == order.OrderStatus.GIAO_THANH_CONG:
if request.method == "POST" and "return-btn" in request.POST:
form_return = ReturnOrderForm(request.POST, instance=order)
if form_return.is_valid():
order = form_return.save(commit=False)
order.Order_Status = order.OrderStatus.HOAN_TRA
order.Return_Date = date.today()
order.save()
messages.success(request, f"Order #{order_id} was successfully returned.")
return redirect("order_detail", order_id=order_id)
else:
messages.error(request, "Failed to return the order. Please check your input.")
else:
form_return = ReturnOrderForm(instance=order)
return render(
request,
"order_detail.html",
{
"method": request.method,
"order": order,
"form_cancel": form_cancel,
"form_return": form_return,
},
)
order_detail.html:
{% extends 'base.html' %}
{% block content %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chi tiết đơn hàng</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
<style>
body {
background-color: #f9f9f9;
}
.order-container {
background: #fff;
border-radius: 10px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.product-item {
display: flex;
align-items: flex-start;
margin-bottom: 20px;
padding-bottom: 15px;
border-bottom: 1px solid #eaeaea;
}
.product-item img {
max-width: 120px;
border-radius: 10px;
margin-right: 20px;
}
.product-info {
flex: 1;
}
.product-info h5 {
font-weight: bold;
}
.product-info .attributes {
color: #666;
font-size: 14px;
margin-bottom: 5px;
}
.product-info .price-info {
font-size: 14px;
color: #444;
}
.product-info .price-info span {
display: block;
}
.total-price {
font-weight: bold;
color: #000;
}
.btn-custom {
background-color: #32827B;
color: white;
border: none;
transition: 0.3s ease-in-out;
}
.btn-custom:hover {
background-color: #fff;
color: #32827B;
border: 1px solid #32827B;
}
/* CSS cho Popup */
.popup-container {
display: none; /* Đảm bảo popup ban đầu ẩn */
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5); /* Màu nền mờ */
justify-content: center;
align-items: center;
}
.popup-content {
background-color: white;
padding: 20px;
border-radius: 5px;
width: 300px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.close-popup {
background-color: red;
color: white;
border: none;
cursor: pointer;
padding: 5px;
margin-top: 10px;
width: 100%;
}
.close-popup:hover {
background-color: darkred;
}
.confirm-popup{
background-color: #FFC107;
color: white;
border: none;
cursor: pointer;
padding: 5px;
margin-top: 10px;
width: 100%;
}
.confirm-popup:hover{
background-color: #f0982d;
}
</style>
</head>
<body>
<h4>Method: {{ method }}</h4>
<div class="container my-4">
<div class="order-container">
<h4>Chi tiết đơn hàng</h4>
<p><strong>Mã đơn hàng:</strong> {{ order.Order_ID }}</p>
<p><strong>Ngày đặt hàng:</strong> {{ order.Order_Date }}</p>
<p><strong>Trạng thái:</strong> {{ order.get_Order_Status_display }}</p>
<hr>
<!-- Hiển thị ngày hủy và lý do hủy -->
{% if order.Cancel_Date %}
<p><strong>Ngày hủy:</strong> {{ order.Cancel_Date }}</p>
<p><strong>Lý do hủy:</strong> {{ order.get_Reason_display }}</p>
{% endif %}
<!-- Hiển thị ngày hoàn và lý do hoàn -->
{% if order.Return_Date %}
<p><strong>Ngày hoàn trả:</strong> {{ order.Return_Date }}</p>
<p><strong>Lý do hoàn trả:</strong> {{ order.get_Return_Reason_display }}</p>
{% endif %}
<hr>
<!-- Danh sách sản phẩm -->
{% for detail in order.orderdetail.all %}
<div class="product-item">
<img src="{{ detail.Product.productimage_set.first.Image_Url }}" alt="{{ detail.Product.Product_Name }}">
<div class="product-info">
<h5>{{ detail.Product.Product_Name }}</h5>
<div class="attributes">Màu sắc: {{ detail.Product.Color }}</div>
<div class="price-info">
<span>Số lượng: {{ detail.Quantity }}</span>
<span>Đơn giá: {{ detail.Product.Unit_Price|floatformat:"0" }}₫</span>
<span class="total-price">Thành tiền: {{ detail.Subtotal|floatformat:"0" }}₫</span>
</div>
</div>
</div>
{% endfor %}
<hr>
<!-- Tổng cộng và nút hành động -->
<div class="d-flex justify-content-between">
<h5>Tổng cộng: <span>{{ order.Total_Price|floatformat:"0" }}₫</span></h5>
<div>
{% if order.Order_Status == 'ĐƠN HÀNG MỚI' %}
<!-- Chỉ hiển thị nút Hủy nếu trạng thái là "Đơn hàng mới" -->
<button class="btn btn-custom" id="cancel-btn">Hủy</button>
{% elif order.Order_Status == 'GIAO THÀNH CÔNG' %}
<!-- Chỉ hiển thị nút Hoàn nếu trạng thái là "Giao thành công" -->
<button class="btn btn-danger" id="return-btn">Hoàn Trả</button>
<button class="btn btn-warning" id="rate-btn">Đánh giá</button>
{% endif %}
</div>
</div>
</div>
</div>
<!-- Popup để chọn lý do hủy -->
<div class="popup-container" id="cancel-popup">
<div class="popup-content">
<h5>Chọn lý do hủy đơn hàng</h5>
<form method="POST" action="">
{% csrf_token %}
{{ form_cancel.as_p }}
<button type="submit" class="confirm-popup">Xác nhận</button>
<button type="button" class="close-popup" onclick="closePopup('cancel-popup')">Hủy</button>
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">{{ message }}</div>
{% endfor %}
{% endif %}
</form>
</div>
</div>
<!-- Popup để chọn lý do hoàn -->
<div class="popup-container" id="return-popup">
<div class="popup-content">
<h5>Chọn lý do hoàn đơn hàng</h5>
<form method="POST">
{% csrf_token %}
{{ form_return.as_p }}
<button type="submit" class="confirm-popup">Xác nhận</button>
<button type="button" class="close-popup" onclick="closePopup('return-popup')">Hủy</button>
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">{{ message }}</div>
{% endfor %}
{% endif %}
</form>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const cancelButton = document.getElementById('cancel-btn');
const returnButton = document.getElementById('return-btn');
const rateButton = document.getElementById('rate-btn');
const cancelPopup = document.getElementById('cancel-popup');
const returnPopup = document.getElementById('return-popup');
if (cancelButton) {
cancelButton.onclick = function() {
cancelPopup.style.display = 'flex';
};
}
if (returnButton) {
returnButton.onclick = function() {
returnPopup.style.display = 'flex';
};
}
function closePopup(popupId) {
document.getElementById(popupId).style.display = 'none';
}
});
</script>
</body>
</html>
{% endblock %}
I really need help with this issue cause I’ve asked ChatGPT and read all topics about this but I still cannot resolve this. Thanks everyone!