Issue: Curiosity: how would I go about implementing a refresh() method on Model?
Author: josefdlange
State: closed
Created: 2015-08-12 16:04:53

Here's the scoop:

The project I'm working on creates the scenario where I have local variables holding instances of a variety of Models. Changes to some have side-effects on the others (intentional ones, via signals). Right now I'm using the following to "refresh" a model instance when I want to ensure the data is up-to-date:

```
instance = Model.get(Model.something = something)
# ...
# .. do work
# ...
# .. need to "refresh"
instance = Model.get(Model.id == instance.id)
# .. continue
```

I don't mind the additional get, but I thought it might be fun to implement a `refresh()` method on my base Model subclass. At my first crack, I caught myself trying this boneheaded implementation:

```
def refresh(self):
    self = self.__class__.get(self.__class__.id == self.id)
```

Obviously that's stupid. I don't want to change `self`. Anybody holding reference to that object won't see the updates, as far as I can tell. There might be a nuance I'm missing in the Python runtime, but I'm pretty sure it wouldn't work.

Have any ideas as to what I could try? I'd be glad to write it in such a way that it could make a PR to the project, if you think it'll be useful for the general audience.


I think what you have in your first snippet is good.

``` python
instance = Model.get(...)

...

instance = instance.refresh()
```

The `refresh()` method could be:

``` python
def refresh(self):
    return type(self).get(self._pk_expr())
```


How about adding this to core? I too would find it useful is some cases.


We are also using this snippet, although I personally would expect a `refresh` method (terminology-wise) to update the instance in place, instead of returning a fresh copy.


I defer to @coleifer of course since I'm just a barely-familiar user of `peewee`, but here's my $0.02:

I'd buy you a beer if you could get that to work cleanly -- it's not as trivial as it would seem given my experience trying to work it out on my end. It's one thing to set the data on the Model subclass object, but once you start considering things like dirty fields, object caches, etc., it starts unraveling.

`thing = thing.refresh()` is really not that much more of a headache IMO. If that's losing any kind of locally-set data, my guess is you're probably Doing It Wrong™.

Maybe it's a semantics issue then -- perhaps it'd be better off called `inflate()` or `regenerate()` or `evolve()` or `reincarnate()`... actually, I kind of like `reincarnate()`...


If you want to get into a really hairy territory, you could go so far as to write some kind of code that takes in the remote updated version and _merges_ it with the local object, and if it's trying to clobber a locally-dirty field, raise some kind of exception, etc, but then again this is becoming more not-very-Pythonic the more we explore the possibilities.


I think something like this should be nice, but I in fact just wrote it now and haven't yet tested enough. This works «in place» by loading another instance of the same class and copying all its fields. Seems to work as planned, but I am pretty sure there are some unwanted side effects somewhere. Maybe somebody has a better implementation?

``` python
def reload(self):
    newer_self = self.get(self._meta.primary_key == self._get_pk_value())
    for field_name in self._meta.fields.keys():
        val = getattr(newer_self, field_name)
        setattr(self, field_name, val)
    self._dirty.clear()
```