ManyRelatedManager with explicit intermediary model

105 views
Skip to first unread message

Roald de Vries

unread,
Sep 20, 2011, 9:52:04 AM9/20/11
to django-d...@googlegroups.com
Hi all,

Is there a fundamental reason that I'm missing (other than "nobody's
taken the trouble of writing it") that I can't do the following? If
there isn't I'll create a ticket for it.

class R(Model):
user = ForeignKey(User)
my_model = ForeignKey('MyModel')
comment = CharField(max_length=100, blank=True)

class MyModel(Model):
users = ManyToManyField(User, through=R, null=True)

m = MyModel.objects.create()
u = User.objects.create_user('roald', 'down...@gmail.com',
'password')


# these things I can't do:
m.users.add(u)
m.users.add(u, comment='Blablabla')

Cheers, Roald

Łukasz Rekucki

unread,
Sep 20, 2011, 9:57:38 AM9/20/11
to django-d...@googlegroups.com

I'm 100% sure there's *at least one* ticket for this. You just need to
search for it and you'll probably find the discussion of this too.

--
Łukasz Rekucki

Stephan Jaensch

unread,
Sep 20, 2011, 10:23:03 AM9/20/11
to django-d...@googlegroups.com
Hi Roald,

https://docs.djangoproject.com/en/1.3/topics/db/models/#intermediary-manytomany

You can't use add() when specifying the intermediate model. You would have to check all fields of the intermediate model and make sure all of them have defaults or are allowed to be null. It might not be worth the trouble of implementing it.

Cheers,
Stephan

Roald de Vries

unread,
Sep 20, 2011, 11:12:38 AM9/20/11
to django-d...@googlegroups.com

I don't see how this is different from the create method on the
intermediary model.

Cheers, Roald

PS: I found an open ticket on this, https://code.djangoproject.com/ticket/9475

Johannes Dollinger

unread,
Sep 20, 2011, 11:31:39 AM9/20/11
to django-d...@googlegroups.com

#9475 [1] is the ticket for the default value case. Russell's comments indicate that the other case (providing extra attributes for the intermediary model) may also be in scope for this ticket.

[1] https://code.djangoproject.com/ticket/9475

__
Johannes

Tom Evans

unread,
Sep 20, 2011, 11:50:47 AM9/20/11
to django-d...@googlegroups.com
On Tue, Sep 20, 2011 at 4:12 PM, Roald de Vries <down...@gmail.com> wrote:
>
> I don't see how this is different from the create method on the intermediary
> model.
>
> Cheers, Roald
>
> PS: I found an open ticket on this,
> https://code.djangoproject.com/ticket/9475
>

Here is the function definition for add() on related object manager:

add(obj1[, obj2, ...])

As you can see, it can be used to add multiple objects to the
relationship in one go, and therefore the arguments to this function
would need to change to support what you propose. This would require
going through the whole deprecation procedure (2/3 major releases
before it is gone), and I guess the pain outweighs the gain on that
one.

create() takes **kwargs, but those arguments relate to the instance
being created on the other end of the relationship, there would still
be no way to specify non-default values for the intermediate model.
You would have to do something similar to passing a defaults
dictionary to create(), which then makes it different to how create()
on an object manager works, and introduces another field that you
would have to do some magic to work around.

I guess the main thing is what's the point? The argument is over which
of these is prettier:

model_a_instance.modelb_set.add(model_b_instance)

and

Intermediate.objects.create(model_a=model_a_instance, model_b=model_b_instance)

Beauty contests in code are rather pointless - the documentation has
for a long time said that the latter is the only way you can do it,
and most developers are now used to that.

Cheers

Tom

Roald de Vries

unread,
Sep 21, 2011, 4:02:12 AM9/21/11
to django-d...@googlegroups.com

On Sep 20, 2011, at 5:50 PM, Tom Evans wrote:

> On Tue, Sep 20, 2011 at 4:12 PM, Roald de Vries <down...@gmail.com>
> wrote:
>>
>> I don't see how this is different from the create method on the
>> intermediary
>> model.
>>
>> Cheers, Roald
>>
>> PS: I found an open ticket on this,
>> https://code.djangoproject.com/ticket/9475
>>
>
> Here is the function definition for add() on related object manager:
>
> add(obj1[, obj2, ...])
>
> As you can see, it can be used to add multiple objects to the
> relationship in one go, and therefore the arguments to this function
> would need to change to support what you propose. This would require
> going through the whole deprecation procedure (2/3 major releases
> before it is gone), and I guess the pain outweighs the gain on that
> one.

add(*objs, **kwargs) is backward compatible with add(*objs), so that's
not the reason a deprecation procedure is needed. The thing that might
be considered backward incompatible is the fact that with this new
feature, the 'add' method is also defined on ManyRelatedManagers with
explicit intermediary models.

> create() takes **kwargs, but those arguments relate to the instance
> being created on the other end of the relationship, there would still
> be no way to specify non-default values for the intermediate model.
> You would have to do something similar to passing a defaults
> dictionary to create(), which then makes it different to how create()
> on an object manager works, and introduces another field that you
> would have to do some magic to work around.
>
> I guess the main thing is what's the point? The argument is over which
> of these is prettier:
>
> model_a_instance.modelb_set.add(model_b_instance)
>
> and
>
> Intermediate.objects.create(model_a=model_a_instance,
> model_b=model_b_instance)
>
> Beauty contests in code are rather pointless - the documentation has
> for a long time said that the latter is the only way you can do it,
> and most developers are now used to that.

I don't want to forbid the second form, you may still use it if you
like it better. For me, it seems more consistent (which I think is
more beautiful) to create a relation between 2 instances from one of
the instances, because I always access the other instance through the
ManyRelatedManager on the one. If there are enough people that like
the first form, then that's the point.


Russell Keith-Magee

unread,
Sep 21, 2011, 6:17:34 AM9/21/11
to django-d...@googlegroups.com
2011/9/20 Łukasz Rekucki <lrek...@gmail.com>:

There certainly is "at least one" ticket :-)

There's the original ticket that introduced m2m intermediate models:

https://code.djangoproject.com/ticket/6095

And there's this one:

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

which asks for this feature specifically.

Back when the feature was added (#6095), we discussed add() with
intermediate models that have extra data. If you read the full ticket
history for #6095, and #9475, you can see the edge cases that existed
at the time. Ultimately, we punted on the issue in the interest of
delivering *something*.

I'm certainly interested in the idea, as long as the edge cases can be
managed and/or explained.

Yours
Russ Magee %-)

Reply all
Reply to author
Forward
0 new messages