urls tutorial tipos create consultas campos avanzadas python django inheritance django-models

python - tutorial - tipos de campos django



¿Cómo heredarías e invalidarías las clases del modelo django para crear un listOfStringsField? (5)

Quiero crear un nuevo tipo de campo para los modelos django que es básicamente un ListOfStrings. Entonces, en su código modelo tendría lo siguiente:

models.py:

from django.db import models class ListOfStringsField(???): ??? class myDjangoModelClass(): myName = models.CharField(max_length=64) myFriends = ListOfStringsField() #

other.py:

myclass = myDjangoModelClass() myclass.myName = "bob" myclass.myFriends = ["me", "myself", "and I"] myclass.save() id = myclass.id loadedmyclass = myDjangoModelClass.objects.filter(id__exact=id) myFriendsList = loadedclass.myFriends # myFriendsList is a list and should equal ["me", "myself", "and I"]

¿Cómo haría para escribir este tipo de campo, con las siguientes estipulaciones?

  • No queremos crear un campo que agrupe todas las cadenas y las separe con un token en un campo como este . En algunos casos, es una buena solución, pero queremos mantener los datos de cadena normalizados para que las herramientas que no sean django puedan consultar los datos.
  • El campo debe crear automáticamente las tablas secundarias necesarias para almacenar los datos de cadena.
  • La tabla secundaria idealmente debería tener solo una copia de cada cadena única. Esto es opcional, pero sería bueno tenerlo.

Al mirar el código de Django, parece que quisiera hacer algo similar a lo que hace ForeignKey, pero la documentación es escasa.

Esto lleva a las siguientes preguntas:

  • Se puede hacer esto?
  • ¿Ha sido hecho (y si es así en dónde)?
  • ¿Hay alguna documentación sobre Django sobre cómo extender y anular sus clases modelo, específicamente sus clases de relación? No he visto mucha documentación sobre ese aspecto de su código, pero hay esto .

Esto viene de esta pregunta .



Hay muy buena documentación sobre cómo crear campos personalizados aquí .

Sin embargo, creo que estás pensando demasiado en esto. Parece que en realidad solo quiere una clave externa estándar, pero con la capacidad adicional de recuperar todos los elementos como una lista única. Entonces, lo más fácil sería usar una ForeignKey y definir un método get_myfield_as_list en el modelo:

class Friends(model.Model): name = models.CharField(max_length=100) my_items = models.ForeignKey(MyModel) class MyModel(models.Model): ... def get_my_friends_as_list(self): return '', ''.join(self.friends_set.values_list(''name'', flat=True))

Ahora llamando a get_my_friends_as_list() en una instancia de MyModel le devolverá una lista de cadenas, según sea necesario.


También creo que estás haciendo esto de la manera incorrecta. Tratar de hacer que un campo de Django cree una tabla de base de datos auxiliar es casi seguramente el enfoque equivocado. Sería muy difícil de hacer, y probablemente confundiría a los desarrolladores de terceros si está tratando de hacer su solución generalmente útil.

Si está intentando almacenar una masa de datos desnormalizada en una sola columna, tomaría un enfoque similar al que usó para enlazar, serializando la estructura de datos de Python y almacenándola en un TextField. Si desea que otras herramientas que no sean Django puedan operar con los datos, puede serializar a JSON (u otro formato que tenga amplio soporte de idiomas):

from django.db import models from django.utils import simplejson class JSONDataField(models.TextField): __metaclass__ = models.SubfieldBase def to_python(self, value): if value is None: return None if not isinstance(value, basestring): return value return simplejson.loads(value) def get_db_prep_save(self, value): if value is None: return None return simplejson.dumps(value)

Si solo desea un descriptor similar a django Manager que le permita operar en una lista de cadenas asociadas con un modelo, entonces puede crear manualmente una tabla de unión y usar un descriptor para administrar la relación. No es exactamente lo que necesita, pero este código debería ayudarlo a comenzar .


Gracias a todos los que respondieron. Incluso si no usé su respuesta directamente, los ejemplos y enlaces me ayudaron a avanzar en la dirección correcta.

No estoy seguro si esto está listo para la producción, pero parece estar funcionando en todas mis pruebas hasta el momento.

class ListValueDescriptor(object): def __init__(self, lvd_parent, lvd_model_name, lvd_value_type, lvd_unique, **kwargs): """ This descriptor object acts like a django field, but it will accept a list of values, instead a single value. For example: # define our model class Person(models.Model): name = models.CharField(max_length=120) friends = ListValueDescriptor("Person", "Friend", "CharField", True, max_length=120) # Later in the code we can do this p = Person("John") p.save() # we have to have an id p.friends = ["Jerry", "Jimmy", "Jamail"] ... p = Person.objects.get(name="John") friends = p.friends # and now friends is a list. lvd_parent - The name of our parent class lvd_model_name - The name of our new model lvd_value_type - The value type of the value in our new model This has to be the name of one of the valid django model field types such as ''CharField'', ''FloatField'', or a valid custom field name. lvd_unique - Set this to true if you want the values in the list to be unique in the table they are stored in. For example if you are storing a list of strings and the strings are always "foo", "bar", and "baz", your data table would only have those three strings listed in it in the database. kwargs - These are passed to the value field. """ self.related_set_name = lvd_model_name.lower() + "_set" self.model_name = lvd_model_name self.parent = lvd_parent self.unique = lvd_unique # only set this to true if they have not already set it. # this helps speed up the searchs when unique is true. kwargs[''db_index''] = kwargs.get(''db_index'', True) filter = ["lvd_parent", "lvd_model_name", "lvd_value_type", "lvd_unique"] evalStr = """class %s (models.Model):/n""" % (self.model_name) evalStr += """ value = models.%s(""" % (lvd_value_type) evalStr += self._params_from_kwargs(filter, **kwargs) evalStr += ")/n" if self.unique: evalStr += """ parent = models.ManyToManyField(''%s'')/n""" % (self.parent) else: evalStr += """ parent = models.ForeignKey(''%s'')/n""" % (self.parent) evalStr += "/n" evalStr += """self.innerClass = %s/n""" % (self.model_name) print evalStr exec (evalStr) # build the inner class def __get__(self, instance, owner): value_set = instance.__getattribute__(self.related_set_name) l = [] for x in value_set.all(): l.append(x.value) return l def __set__(self, instance, values): value_set = instance.__getattribute__(self.related_set_name) for x in values: value_set.add(self._get_or_create_value(x)) def __delete__(self, instance): pass # I should probably try and do something here. def _get_or_create_value(self, x): if self.unique: # Try and find an existing value try: return self.innerClass.objects.get(value=x) except django.core.exceptions.ObjectDoesNotExist: pass v = self.innerClass(value=x) v.save() # we have to save to create the id. return v def _params_from_kwargs(self, filter, **kwargs): """Given a dictionary of arguments, build a string which represents it as a parameter list, and filter out any keywords in filter.""" params = "" for key in kwargs: if key not in filter: value = kwargs[key] params += "%s=%s, " % (key, value.__repr__()) return params[:-2] # chop off the last '', '' class Person(models.Model): name = models.CharField(max_length=120) friends = ListValueDescriptor("Person", "Friend", "CharField", True, max_length=120)

En última instancia, creo que esto sería aún mejor si se profundizara en el código django y funcionara más como ManyToManyField o ForeignKey.


Lo que has descrito me suena muy similar a las etiquetas.
Entonces, ¿por qué no usar el etiquetado django ?
Funciona como un encanto, puede instalarlo independientemente de su aplicación y su API es bastante fácil de usar.