Hi all,

A combination of recent tinkering with descriptors and a couple of
recent threads on mailing lists have collectively revealed a few
descriptor edge cases that do not appear to have been the subject of
any past discussion. This thread is an attempt to establish the
official behaviour for these edge cases.

1) What should be the behaviour of __set__ for descriptors where
multiple values are allowed (ForeignKey, ManyToManyField)?

I propose that: 'Article.reporter_set = X' be allowed, where X is any
iterable. The assignment results in the relation set being cleared and
replaced with all items in X.

i.e., Article.reporter_set = [r1, r2]
should be equivalent to:
Article.reporter_set.clear();
Article.reporter_set.add(r1, r2)

The new-style 'Article.reporters.add(r3)' syntax would continue to
exist as a non-destructive add. I'm also open (+0) to the idea of
allowing single object assignment in addition to lists (i.e.,
reporter_set = r1 is equivalent to reporter_set=[r1])

Known Objections:
Kieren Holland suggested that Article.reporter_set = [r1,r2] implies
that reporter_set is an ordered collection, because of the use of a
list in the assignment. Kieren believes that any automatic data
conversion (in this case, list into set) is a bad idea, and
potentially confusing. Therefore, __set__ should raise a TypeError if
used on a many-object descriptor.

Keiren's objection spawns a third alternative: Rather than allowing
any iterable or single object, only allow sets on the assignment. I'm
-1 on this one, type specificity being non-pythonic.

2) If a related attribute currently has a value of None, should the
__get__ method return None, or raise a DoesNotExist if accessed? Does
this behaviour change if the attribute is set null=True?

Personally, I am:
+1 returning None if null=True
+0 returning None if null=False

3) What is to become of the _id fields for ForeignKeys?

The descriptor work has always talked about reporter.id becoming the
new notation for reporter_id, but reporter_id is still there.

Is this an oversight, or a design decision that I have missed?

This bit me recently because a1.reporter_id = r.id is still legal
syntax, but doesn't update/flush the cache for a1.reporter.id. If _id
is to be retained, how is single object descriptor caching to be
handled to avoid this sort of problem?

4) Reverse descriptors and save() have an interesting relationship. Given:

class Article(models.Model):
    reporter = models.ForeignKey(Reporter)

then:
a1.reporter = r1
a1.save()

is obvious, but:

r1.reporter_set.add(a1)
a1.save()

is not so obvious. Strictly, this problem is not really related to
descriptors, and the reason is obvious if you understand the
internals, but I can see this as an area that isn't immediately
obvious to new users, and therefore acts as a potential barrier to
entry.

I can see three solutions to this. I'm not a huge fan of any of them,
but I'll mention them both as a starting point for discussion. In
order of personal preference:

a) make add()/remove()/clear()/__set__ implicit save points. This
would have the added bonus of making ForeignKey add() etc mirror
ManyToMany add() behaviour (since m2m table additions occur
immediately, where ForeignKey add()'s are not applied until save() on
the underlying objects)

b) make save() cascade into related objects - r1.save() cascades the
a1.save() call because of the relation. However, a1.save() wouldn't
have to cascade into r1.save()

c) implement a global save() - every object modification call adds a
mark in a global modification list, so a single application.save()
will save all modified objects.

Opinions?

Yours,
Russ Magee %-)

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/django-developers
-~----------~----~----~----~------~----~------~--~---

Reply via email to