I was reading this link in the official history
<https://code.djangoproject.com/ticket/6095#comment:31> and this other link
in this group
<https://groups.google.com/forum/#!topic/django-developers/J-ycVFfgb4E>,
but still do not understand why the issue against a way to call .add() to
add a through model. I thought several approaches (they are all backward
compatible for the end-user) that could work:
1. Avoid the restriction to call add() if the model has only the two FK
fields.
2. An additional way to call .add() specifying additional fields to fill
(this one is not mine; was discussed in the linked thread). You risk
getting a (wrapped?) exception if you do not populate other fields
appropriately.
3. (This one was the first I thought and perhaps the easiest to
implement) Allow the ManyToManyField to specify a kwarg like
`through_factory=` which expects a callable which would populate more data.
The restriction to call .add() would remain if no `through_factory=` is
specified.
4. Avoid the restriction to call delete() if the model has only the two
FK fields.
I considered these cases:
- It is quite trivial a model with only two fields, but perhaps the
intermediate models could have additional useful methods. I see no trouble
having such model and allowing .add() or .delete() calls there.
- Having a special factory method would allow calls to .add() since we'd
be providing a way to make .add() actually know how to create an instance
of the intermediate model.
- You can combine these points, implement one, none, or all of them.
- (I did not consider extended use cases for delete() intentionally,
since perhaps a strange model (with different field pairs) could fit in
different relationships, although I cannot think in a case with multiple
relationships not incurrin in, perhaps, duplicate data when tried to be
used as through= model, but anyway I prefer to keep silence for other cases
involving delete(), but the case I stated).
- It is up to the user to be careful regarding migrating an intermediate
model regarding adding, changing, or deleting fields. But anyway, this
applies to any modification in the database models right now.
- Points 1, 2, 3 and/or 4 could be implemented with no clash. A combined
approach of 1, 2, 3 would look like this (this flow only applies for the
case when the through= is present - such scenario right now only consists
of raising an exception; the case with no through= model would not be
affected at all):
- Instantiate `instance = ThroughModel(fka=a, fkb=b,
**kwargs_from_add)` with the respective model instances a and b, from
classes A and B which hold the desired M2M relationship. In this case,
the
point 2 just adds the **kwargs_from_add. If point 2 is not implemented,
no
**kwargs_from_add would be present.
- (*If point 3 is implemented*) Call the callable in
`through_factory=` invoking it `like_this(instance)`, if the callable is
present. It is expected to save the instance.
- (*If either point 1 is implemented and the model has only two
fields, or point 2 is implemented*) Manually save the instance (if
point 3 was not implemented or it was but the factory callable was not
specified). (*Otherwise - point 2 not implemented AND (point 1 not
implemented or model with more than two fields*)) Raise the currently
implemented exception for the .add() method with through= specified (with
a
different string message) because the through_factory was not present,
and
so the framework does not know how to populate additional fields.
- Catch-and-reraise (or don't catch at all and let them be) the error
for missing data in the tried-to-save model.
- An example of the callable in point 3 would be like this (just an
example for, say, a game!):
- def on_added(through_model_instance):
through_model_instance.balance = 1000.0 #although this one could
be a default value at db level.
through_model_instance.save()
through_model_instance.achievements.create(tag=
'joined-this-relation')
I understand these approaches, combined or not, could not be perfect or
bug-free. But I'd like to discuss them instead of plainly discard them.
AFAIK right now the calls to .add() are disallowed when through is
specified.
Could we work or at least discuss these use cases now? The history I quoted
seems to be pretty.... historic, and we're in 2017. The first thing I'd
like to know right not (with django 1.10 alive) is the feasability of this
feature in a future django version.
I await your comments and/or criticism ^^.
--
You received this message because you are subscribed to the Google Groups
"Django developers (Contributions to Django itself)" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/django-developers.
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-developers/ec5ffdfc-ef4b-4c30-a53d-d427cbe053e2%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.