0

I'm trying to save an existing instance of a customer record. Its model has a M2M to the vehicle model (since a customer can multiple vehicles). After reading several questions/answer here, I still do not know how to solve this.

Customer model:

class Customer(models.Model):
    vehicle_id = models.ManyToManyField(VehicleSale)
    name = models.CharField(max_length=40, blank=True, db_index=True, null=True,
                                     verbose_name='name')
    lic = models.CharField(max_length=20, blank=True, db_index=True, null=True,
                                verbose_name='license')
    addr = models.CharField(max_length=40, blank=True, null=True, verbose_name='address')
    city = models.CharField(max_length=15, blank=True, null=True, verbose_name='city')
    state = models.CharField(max_length=2, blank=True, null=True, verbose_name='state')
    zip = models.CharField(max_length=10, blank=True, null=True, verbose_name='zipcode')
    email = models.EmailField(blank=True, null=True, verbose_name='email')
    tel1 = models.CharField(max_length=15, blank=True, verbose_name='Tel. 1', null=True)
    tel2 = models.CharField(max_length=15, blank=True, verbose_name='Tel. 2', null=True)
    ssn = models.CharField(max_length=12, blank=True, db_index=True, null=True,verbose_name='SSN')

    class Meta:
        db_table = 'customer'

    def __unicode__(self):
        return self.name

    def save(self, *args, **kwargs):
        self.name = self.name.upper()
        self.addr = self.addr.upper()
        self.city = self.city.upper()
        self.state = self.state.upper()

        return super(Customer, self).save(*args, **kwargs)

In the view, after defining customer as

customer = current_vehicle.customer_set.all()

I tried the following:

if 'customer' in request.POST:
    if customer:
        customer_form = CustomerForm(request.POST, instance=customer[0])
        if customer_form.is_valid():
            customer_form.save()

Also tried adding before customer_form is defined:

customer.vehicle_id = current_vehicle.id

And then this after the form:

customer_form.vehicle_id = current_vehicle.id

Form is not valid so it's not saved. Upon checking {{ form.errors}}, it always reports vehicle_id is required.

Finally, after the answer in this, I adjusted it to my scenario by adding:

obj = customer_form.save(commit=False)

and hoping to assign vehicle_id, but it fails immediately.

What am I missing?

Thanks.

1st EDIT: The section on the view now looks as:

customer_form = CustomerForm(request.POST, instance=customer[0])
customer_form.save()
customer_form.vehicle_id.add(current_vehicle)
Community
  • 1
  • 1
Rmartin
  • 243
  • 3
  • 6
  • 14
  • Can a vehicle be associated to more than one customer? If not then I think your models are incorrect and you should have a customer foreign key on the vehicle model and no M2M field defined at all. Please show both model definitions and show your complete view. – Iain Shelvington Feb 05 '14 at 16:03
  • what version of Django are you on? – Anentropic Feb 05 '14 at 17:57
  • is the last line of your save method indented correctly in the original source code? – Anentropic Feb 05 '14 at 18:02
  • Django 1.6.1. I made a mistake here with the layout. Now it's fixed and return shows correctly as part of the save method. Yes, it was correct in the original source code; only wrong here. – Rmartin Feb 05 '14 at 18:33

1 Answers1

1

You are misunderstanding what a ManyToMany field is here:

customer_form.vehicle_id = current_vehicle.id

vehicle_id is defined as a ManyToMany field on your Customer model, therefore you can't just assign a single id to it. You have to add an instance of VehicleSale model, eg:

customer_form.vehicle_id.add(current_vehicle)

See docs here:
https://docs.djangoproject.com/en/dev/topics/db/examples/many_to_many/

See also this answer for why you can't save until you populate the vehicle_id relation:
https://stackoverflow.com/a/2529875/202168

Community
  • 1
  • 1
Anentropic
  • 32,188
  • 12
  • 99
  • 147
  • Ok, following strictly the django docs link you provided, after doing customer_form = CustomerForm(request.POST, instance=customer[0]), added customer_form.save(), and THEN customer_form.vehicle_id.add(current_vehicle). Unfortunately fails in the customer_form.save() line, with a value error ("Customer could not be changed because the data didn't validate"). However, the request.POST does have the change I made, which it was simply adding a letter at the end of one of the fields. BTW, this still has plenty of space for extra characters. What am I doing wrong? – Rmartin Feb 05 '14 at 17:39
  • please show the full source of your `Customer` model. When you call `save()` on a modelform it calls `full_clean` on the model, so it's something in the model validation that is failing – Anentropic Feb 05 '14 at 17:50
  • FUll customer model was added. Thanks. – Rmartin Feb 05 '14 at 17:58
  • try adding `blank=True` to your `vehicle_id` field – Anentropic Feb 05 '14 at 18:11
  • When adding blank=True and doing customer_form.save(), it removes the record on the intermediary table. After re-creating it, if I have customer_form.save(commit=False) and then add vehicle instance this fails on this line. Any other suggestion? – Rmartin Feb 05 '14 at 18:29
  • ...if it removes the m2m record it sounds like you've submitted no value for `vehicle_id` in the form. if you don't want that field in the html form you should `exclude` it in the modelform definition – Anentropic Feb 05 '14 at 18:44
  • Ok, now it does the customer record, but because after customer_form.save() I have customer_form.vehicle_id.add(current_vehicle), it fails in this last line with: 'CustomerForm' object has no attribute 'vehicle_id'. Actually, now the record in the intermediary table was NOT removed. Is that not needed anymore? – Rmartin Feb 05 '14 at 18:57
  • it should be `customer = customer_form.save()` and then `customer.vehicle_id.add(current_vehicle)` the m2m relation is part of the model, not the form – Anentropic Feb 05 '14 at 19:01
  • You are correct. Just wondering, I notice the record in intermediary table is not removed/re-created nor updated. Is still updating the M2M relation advisable? Thanks a lot for your help. – Rmartin Feb 05 '14 at 19:07
  • if you already added it in previous attempts to get things working you won't see any change now. if you manually delete the intermediate record from the db (via sql) then submit your form do you see it added? – Anentropic Feb 05 '14 at 19:11
  • A customer record was already created when the associated vehicle record was created as well, so I am just fixing the part of updating the customer record. On the other hand, I should not manually delete intermediate record for customer because when I request the vehicle form, which also displays the customer form, it should be blank since there is no vehicle.customer_set. Please correct me if I'm wrong. – Rmartin Feb 05 '14 at 19:15