1

I need to know how to clear the unsaved related data which causes the following error:

save() prohibited to prevent data loss due to unsaved related object 'order'.

My code is working as intended, but I failed to complete an order. Ever since this, any attempt to save a new order results in above error. I am aware of the cause being the changes to django.db.models.base.py, but there must be some way to clear this via logs or something... I have tried recreating the database, and also the sqlflush command but neither is working.

VIEWS

# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import weasyprint
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.admin.views.decorators import staff_member_required
from django.core.urlresolvers import reverse
from django.conf import settings
from django.http import HttpResponse
from django.template.loader import render_to_string
from .models import OrderItem, Order
from .forms import OrderCreateForm
from cart.cart import Cart
from .tasks import order_created
# Create your views here.
@staff_member_required
def admin_order_detail(request, order_id):
    order = get_object_or_404(Order, id=order_id)
    return render(request, 'admin/orders/order/detail.html', {'order': order})

@staff_member_required
def admin_order_pdf(request, order_id):
    order = get_object_or_404(Order, id=order_id)
    html = render_to_string('orders/order/pdf.html', {'order': order})
    response = HttpResponse(content_type='application/pdf')
    response['Content-Disposition'] = 'filename="order_{}.pdf"'.format(order.id)
    weasyprint.HTML(string=html).write_pdf(response, stylesheets=[weasyprint.CSS(settings.STATIC_ROOT + 'css/pdf.css')])

    return response


def order_create(request):
    cart = Cart(request)
    if request.method == 'POST':
        form = OrderCreateForm(request.POST)
        if form.is_valid():
            order = form.save(commit=False)
            if cart.coupon:
                order.coupon = cart.coupon
                order.discount = cart.coupon.discount
                order.save()
            for item in cart:
                OrderItem.objects.create(order=order, product=item['product'], price=item['price'], quantity=item['quantity'])
            #emptying the cart
            cart.clear()
            #launch celery async task
            order_created.delay(order.id)
            request.session['order_id'] = order.id #set order.id session
            return redirect(reverse('payment:process'))
    else:
        form = OrderCreateForm()
    return render(request, 'orders/order/create.html', {'cart': cart, 'form':form})

MODELS

class Order(models.Model):
    first_name = models.CharField(_('first_name'),max_length=50)
    last_name = models.CharField(_('last_name'),max_length=50)
    email = models.EmailField(_('email'),)
    address = models.CharField(_('address'),max_length=250)
    postal_code = models.CharField(_('postal_code'),max_length=250)
    city = models.CharField(_('city'),max_length=100)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    paid = models.BooleanField(default=False)
    coupon = models.ForeignKey(Coupon, related_name='orders', null=True, blank=True)
    discount = models.IntegerField(default=0, validators=[MinValueValidator(0), MaxValueValidator(100)])

    class Meta:
        ordering = ('-created',)

    def __unicode__(self):
        return 'Order {}'.format(self.id)

    def get_total_cost(self):
        total_cost  = sum(item.get_cost() for item in self.items.all())
        return total_cost - total_cost *(self.discount)

class OrderItem(models.Model):
    order = models.ForeignKey(Order, related_name='items')
    product = models.ForeignKey(Product, related_name='order_items')
    price = models.DecimalField(max_digits=10, decimal_places=2)
    quantity = models.PositiveIntegerField(default=1)

    def __unicode__(self):
        return '{}'.format(self.id)

    def get_cost(self):
        return self.price * self.quantity

FORMS

from django import forms
from .models import Order


class OrderCreateForm(forms.ModelForm):
    class Meta:
        model = Order
        fields = ['first_name', 'last_name', 'email', 'address', 'postal_code', 'city']
Paddy Popeye
  • 1,634
  • 1
  • 16
  • 29
  • Why do you think this is caused by a model? Seems to me that the error is in one of your `view` methods, could you post code? I think [this](https://stackoverflow.com/questions/33838433/save-prohibited-to-prevent-data-loss-due-to-unsaved-related-object?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa) question would give you some more insight on your error – Snackoverflow Mar 27 '18 at 12:53
  • 1
    i have read through that already.. was kind of why I thought it was because of the models – Paddy Popeye Mar 27 '18 at 13:56

2 Answers2

0

If you check your models, then you can see that the coupon field in the order table is a foreign key, in other words, a related object.

Because you don't save the coupon object, it doesn't have a pk and can't be added to the order. First save the coupon object, and then add it to the order.

So something like:

coupon = cart.coupon # not sure if this in between step is needed
coupon.save()
order.coupon = coupon
order.save()
Snackoverflow
  • 736
  • 9
  • 31
  • ok... 1: the failing action does not have a coupon applied, and as this is within an if statement I do not see why you think the coupon is the issue – Paddy Popeye Mar 27 '18 at 14:26
  • You are saying it fails on adding an order, I can only see one `order.save()` in your entire code, which is where it adds a coupon to it. It is an issue because you don't `save()` the coupon object before adding it to the order, which throws the error you're facing.. @PaddyPopeye – Snackoverflow Mar 27 '18 at 14:29
  • 2: the code worked, as it is, before. The only thing that caused the error, was not completing an order process. I had seen this error before, with an uncompleted order, that time when I back spaced to the order and completed it there was no problem. This time, as the browser had been closed there was no possibility to do the same, and ever since the error appears when a new order is submitted. All orders prior to that, with or without coupons applied, worked perfectly. The cause of the issue is the interrupted and uncompleted order, it seems to me – Paddy Popeye Mar 27 '18 at 14:36
  • the code as per your suggestion ... same error if cart.coupon: coupon = cart.coupon coupon.save() order.discount = coupon.discount order.save( – Paddy Popeye Mar 27 '18 at 14:38
  • Then the only thing I could think of is emptying your browser cache – Snackoverflow Mar 27 '18 at 14:43
  • cookies cleared, cached cleared, db flushed, even changed from sqlite3 to postgres.But nothing helps still the same error. – Paddy Popeye Mar 27 '18 at 14:49
  • Have you tried printing the actual data that is being sent when you get the error? – Snackoverflow Mar 27 '18 at 14:52
0

SOLVED

simply save the order object a little earlier in the code so within the orders.views.py file, within the create_order() method, add an earlier invocation of order.save() ...

THIS

if form.is_valid():
    order = form.save(commit=False)

BECOMES

if form.is_valid():
      order = form.save(commit=False)
      order.save()
    if cart.coupon:
        ............
        ..............

All working fine again!!

@Snackoverflow thks for all your help :-)

Paddy Popeye
  • 1,634
  • 1
  • 16
  • 29