Django form cannot save data into database

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!

Welcome @Trang2201 !

Side Note: When posting code here, enclose the code between lines of three
backtick - ` characters. This means you’ll have a line of ```, then your code,
then another line of ```. This forces the forum software to keep your code
properly formatted. (I have taken the liberty of correcting your original posts.
Please remember to do this in the future.)

You’ve mentioned what this isn’t doing, but you haven’t described what’s actually happening.

You’ve also got a rather large template here with a lot of different parts, and no description or identification as to what we need to be looking at.

Which form are you submitting? What button are you pressing? Are you sure you’re getting the data you’re expecting?

You’ve got multiple nested conditionals, have you verified that you’re taking the right path through your code? (I suggest either adding some print calls at key places to see what’s happening, or else running this in the debugger.)

Also, are you getting any error messages, either in the browser or in the server console?

Sorry for unclearliness. All those files above are used for contributing to my e-commerce website project. Let me explain clearer:

  1. models.py: I define class Order and literally it is about orders in e-commerce website.
  2. forms.py: Here, I have 2 forms that are CancelOrderForm and ReturnOrderForm, they are used for cancel and return function in order detail management.
  3. views.py: I define logic that is when order status is DON_HANG_MOI (this is in my native language), then Cancel button will be shown and choose- cancel- reason form appear when click on that button, similarly to Return button. And after submitting the form, data will be saved into my database (include cancel reason, cancel_date, and transform order status into DA_HUY(means cancelled). And I’ve been stuck in here, my data cannot be saved into database.

I forgot one thing: I’ve used print calls and also console to check if there is any error but nothing happened

But it’s very helpful for other people if you can reduce your code down to the minimum amount required to demonstrate the problem. Doing this can sometimes also reveal to you where the problem lies, so you can solve it yourself.

For example, what’s the smallest order_detail.html template that will demonstrate the problem?

Hi @Trang2201, I agree with everyone, their tips will help people help you.
The only thing I would do at this first moment is to remove any conditionals you have checking if the form is valid, etc., and try to simulate again. The purpose of this is to make things fail so you can investigate what’s going on.

Try to isolate each functionality, for example, keep just cancel or just return, remove the conditional checking the buttons and if the form is valid, see what happens, etc. With some error, it will be easier to help you as well.

Is the form actually submitting? (Do you see the POST request on the console? What data is being submitted? Is it correct? Is the form passing validation?)

Work through the process step-by-step - make sure that everything is happening that is supposed to happen, and check all the data along the way. (This generally means either adding print statements in the code to show you the important data, or running this in the debugger.)

And again here - you’re describing what you’re doing, but you’re not showing it. This type of information isn’t usually helpful. We can’t debug descriptions. We need the actual information that you’re getting if we’re going to try to help you.