Frage

I'm having an issue where I instantiate a ModelForm-based form with a pre-existing instance of the model in the GET portion of my view function. This model already has several fields filled in; the ModelForm is used to collect other fields in the model. The pre-existing fields are excluded in the ModelForm's definition.

The problem is, during POST processing, after successful validation of the ModelForm, I'm calling ModelForm.save(commit=False)...and the model returned (which should be the same one I passed in as 'instance' in the GET handling, remember) has somehow lost all the fields that were previously set. The fields actually set by the form are fine; but it's no longer the original instance of my model.

This is not the behavior I expected; and in fact I've used this partial-model-in-modelform previously, and it works other places. What am I missing here??

Hopefully some code'll make all this clear...

So here's the Model:

class Order(models.Model):

    STATUS = (
       ('Created', -1),
       ('Pending', 0),
       ('Charged', 1),
       ('Credited', 2),
    )

    SHIPPING_STATUS = (
       ('Cancelled', 0),
       ('Ready for pickup', 1),
       ('Shipped', 2),
       ('OTC', 3),
    )

    orderID = models.IntegerField(max_length=15, null=True, blank=True)
    store = models.ForeignKey(Store)
    paymentInstrument = models.ForeignKey(PaymentInstrument, null=True, blank=True)
    shippingMethod = models.ForeignKey(ShippingMethod, null=True, blank=True)

    last_modified = models.DateTimeField(null=True, blank=True)
    date = models.DateTimeField(auto_now_add=True, null=True, blank=True)

    total = models.FloatField(default=0.0, blank=True)
    shippingCharge = models.FloatField(default=0.0, blank=True)
    tax = models.FloatField(default=0.0, blank=True)

    status = models.CharField(max_length=50, choices=STATUS, default = 'Created')
    shippingStatus = models.CharField(max_length=50, choices=SHIPPING_STATUS, default = '1')

    errstr = models.CharField(max_length=100, null=True, blank=True)

    #  billing info
    billingFirstname = models.CharField(max_length = 50, blank = True)
    billingLastname = models.CharField(max_length = 50, blank = True)
    billingStreet_line1 = models.CharField(max_length = 100, blank = True)
    billingStreet_line2 = models.CharField(max_length = 100, blank = True)
    billingZipcode = models.CharField(max_length = 5, blank = True)
    billingCity = models.CharField(max_length = 100, blank = True)
    billingState = models.CharField(max_length = 100, blank = True)
    billingCountry = models.CharField(max_length = 100, blank = True)

    email = models.EmailField(max_length=100, blank = True)
    phone = models.CharField(max_length=20, default='', null=True, blank=True)

    shipToBillingAddress = models.BooleanField(default=False)

    #  shipping info
    shippingFirstname = models.CharField(max_length = 50, blank = True)
    shippingLastname = models.CharField(max_length = 50, blank = True)
    shippingStreet_line1 = models.CharField(max_length = 100, blank = True)
    shippingStreet_line2 = models.CharField(max_length = 100, blank = True)
    shippingZipcode = models.CharField(max_length = 5, blank = True)
    shippingCity = models.CharField(max_length = 100, blank = True)
    shippingState = models.CharField(max_length = 100, blank = True)
    shippingCountry = models.CharField(max_length = 100, blank = True)

Here's the ModelForm definition:

class OrderForm(ModelForm):

   class Meta:
      model = Order
      exclude = ('orderID',
                 'store', 
                 'shippingMethod', 
                 'shippingStatus', 
                 'paymentInstrument',
                 'last_modified',
                 'date',
                 'total',
                 'payportCharge',
                 'errstr',
                 'status', )
      widgets = {
          'billingCountry': Select(choices = COUNTRIES, attrs = {'size': "1"}),
          'shippingCountry': Select(choices = COUNTRIES, attrs = {'size': "1"}),
          'billingState': Select(choices = STATES, attrs = {'size': "1"}),
          'shippingState': Select(choices = STATES, attrs = {'size': "1"}),
                 }

And here is the view function:

def checkout(request):

    theDict = {}

    store = request.session['currentStore']
    cart = request.session.get('cart', False)
    order = request.session['currentOrder'] # some fields already set
    if not cart:   # ...then we don't belong on this page.
        return HttpResponseRedirect('/%s' % store.urlPrefix)

    if request.method == 'GET':

        form = OrderForm(instance=order, prefix='orderForm')

    else:    # request.method == 'POST':
        logging.info("Processing POST data...")

        form = OrderForm(request.POST, prefix='orderForm')

        if form.is_valid():
            ### AT THIS POINT, ORDER FIELDS ARE STILL GOOD (I.E. FILLED IN)
            order = form.save(commit=False)
            ### AFTER THE SAVE, WE'VE LOST PRE-EXISTING FIELDS; ONLY ONES SET ARE
            ### THOSE FILLED IN BY THE FORM.

            chargeDict = store.calculateCharge(order, cart)

            request.session['currentOrder'] = order

            return HttpResponseRedirect('/%s/payment' % store.urlPrefix)

        else:  
            logging.info("Form is NOT valid; errors:")
            logging.info(form._errors)

            messages.error(request, form._errors)

    theDict['form'] = form
    theDict['productMenu'] = buildCategoryList(store)

    t = loader.get_template('checkout.html')
    c = RequestContext(request, theDict)

    return HttpResponse(t.render(c))

Any/all help appreciated...

War es hilfreich?

Lösung

When you instantiate the form during the POST, the instance of the model you're editing is None, because you're not passing it in, and you're not persisting the form instance from the GET. Django won't do anything on its own to persist data between requests for you.

Try:

...

form = OrderForm(request.POST or None, instance=order, prefix='orderForm')

if request.method == 'POST':
    logging.info("Processing POST data...")

    if form.is_valid():
        ...

Now the instance will be populated for GET and POST, but the data from request.POST is optional for the form if it's None. It also saves you from having to instantiate the form in two places depending on request.method

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top