¿Debe un método de clase de modificación guardarse a sí mismo o debe llamarse explícitamente después de llamar al método?

Supongamos que una clase tiene un método que modifica sus aspectos internos. ¿Debería ese método llamar a guardarse en sí mismo antes de regresar o debería dejarse a la persona que llama guardar explícitamente después de que se haya llamado al método de modificación?

Ejemplo:

Llamando explícitamente a guardar:

class Bar(models.Model): def set_foo(self, foo): self.foo = foo bar = Bar() bar.set_foo("foobar") bar.save() 

o permitiendo método para llamar a guardar:

 class Bar(models.Model): def set_foo(self, foo): self.foo = foo self.save() bar = Bar() bar.set_foo("foobar") 

Estoy trabajando con django, pero me preguntaba si existía una mejor práctica en django o en general para esta situación.

El usuario de su API podría olvidar llamar a .save () y luego atornillarse. Así que creo que es mejor llamar a salvar por él. Para casos como los que menciona Daslch, si tiene sentido, puede definir:

 def set_foo(self, foo, skip_save=False): self.foo = foo if not skip_save: self.save() 

para que el usuario pueda, si lo desea (y lo declara explícitamente), evitar el guardado.

El usuario de su API podría querer hacer varios cambios, guardar el objeto después de cada cambio no es bueno, así que no, no llame a guardar en su método.

En realidad, estoy de acuerdo con Ofri y Daslch … dependiendo de qué día de la semana sea. Si esta es solo una de las muchas rutinas de modificación que puede hacer con un objeto en particular, entonces resultará bastante costoso que cada uno haga su propia salvación. Por otro lado, si este es un evento raro y autocontenido, entonces desea guardar, ya que puede que no sea obvio para la persona que llama (es decir, alguien que no sea usted y que debe hacerse).

Por ejemplo, los eventos de etiquetado (que utilizan ManyToMany de todos modos) no deben requerir guardar () adicional en la parte de los progtwigdores.

Para abordar todos los problemas expresados ​​en varias respuestas existentes, sugiero el siguiente enfoque: haga un método, llámelo decir saving o modifying , es un administrador de contexto. La entrada a ese método establece una marca privada que dice que la modificación está en progreso; la salida restablece las banderas y realiza el guardado; todos los métodos de modificación comprueban la bandera y generan una excepción si no están configurados. Por ejemplo, al usar una clase base y un método de save que las subclases reales deben reemplazar:

 import contextlib class CarefullyDesigned(object): def __init__(self): self.__saving = False def _save(self): raise NotImplementedError('Must override `_save`!') def _checksaving(self): "Call at start of subclass `save` and modifying-methods" if not self.__saving: raise ValueError('No saving in progress!') @contextlib.contextmanager def saving(self): if self.__saving: raise ValueError('Saving already in progress!') self.__saving = True yield self._save() self.__saving = False 

Ejemplo de uso …:

 class Bar(models.Model, CarefullyDesigned): def __init__(self, *a, **k): models.Model.__init__(self, *a, **k) CarefullyDesigned.__init__(self) def _save(self): self._checksaving() self.save() def set_foo(self, foo): self._checksaving() self.foo = foo def set_fie(self, fie): self._checksaving() self.fie = fie bar = Bar() with bar.saving(): bar.set_foo("foobar") bar.set_fie("fo fum") 

Esto garantiza que el usuario no olvidará llamar a saving ni lo llamará accidentalmente de forma anidada (ese es el propósito de todas esas excepciones), y llamar a save solo una vez cuando el grupo de métodos de modificación se realice, de forma práctica y, Yo diría, de manera bastante natural.