python - relaciones - ¿Cuál es la forma más eficiente de almacenar una lista en los modelos de Django?
modelos en python (11)
Actualmente tengo muchos objetos Python en mi código similares a los siguientes:
class MyClass():
def __init__(self, name, friends):
self.myName = name
self.myFriends = [str(x) for x in friends]
Ahora quiero convertir esto en un modelo de Django, donde self.myName es un campo de cadena, y self.myFriends es una lista de cadenas.
from django.db import models
class myDjangoModelClass():
myName = models.CharField(max_length=64)
myFriends = ??? # what goes here?
Como la lista es una estructura de datos tan común en python, esperaba que hubiera un campo de modelo de Django para ella. Sé que puedo usar una relación ManyToMany o OneToMany, pero esperaba evitar esa indirección adicional en el código.
Editar:
Agregué esta pregunta relacionada , que las personas pueden encontrarle útil.
"La optimización temprana es la raíz de todo mal."
Con eso firmemente en mente, ¡hagamos esto! Una vez que sus aplicaciones alcanzan cierto punto, la desnormalización de datos es muy común. Si se hace correctamente, puede ahorrar numerosas búsquedas costosas de bases de datos a costa de un poco más de mantenimiento.
Para devolver una list
de nombres de amigos, tendremos que crear una clase de campo de Django personalizada que devolverá una lista cuando se acceda.
David Cramer publicó una guía para crear un SeperatedValueField en su blog . Aquí está el código:
from django.db import models
class SeparatedValuesField(models.TextField):
__metaclass__ = models.SubfieldBase
def __init__(self, *args, **kwargs):
self.token = kwargs.pop(''token'', '','')
super(SeparatedValuesField, self).__init__(*args, **kwargs)
def to_python(self, value):
if not value: return
if isinstance(value, list):
return value
return value.split(self.token)
def get_db_prep_value(self, value):
if not value: return
assert(isinstance(value, list) or isinstance(value, tuple))
return self.token.join([unicode(s) for s in value])
def value_to_string(self, obj):
value = self._get_val_from_obj(obj)
return self.get_db_prep_value(value)
La lógica de este código se refiere a serializar y deserializar valores de la base de datos a Python y viceversa. Ahora puede importar y usar fácilmente nuestro campo personalizado en la clase de modelo:
from django.db import models
from custom.fields import SeparatedValuesField
class Person(models.Model):
name = models.CharField(max_length=64)
friends = SeparatedValuesField()
¿Esta relación no se expresaría mejor como una relación de clave externa de uno a muchos con una tabla de Friends
? Entiendo que myFriends
son solo cadenas, pero creo que un mejor diseño sería crear un modelo de Friend
y que MyClass
contenga una relación de clave externa con la tabla resultante.
Como esta es una vieja pregunta, y las técnicas de Django deben haber cambiado significativamente desde entonces, esta respuesta refleja la versión 1.4 de Django, y es muy probable que sea aplicable para v 1.5.
Django usa de forma predeterminada bases de datos relacionales; deberías hacer uso de ellos. Establezca relaciones de amistad con relaciones de bases de datos (restricciones de clave externa) con el uso de ManyToManyField. Hacerlo le permite utilizar los Administradores relacionados para listas de amigos, que usan conjuntos de consulta inteligentes. Puede usar todos los métodos disponibles, como filter
o values_list
.
Utilizando relaciones y propiedades de ManyToManyField
:
class MyDjangoClass(models.Model):
name = models.CharField(...)
friends = models.ManyToManyField("self")
@property
def friendlist(self):
# Watch for large querysets: it loads everything in memory
return list(self.friends.all())
Puede acceder a la lista de amigos de un usuario de esta manera:
joseph = MyDjangoClass.objects.get(name="Joseph")
friends_of_joseph = joseph.friendlist
Sin embargo, tenga en cuenta que estas relaciones son simétricas: si José es amigo de Bob, entonces Bob es amigo de José.
El uso de la relación de uno a varios (FK de amigo a clase principal) hará que su aplicación sea más escalable (ya que puede ampliar trivialmente el objeto Friend con atributos adicionales más allá del nombre simple). Y así esta es la mejor manera
En caso de que esté usando postgres, puede usar algo como esto:
class ChessBoard(models.Model):
board = ArrayField(
ArrayField(
models.CharField(max_length=10, blank=True),
size=8,
),
size=8,
)
si necesita más detalles, puede leer el siguiente enlace: https://docs.djangoproject.com/pt-br/1.9/ref/contrib/postgres/fields/
Puede almacenar virtualmente cualquier objeto usando un campo de Pick-up de Django, a la altura de este fragmento:
Recuerde que esto finalmente tiene que terminar en una base de datos relacional. Entonces, usar relaciones realmente es la manera común de resolver este problema. Si insistes por completo en almacenar una lista en el objeto mismo, puedes hacerla, por ejemplo, separada por comas, y almacenarla en una cadena, y luego proporcionar funciones de acceso que dividan la cadena en una lista. Con eso, estará limitado a un número máximo de cadenas y perderá consultas eficientes.
Si está utilizando 1.9 Django con Postgres, puede usar las ventajas de ArrayField
Un campo para almacenar listas de datos. La mayoría de los tipos de campo se pueden usar, simplemente pasa otra instancia de campo como base_field. También puedes especificar un tamaño. ArrayField se puede anidar para almacenar matrices multidimensionales.
Una manera simple de almacenar una lista en Django es simplemente convertirla en una cadena JSON, y luego guardarla como texto en el modelo. Luego puede recuperar la lista convirtiendo la cadena (JSON) en una lista de Python. Así es cómo:
La "lista" se almacenaría en su modelo de Django así:
class MyModel(models.Model):
myList = models.TextField(null=True) # JSON-serialized (text) version of your list
En su código de vista / controlador:
Almacenar la lista en la base de datos:
import simplejson as json # this would be just ''import json'' in Python 2.7 and later
...
...
myModel = MyModel()
listIWantToStore = [1,2,3,4,5,''hello'']
myModel.myList = json.dumps(listIWantToStore)
myModel.save()
Recuperando la lista de la base de datos:
jsonDec = json.decoder.JSONDecoder()
myPythonList = jsonDec.decode(myModel.myList)
Conceptualmente, esto es lo que está pasando:
>>> myList = [1,2,3,4,5,''hello'']
>>> import simplejson as json
>>> myJsonList = json.dumps(myList)
>>> myJsonList
''[1, 2, 3, 4, 5, "hello"]''
>>> myJsonList.__class__
<type ''str''>
>>> jsonDec = json.decoder.JSONDecoder()
>>> myPythonList = jsonDec.decode(myJsonList)
>>> myPythonList
[1, 2, 3, 4, 5, u''hello'']
>>> myPythonList.__class__
<type ''list''>
Almacenar una lista de cadenas en el modelo de Django:
class Bar(models.Model):
foo = models.TextField(blank=True)
def set_list(self, element):
if self.foo:
self.foo = self.foo + "," + element
else:
self.foo = element
def get_list(self):
if self.foo:
return self.foo.split(",")
else:
None
y puedes llamarlo así:
bars = Bar()
bars.set_list("str1")
bars.set_list("str2")
list = bars.get_list()
if list is not None:
for bar in list:
print bar
else:
print "List is empty."
class Course(models.Model):
name = models.CharField(max_length=256)
students = models.ManyToManyField(Student)
class Student(models.Model):
first_name = models.CharField(max_length=256)
student_number = models.CharField(max_length=128)
# other fields, etc...
friends = models.ManyToManyField(''self'')