Opened 17 years ago

Closed 16 years ago

Last modified 11 years ago

#5599 closed Uncategorized (invalid)

Cannot save value via OneToOneField

Reported by: Peter Baumgartner Owned by: nobody
Component: Database layer (models, ORM) Version: dev
Severity: Normal Keywords: onetoone
Cc: sgt.hulka@…, mocksoul@… Triage Stage: Unreviewed
Has patch: yes Needs documentation: no
Needs tests: yes Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Trying to save a value into a OneToOne child instance via it's parent has no effect.

>>> p = Place.objects.get(pk=1)
>>> p.restaurant.waiters
10
>>> p.restaurant.waiters = 5
>>> p.save()
>>> p.restaurant.waiters
10
>>> p.restaurant.save()
>>> p.restaurant.waiters
10

Saving via the child works as expected.

>>> r = Restaurant.objects.get(pk=1)
>>> r.waiters
10
>>> r.waiters = 5
>>> r.save()
>>> r.waiters
5

Attachments (3)

onetoone_relation_field_saves.diff (868 bytes ) - added by Vadim Fint 16 years ago.
Fast fixup.
onetoone_relation_field_saves_v2.diff (1.1 KB ) - added by Vadim Fint 16 years ago.
onetoone_relation_field_saves_v3.diff (1.1 KB ) - added by MockSoul <mocksoul@…> 16 years ago.
Both prev diffs were invalid, sorry.

Download all attachments as: .zip

Change History (11)

comment:1 by Peter Baumgartner, 17 years ago

Cc: sgt.hulka@… added

comment:2 by Vadim Fint, 16 years ago

Cc: mocksoul@… added

Confirmed with latest SVN (r7003). Value does not even save in the model.

>>> p = Place.objects.get(pk=1)
>>> p.restaurant.waiters
10
>>> p.restaurant.waiters = 5
>>> p.restaurant.waiters
10

comment:3 by Vadim Fint, 16 years ago

Has patch: set
Needs tests: set

Fast fixup. I'm sorry I have no a lot of time to test this, but works fine for me.
So, please make your checks :).

  • db/models/fields/related.py

     
    126126    def __get__(self, instance, instance_type=None):
    127127        if instance is None:
    128128            raise AttributeError, "%s must be accessed via instance" % self.related.opts.object_name
     129        try:
     130            self.__cached_rel_obj
     131        except AttributeError:
     132            params = {'%s__pk' % self.related.field.name: instance._get_pk_val()}
     133            rel_obj = self.related.model._default_manager.get(**params)
     134            self.__cached_rel_obj = rel_obj
    129135
    130         params = {'%s__pk' % self.related.field.name: instance._get_pk_val()}
    131         rel_obj = self.related.model._default_manager.get(**params)
    132         return rel_obj
     136        return self.__cached_rel_obj
    133137
    134138    def __set__(self, instance, value):
    135139        if instance is None:

by Vadim Fint, 16 years ago

Fast fixup.

comment:4 by Vadim Fint, 16 years ago

More accurate fixup

  • db/models/fields/related.py

     
    126126    def __get__(self, instance, instance_type=None):
    127127        if instance is None:
    128128            raise AttributeError, "%s must be accessed via instance" % self.related.opts.object_name
     129        cache_name = self.related.field.get_cache_name()
     130        try:
     131            return getattr(self.related, cache_name)
     132        except AttributeError:
     133            params = {'%s__pk' % self.related.field.name: instance._get_pk_val()}
     134            rel_obj = self.related.model._default_manager.get(**params)
     135            setattr(self.related, cache_name, rel_obj)
     136            return rel_obj
    129137
    130         params = {'%s__pk' % self.related.field.name: instance._get_pk_val()}
    131         rel_obj = self.related.model._default_manager.get(**params)
    132         return rel_obj
    133 
    134138    def __set__(self, instance, value):
    135139        if instance is None:
    136140            raise AttributeError, "%s must be accessed via instance" % self.related.opts.object_name

by Vadim Fint, 16 years ago

by MockSoul <mocksoul@…>, 16 years ago

Both prev diffs were invalid, sorry.

comment:5 by Jacob, 16 years ago

Resolution: invalid
Status: newclosed

Yeah, setting attributes on related objects doesn't automatically save them; this'll happen with an FK or M2M, too. You need to do something like:

r = place.restaurant
r.waiters = 5
r.save()

in reply to:  5 ; comment:6 by Vadim Fint <mocksoul@…>, 16 years ago

Replying to jacob:

Yeah, setting attributes on related objects doesn't automatically save them; this'll happen with an FK or M2M, too. You need to do something like:

This is confusing me a little. Why did such design decision was choosen?

in reply to:  6 ; comment:7 by James Bennett, 16 years ago

Replying to Vadim Fint <mocksoul@gmail.com>:

Replying to jacob:

Yeah, setting attributes on related objects doesn't automatically save them; this'll happen with an FK or M2M, too. You need to do something like:

This is confusing me a little. Why did such design decision was choosen?

It's consistent; Django doesn't push changes to an object's field values down to your DB until you call save(); this is the case with any field type, and is generally considered useful because it means that you don't end up doing a hit to your DB every time you assign to an attribute.

in reply to:  7 comment:8 by daotranbang@…, 11 years ago

Easy pickings: unset
Severity: Normal
Type: Uncategorized
UI/UX: unset

Replying to ubernostrum:

Replying to Vadim Fint <mocksoul@gmail.com>:

Replying to jacob:

Yeah, setting attributes on related objects doesn't automatically save them; this'll happen with an FK or M2M, too. You need to do something like:

This is confusing me a little. Why did such design decision was choosen?

It's consistent; Django doesn't push changes to an object's field values down to your DB until you call save(); this is the case with any field type, and is generally considered useful because it means that you don't end up doing a hit to your DB every time you assign to an attribute.

I think he mean to ask why did this design was choosen:

varname = object.related_name
varname.key = value
varname.save()

instead of

object.related_name.key = value
object.related_name.save()
Note: See TracTickets for help on using tickets.
Back to Top