#29346: Add "intermediary" kwarg to ModelForm._save_m2m
---------------------------------------------+------------------------
               Reporter:  Douglas Denhartog  |          Owner:  nobody
                   Type:  New feature        |         Status:  new
              Component:  Forms              |        Version:  2.0
               Severity:  Normal             |       Keywords:
           Triage Stage:  Unreviewed         |      Has patch:  0
    Needs documentation:  0                  |    Needs tests:  0
Patch needs improvement:  0                  |  Easy pickings:  0
                  UI/UX:  0                  |
---------------------------------------------+------------------------
 This is my first ticket, so while I attempted to follow the applicable
 guidelines, I apologize in advance if this ticket is not a model
 guideline-following example.

 ----

 Django's documentation clearly explain Intermediary Model limitations:
 "[u]nlike normal many-to-many fields, you can’t use add(), create(), or
 set() to create relationships"

 Thus, when `ModelForm.save()` is called and an intermediary model field is
 not in `self._meta.exclude`, users will get an error like so:
 {{{
 Cannot set values on a ManyToManyField which specifies an intermediary
 model. Use app.IntermediaryModelName's Manager instead.
 }}}

 This presents complexity when the intermediary field is included in the
 form.

 My proposal is to add a kwarg `intermediary` to `ModelForm._save_m2m`.
 This kwarg behaves exactly like `self._meta.exclude` within
 `ModelForm._save_m2m`.

 I have already created a github branch with my solution, but have not
 created a pull request per contribution guidelines (but will do so if/when
 this ticket is approved). My solution adds three/four (3/4) lines of code,
 denoted in my solution below with EOL comment `# ADDED`. I believe my
 simple solution is more appropriate than a complex introspective analysis
 of every potential ManyToManyField's 'through' attribute.

 The final code of my proposal is (in django.forms.models):
 {{{
 def _save_m2m(self, intermediary=None):  # ADDED
     """
     Save the many-to-many fields and generic relations for this form.
     """
     # could assert/convert intermediary == str, list, tuple  # ADDED
     cleaned_data = self.cleaned_data
     exclude = self._meta.exclude
     fields = self._meta.fields
     opts = self.instance._meta
     # Note that for historical reasons we want to include also
     # private_fields here. (GenericRelation was previously a fake
     # m2m field).
     for f in chain(opts.many_to_many, opts.private_fields):
         if not hasattr(f, 'save_form_data'):
             continue
         if fields and f.name not in fields:
             continue
         if exclude and f.name in exclude:
             continue
         if intermediary and f.name in intermediary:  # ADDED
             continue  # ADDED
         if f.name in cleaned_data:
             f.save_form_data(self.instance, cleaned_data[f.name])
 }}}

 And yes, this means adding the `intermediary` kwarg to `ModelForm.save()`
 so that `intermediary` can be passed to `ModelForm._save_m2m`.

-- 
Ticket URL: <https://code.djangoproject.com/ticket/29346>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-updates+unsubscr...@googlegroups.com.
To post to this group, send email to django-updates@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/052.82d39bf5c6932c26ba5db9cc9766429c%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to