3

I am getting the error:

Cannot set values on a ManyToManyField which specifies an intermediary model. Use ipaswdb.ProviderLocations's Manager instead.

I am getting tripped up by the ipaswdb.ProviderLocations manager portion, I thought in my code in the views.py I was properly addressing the M2M relationship of my model in the form_valid.

I did see this SO answer: django Cannot set values on a ManyToManyField which specifies an intermediary model. Use Manager instead

Which led me to add a self.object.save() but that doesn't seem to of done anything. In the UpdateView the code seems like it works but I goto check and even if I selected two locations which via the print statements I can see is coming back from the form, I only see one in the database...

I do see this error on the CreateView, with or without the added self.object.save() (Thought i was getting it because the commit=False and the object wasn't saved yet). I will add the models involved at the bottom too, their relationship is complex.

class ProviderCreateView(CreateView):
    model = Provider
    form_class = ProviderForm
    template_name = 'ipaswdb/provider/provider_form.html'
    success_url = 'ipaswdb/provider/'

    def form_valid(self, form):
        self.object = form.save(commit=True) #traceback shows this as offending line
        ProviderLocations.objects.filter(provider=self.object).delete()
        self.object.save()

        for group_location in form.cleaned_data['group_locations']:
            location = ProviderLocations()
            location.provider = self.object
            location.group_location = group_location
            location.save()

        return super(ModelFormMixin, self).form_valid(form)

class ProviderUpdateView(UpdateView):
    model = Provider
    form_class = ProviderForm
    template_name = 'ipaswdb/provider/provider_form.html'
    success_url = 'ipaswdb/provider/'


    def form_valid(self, form):
        self.object = form.save(commit=False)
        ProviderLocations.objects.filter(provider=self.object).delete()
        self.object.save()
        for group_location in form.cleaned_data['group_locations']:
            print("here!" + self.object.first_name)
            location = ProviderLocations()
            location.provider = self.object
            location.group_location = group_location
            location.save()

            return super(ModelFormMixin, self).form_valid(form)

Then my models:

class Provider(models.Model):
    first_name = models.CharField(max_length = 50)
    last_name = models.CharField(max_length = 50)
    date_of_birth = models.DateField(auto_now_add=False) 
    group_locations = models.ManyToManyField('GroupLocations', through='ProviderLocations', blank=True, null=True)
    etc...

class ProviderLocations(models.Model):
        #group_location = models.ForeignKey('GroupLocations', on_delete=models.CASCADE)
    provider = models.ForeignKey('Provider', on_delete=models.CASCADE)
    group_location = models.ForeignKey('GroupLocations', on_delete=models.CASCADE)
    created_at=models.DateField(auto_now_add=True)
    updated_at=models.DateField(auto_now=True)
    def __str__(self):
        return self.provider.first_name

class GroupLocations(models.Model):
    address = models.ForeignKey('Address', on_delete= models.SET_NULL, null=True)
    group = models.ForeignKey('Group', on_delete=models.CASCADE)
    doing_business_as = models.CharField(max_length = 255)
    created_at=models.DateField(auto_now_add=True)
    updated_at=models.DateField(auto_now=True)
    def __str__(self):
        return self.doing_business_as


class Group(models.Model):
    group_name = models.CharField(max_length=50) 
    etc...

Okay debug logger turned all the way up shows this sql doing only one INSERT when print statements show the numerous locations it is trying to add:

0.001) SELECT "ipaswdb_grouplocations"."id", "ipaswdb_grouplocations"."address_id", "ipaswdb_grouplocations"."group_id", "ipaswdb_grouplocations"."doing_business_as", "ipaswdb_grouplocations"."created_at", "ipaswdb_grouplocations"."updated_at" FROM "ipaswdb_grouplocations" WHERE "ipaswdb_grouplocations"."id" IN (3, 2, 5, 4); args=(3, 2, 5, 4)
(0.000) BEGIN; args=None
(0.000) DELETE FROM "ipaswdb_providerlocations" WHERE "ipaswdb_providerlocations"."provider_id" = NULL; args=(None,)
(0.000) BEGIN; args=None
(0.001) INSERT INTO "ipaswdb_provider" ("first_name", "last_name", "date_of_birth", "license_number", "license_experation", "dea_number", "dea_experation", "phone", "fax", "ptan", "caqh_number", "effective_date", "provider_npi", "provisional_effective_date", "date_joined", "provider_contact", "credentialing_contact", "notes", "hospital_affiliation", "designation_id", "specialty_id", "created_at", "updated_at") VALUES ('Onemore', 'Test', '2016-08-12', 'kljlk', '2016-08-12', 'kljjkl', '2016-08-12', '', '', '', 'lk;fsd', '2016-08-12', 'jksalfas', '2016-08-12', '2016-08-12', 'kj;jasdf', ';kjsfas', '', '', NULL, NULL, '2016-08-12', '2016-08-12'); args=[u'Onemore', u'Test', u'2016-08-12', u'kljlk', u'2016-08-12', u'kljjkl', u'2016-08-12', u'', u'', u'', u'lk;fsd', u'2016-08-12', u'jksalfas', u'2016-08-12', u'2016-08-12', u'kj;jasdf', u';kjsfas', u'', u'', None, None, u'2016-08-12', u'2016-08-12']
here!IPAABQ     <-- all the locations to add is with the here!
here!ststs
here!2312
here!fsfd315
(0.000) BEGIN; args=None

