Can assign directly to referenceset attribute without error but value is not persisted

Bug #394428 reported by Neil de Carteret
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
Storm
Fix Released
Low
James Henstridge

Bug Description

It's currently possible to do this:

>>> parent.children = [Child("foo"), Child("bar")]

where parent is an instance of a class with a one-to-many or many-to-many relationship to the the Child class.

With no error. But this actually overwrites the attribute with a list of unbound objects, rather than saving the relationship. It would be convenient if there was a __set__ method on the appropriate class which would at least thrust an exception under my nose, or better, do a sort of implicit:

>>> self.clear()
>>> for v in values:
... self.add(v)

(I tried monkeypatching from storm.references BoundReferenceSet and BoundIndirectReferenceSet but I just don't get the insides of Storm and found that these classes weren't what I thought they were.)

Related branches

description: updated
Revision history for this message
James Henstridge (jamesh) wrote :

__set__() is invoked on the ReferenceSet instance in the class for this case. The BoundReferenceSet and BoundIndirectReferenceSet classes are what is returned by ReferenceSet.__get__().

A possible implementation for this is:

    def __set__(self, obj, value):
        bound = self.__get__(obj)
        bound.clear()
        for item in value:
            bound.add(item)

A more advanced implementation might try to clear only the objects that won't be in the new set, and only add those objects that aren't in the old set.

Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

Note that this may create unexpected semantics. What would happen when we change:

l = [1, 2, 3]
l.append(4)
obj.attr = l

to this:

l = [1, 2, 3]
obj.attr = l
l.append(4)

In normal Python code they would result on the same thing, but with the proposed implementation they wouldn't.

I'd personally feel better if it was implemented as a method on the ReferenceSet instead (update(l)?), and __set__ raised an error.

Revision history for this message
Neil de Carteret (n3dst4) wrote :

Or it could do a bit of duck typing:

http://paste.pocoo.org/show/126501/

...which also includes an update() method, to make it look even settier.

Revision history for this message
James Henstridge (jamesh) wrote :

Neil: Gustavo's complaint is that the user might assign a mutable sequence/set to the ReferenceSet attribute and then update that object afterwards. The changes to the object would not be reflected in the database.

If we do support direct assignment, then the only way to get around this would be to limit it to immutable types (e.g. tuple or frozenset).

Jamu Kakar (jkakar)
Changed in storm:
status: New → In Progress
importance: Undecided → Low
assignee: nobody → James Henstridge (jamesh)
milestone: none → 0.17
Revision history for this message
Free Ekanayaka (free.ekanayaka) wrote :

It looks like this bug can be marked Fix-Committed?

Revision history for this message
James Henstridge (jamesh) wrote :

I guess marking this fix committed is best. We haven't implemented what the reporter asked for, but we now give a useful error message for that particular API misuse.

Changed in storm:
status: In Progress → Fix Committed
Jamu Kakar (jkakar)
Changed in storm:
status: Fix Committed → Fix Released
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.