python - queryset - Cómo funcionan las propiedades en los campos del modelo django?
django tutorial (3)
Creo que la mejor manera de hacer esta pregunta es con algún código ... ¿puedo hacer esto? ( editar : RESPUESTA: no)
class MyModel(models.Model):
foo = models.CharField(max_length = 20)
bar = models.CharField(max_length = 20)
def get_foo(self):
if self.bar:
return self.bar
else:
return self.foo
def set_foo(self, input):
self.foo = input
foo = property(get_foo, set_foo)
o tengo que hacerlo así:
Sí, tienes que hacerlo así:
class MyModel(models.Model):
_foo = models.CharField(max_length = 20, db_column=''foo'')
bar = models.CharField(max_length = 20)
def get_foo(self):
if self.bar:
return self.bar
else:
return self._foo
def set_foo(self, input):
self._foo = input
foo = property(get_foo, set_foo)
nota : puede mantener el nombre de la columna como ''foo'' en la base de datos al pasar una columna db al campo modelo. Esto es muy útil cuando trabajas en un sistema existente y no quieres tener que hacer migraciones de db sin ningún motivo
Como se mencionó, una alternativa correcta para implementar su propia clase django.db.models.Field, se debe usar el argumento db_column y un atributo de clase personalizado (u oculto). Solo estoy reescribiendo el código en la edición de @Jiaaro siguiendo convenciones más estrictas para OOP en python (por ejemplo, si _foo debe estar realmente oculto):
class MyModel(models.Model):
__foo = models.CharField(max_length = 20, db_column=''foo'')
bar = models.CharField(max_length = 20)
@property
def foo(self):
if self.bar:
return self.bar
else:
return self.__foo
@foo.setter
def foo(self, value):
self.__foo = value
__foo se resolverá en _MyModel__foo (como se ve por dir (..) ) así oculto ( private ). Tenga en cuenta que este formulario también permite el uso de @property decorator, que en última instancia sería una forma más agradable de escribir código legible.
De nuevo, django creará la tabla * _MyModel con dos campos foo y barra .
Las soluciones anteriores sufren porque @property causa problemas en el administrador y .filter (_foo).
Una mejor solución sería anular setattr, excepto que esto puede causar problemas al inicializar el objeto ORM desde el DB. Sin embargo, hay un truco para evitar esto, y es universal.
class MyModel(models.Model):
foo = models.CharField(max_length = 20)
bar = models.CharField(max_length = 20)
def __setattr__(self, attrname, val):
setter_func = ''setter_'' + attrname
if attrname in self.__dict__ and callable(getattr(self, setter_func, None)):
super(MyModel, self).__setattr__(attrname, getattr(self, setter_func)(val))
else:
super(MyModel, self).__setattr__(attrname, val)
def setter_foo(self, val):
return val.upper()
El secreto es '' attrname en self .__ dict__ ''. ¡Cuando el modelo se inicializa desde nuevo o hidratado desde el __dict__ !
Un campo modelo ya es propiedad, por lo que diría que tienes que hacerlo de la segunda manera para evitar un conflicto de nombres.
Cuando defines foo = property (..), en realidad anula la línea foo = models .. para que ese campo ya no sea accesible.
Tendrá que usar un nombre diferente para la propiedad y el campo. De hecho, si lo haces de la manera en que lo tienes en el ejemplo n. ° 1, obtendrás un bucle infinito cuando intentes acceder a la propiedad, ya que ahora intenta regresar por sí mismo.
EDITAR: Quizás también debas considerar no usar _foo como nombre de campo, sino foo, y luego definir otro nombre para tu propiedad porque las propiedades no se pueden usar en QuerySet, por lo que necesitarás usar los nombres de campo reales cuando hagas un filtrar por ejemplo.