see one insert

(0.000) INSERT INTO "ipaswdb_providerlocations" ("provider_id", "group_location_id", "created_at", "updated_at") VALUES (22, 5, '2016-08-12', '2016-08-12'); args=[22, 5, u'2016-08-12', u'2016-08-12']
[12/Aug/2016 19:46:26] "POST /ipaswdb/provider/add/ HTTP/1.1" 302 0
(0.001) SELECT COUNT(*) AS "__count" FROM "ipaswdb_provider"; args=()
(0.000) SELECT "ipaswdb_provider"."id", "ipaswdb_provider"."first_name", "ipaswdb_provider"."last_name", "ipaswdb_provider"."date_of_birth", "ipaswdb_provider"."license_number", "ipaswdb_provider"."license_experation", "ipaswdb_provider"."dea_number", "ipaswdb_provider"."dea_experation", "ipaswdb_provider"."phone", "ipaswdb_provider"."fax", "ipaswdb_provider"."ptan", "ipaswdb_provider"."caqh_number", "ipaswdb_provider"."effective_date", "ipaswdb_provider"."provider_npi", "ipaswdb_provider"."provisional_effective_date", "ipaswdb_provider"."date_joined", "ipaswdb_provider"."provider_contact", "ipaswdb_provider"."credentialing_contact", "ipaswdb_provider"."notes", "ipaswdb_provider"."hospital_affiliation", "ipaswdb_provider"."designation_id", "ipaswdb_provider"."specialty_id", "ipaswdb_provider"."created_at", "ipaswdb_provider"."updated_at" FROM "ipaswdb_provider" LIMIT 3; args=()
[12/Aug/2016 19:46:26] "GET /ipaswdb/provider/add/ipaswdb/provider/ HTTP/1.1" 200 4835

Looks like something with the Traceback:

Environment:


Request Method: POST
Request URL: http://localhost:8001/ipaswdb/provider/add/

