Nice summary of things to think about, Russ.==

On Mon, 2006-03-06 at 14:30 +0800, Russell Keith-Magee wrote:
[...]
> 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.

I'm not convinced by this objection (or only allowing set assignment)
and prefer your original idea: any iterable will do. This is analogous
with the Set class. s = Set([1, 2, 3, 4]) does not imply any ordering on
elements in the set, despite the list involved; it just does a
conversion from list to Set.

+1 on assigning any iterable.
+0 on allowing singleton assignments (it makes sense in many cases, but
is slightly inconsistent).

> 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?

I don't understand this, because null = False and blank = True seems
like it should be impossible. Either the related object must exist (in
which case, if it is missing, you have an inconsistent state) or it is
optional. So I would have thought this was dependent upon blank = True
(and blank = True => null = True, otherwise we get very unusual SQL
entries).

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

+1 on returning None if null = True, blank = True
-1 on returning None if blank = False (inconsistent model)
"confused" for the other possibilities.

> 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)

This means your object would have to totally valid at the point you
assigned to the first relation field. Not sure this will always be
possible (two non-null foreign keys, for example?). Am I missing
something here?

-1 on this, at the moment.

> 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()

This sounds reasonable. It took me a few minutes to work out what was
going on in this case, so I initially suspected it was confusing or
complicated. But once one gets to the point of wanting to change
r1.reporter_set, rather than a1.reporter, it should be understandable.

Why don't you like this option? I realise you probably want to let
others speak first, but at some point in a few days, it would be
interesting to hear your problems with each of the three alternatives
here, since you have clearly thought about this a lot.

> 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.

You need to have every object consistent / valid at the moment you call
save. It's certainly possible, but it seems fiddly.

-0 on this option.

Again, nice write-up. Hopefully some consensus will arise.

Thanks,
Malcolm


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to django-developers@googlegroups.com
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