Hi,

Check out https://code.djangoproject.com/ticket/9475

Seems like it would be fine if Django allowed add() and let any errors
about missing data bubble-up. I personally think passing in a defaults dict
(just like get_or_create does) would also be fine, but a callback seems
like overkill.

Here's a proof of concept:
https://github.com/django/django/compare/master...collinanderson:ticket9475

Collin


On Mon, Mar 20, 2017 at 5:46 PM, Luis Masuelli <luisfmasue...@gmail.com>
wrote:

> 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 django-developers+unsubscr...@googlegroups.com.
> To post to this group, send email to django-developers@googlegroups.com.
> 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
> <https://groups.google.com/d/msgid/django-developers/ec5ffdfc-ef4b-4c30-a53d-d427cbe053e2%40googlegroups.com?utm_medium=email&utm_source=footer>
> .
> For more options, visit https://groups.google.com/d/optout.
>

-- 
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 django-developers+unsubscr...@googlegroups.com.
To post to this group, send email to django-developers@googlegroups.com.
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/CAFO84S73z%2BXW4xj9YKmHNXu4KQ%2Be%3DEKqFfXJeK5JyCSXGANGfQ%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to