Django Version: 1.9.5
Python Version: 2.7.11
Installed Applications:
['ipaswdb.apps.IpaswdbConfig',
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']



Traceback:

File "/usr/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  149.                     response = self.process_exception_by_middleware(e, request)

File "/usr/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  147.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/usr/local/lib/python2.7/site-packages/django/views/generic/base.py" in view
  68.             return self.dispatch(request, *args, **kwargs)

File "/usr/local/lib/python2.7/site-packages/django/views/generic/base.py" in dispatch
  88.         return handler(request, *args, **kwargs)

File "/usr/local/lib/python2.7/site-packages/django/views/generic/edit.py" in post
  256.         return super(BaseCreateView, self).post(request, *args, **kwargs)

File "/usr/local/lib/python2.7/site-packages/django/views/generic/edit.py" in post
  222.             return self.form_valid(form)

File "/Users/shane.thomas/programming/py3env/ipa_django/mysite/ipaswdb/views.py" in form_valid
  38.       self.object = form.save(commit=True)

File "/usr/local/lib/python2.7/site-packages/django/forms/models.py" in save
  452.             self._save_m2m()

File "/usr/local/lib/python2.7/site-packages/django/forms/models.py" in _save_m2m
  434.                 f.save_form_data(self.instance, cleaned_data[f.name])

File "/usr/local/lib/python2.7/site-packages/django/db/models/fields/related.py" in save_form_data
  1618.         setattr(instance, self.attname, data)

File "/usr/local/lib/python2.7/site-packages/django/db/models/fields/related_descriptors.py" in __set__
  481.         manager.set(value)

File "/usr/local/lib/python2.7/site-packages/django/db/models/fields/related_descriptors.py" in set
  882.                     (opts.app_label, opts.object_name)

Exception Type: AttributeError at /ipaswdb/provider/add/
Exception Value: Cannot set values on a ManyToManyField which specifies an intermediary model. Use ipaswdb.ProviderLocations's Manager instead.
Community
  • 1
  • 1
Codejoy
  • 3,722
  • 13
  • 59
  • 99
  • There is only one place that django [generates this error](https://github.com/django/django/blob/d30febb4e59b659e0d279c77f61f936c199a05b2/django/db/models/fields/related_descriptors.py#L971)... I suggest you look at your stack trace and see how this is getting invoked in your actual (more complex) code... – Peter Brittain Aug 09 '16 at 23:49
  • Okay I revisited this and added a traceback, I am not sure what it wast elling me but something on saving the forms. Perhaps the views.py ProviderCreateView(CreateView) has an issue on the form.save(commit=True) part, really it looks deeper than that. Given the link you posted I am guessing the error lies somehow in how my models are defined as it looks like the error is coming from the related_descriptiors stuff. – Codejoy Aug 10 '16 at 20:28

1 Answers1

5

Don't commit when saving your save. As documented here if you specify commit=True it will try to write the M2M mapping at the same time. You don't want that to happen.

By specifying a vale of False instead, you can call save_m2m later to save the mapping, or create your own mapping instead. You need to do the latter and the rest of your code is already doing the right thing for that.

Peter Brittain
  • 13,489
  • 3
  • 41
  • 57
  • That did fix the error sure enough, but it still only saves one item from the UI even though I control click two in the widget and click save, and have verified that two items come back to the views.py. Is there more leg I have to do than this ? I wonder if the lines in the create view are not creating two instances but writing over one instance created location = ProviderLocations() location.provider = self.object – Codejoy Aug 11 '16 at 14:41
  • Not sure why... You could try enabling the [SQL backend logger](https://docs.djangoproject.com/en/1.10/topics/logging/#django-db-backends) to see how djago is interpreting your code. You might find you need to [force an insert](https://docs.djangoproject.com/en/1.10/ref/models/instances/#forcing-an-insert-or-update). – Peter Brittain Aug 11 '16 at 22:19
  • Thanks again, updated the question with the latest after pushing the debug system to the screen. I cannot tell if this is a new problem (shouldn't be I havent touched any cod really), or if its part of one that first brought me here. Updated the question to add the ouptut from latest run on windows – Codejoy Aug 12 '16 at 18:48
  • 1
    This is now a completely different question. The problem you now have cannot be reproduced with the supplied code - see http://stackoverflow.com/questions/15367760/unboundlocalerror-local-variable-referenced-before-assignment-when-reading-from. You need an [MCVE](http://stackoverflow.com/help/mcve) in a new question rather than re-writing this one... – Peter Brittain Aug 12 '16 at 19:44
  • 1
    You are right, my mistake, The error was from how I tested I edited the question just to include the relevant sql stack trace here. – Codejoy Aug 12 '16 at 19:47