#20835: Allow standard m2m behaviour on through-relationships if extra-fields 
are
nullable or have default values
----------------------------------------------+------------------------
     Reporter:  anonymous                     |      Owner:  nobody
         Type:  New feature                   |     Status:  new
    Component:  Database layer (models, ORM)  |    Version:  1.6-beta-1
     Severity:  Normal                        |   Keywords:
 Triage Stage:  Unreviewed                    |  Has patch:  0
Easy pickings:  0                             |      UI/UX:  0
----------------------------------------------+------------------------
 I'm going to illustrate this feature using example from the documentation
 with minor changes:
 https://docs.djangoproject.com/en/dev/topics/db/models/#extra-fields-on-
 many-to-many-relationships

 {{{
 class Person(models.Model):
     name = models.CharField(max_length=128)

 class Group(models.Model):
     name = models.CharField(max_length=128)
     members = models.ManyToManyField(Person, through='Membership')

 class Membership(models.Model):
     person = models.ForeignKey(Person)
     group = models.ForeignKey(Group)
     date_joined = models.DateField(null=True) # added null=True
     invite_reason = models.CharField(max_length=64, default='unknown') #
 added default='unknown'
 }}}

 {{{
 # THIS WILL NOT WORK
 >>> beatles.members.add(john)
 # NEITHER WILL THIS
 >>> beatles.members.create(name="George Harrison")
 # AND NEITHER WILL THIS
 >>> beatles.members = [john, paul, ringo, george]
 }}}

 This is due to the fact that extra fields must be populated. It's okay.
 But when extra-fields are nullable or have default values it's makes sense
 to allow adding entries via .add() method. Extra fields can be populated
 on the fly, using default values or nulls, if nulls are allowed.

 So I propose, that in this case the following should work:
 {{{
 >>> beatles.members.add(john) # Creates a Membership with person=John,
 date_joined=null, invite_reason='unknown'
 }}}

 I've looked up the code which is blocking this behaviour:
 django/db/models/fields/related.py:564
 {{{
         if rel.through._meta.auto_created: # it's here
             def add(self, *objs):
                 self._add_items(self.source_field_name,
 self.target_field_name, *objs)

                 # If this is a symmetrical m2m relation to self, add the
 mirror entry in the m2m table
                 if self.symmetrical:
                     self._add_items(self.target_field_name,
 self.source_field_name, *objs)
             add.alters_data = True
 }}}

 In some places there are also exceptions:
 {{{
             raise AttributeError("Cannot use create() on a ManyToManyField
 which specifies an intermediary model. Use %s.%s's Manager instead." %
 (opts.app_label, opts.object_name))
             raise AttributeError("Cannot set values on a ManyToManyField
 which specifies an intermediary model. Use %s.%s's Manager instead." %
 (opts.app_label, opts.object_name))
 }}}

 So actually, if one comments out these "raises" and unlocks the .add()
 method then everything works smoothly. Even the ModelAdmin can now save
 m2m's without need to write your own save_m2m method. Of course, only if
 all the extra fields are nullable or have default values.

-- 
Ticket URL: <https://code.djangoproject.com/ticket/20835>
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.33f0c635a7cda133916ef94daaf066dd%40djangoproject.com.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to