Hay algunos enfoques:
clave
- /modelo de valor (Fácil, bien apoyado)
- datos JSON en un campo de texto (fácil, flexible, no puede buscar/indexar fácilmente)
- Definición del modelo dinámico (no tan fácil, muchos problemas ocultos)
Parece que quieres la última, pero no estoy seguro de que sea la mejor para ti. Django es muy fácil de cambiar/actualizar, si los administradores del sistema quieren campos adicionales, solo agrégalos y utiliza el sur para migrar. No me gustan los esquemas genéricos de bases de datos clave/valor, el objetivo de un potente framework como Django es que puede escribir y reescribir fácilmente esquemas personalizados sin recurrir a enfoques genéricos.
Si debe permitir que los usuarios/administradores del sitio definan directamente sus datos, estoy seguro de que otros le mostrarán cómo hacer los dos primeros enfoques anteriores. El tercer enfoque es lo que estabas pidiendo, y un poco más loco, te mostraré cómo hacerlo. No recomiendo usarlo en casi todos los casos, pero a veces es apropiado.
modelos dinámicos
Una vez que sepa qué hacer, esto es relativamente sencillo. Necesitará:
- 1 o 2 modelos para almacenar los nombres y tipos de los campos
- (opcional) Un modelo abstracto para definir la funcionalidad común para sus subclases() modelos dinámicos
- Una función para construir (o reconstruir) el modelo dinámico cuando sea necesario
- Código para construir o actualizar las tablas de base de datos cuando se añaden campos/eliminados/renombrado
1. Almacenamiento de la definición del modelo
Depende de usted. Imagino que tendrá un modelo CustomCarModel
y CustomField
para permitir que el usuario/administrador defina y almacene los nombres y tipos de los campos que desee. No es necesario que duplique los campos de Django directamente, puede crear sus propios tipos que el usuario pueda comprender mejor.
Use un forms.ModelForm
con formularios en línea para que el usuario cree su clase personalizada.
2. Modelo abstracto
Una vez más, esto es sencillo, basta con crear un modelo base con los campos/métodos comunes para todos los modelos dinámicos. Haz este modelo abstracto
3. Construir un modelo dinámico
Definir una función que toma la información requerida (tal vez una instancia de su clase desde el # 1) y produce una clase del modelo. Este es un ejemplo básico:
from django.db.models.loading import cache
from django.db import models
def get_custom_car_model(car_model_definition):
""" Create a custom (dynamic) model class based on the given definition.
"""
# What's the name of your app?
_app_label = 'myapp'
# you need to come up with a unique table name
_db_table = 'dynamic_car_%d' % car_model_definition.pk
# you need to come up with a unique model name (used in model caching)
_model_name = "DynamicCar%d" % car_model_definition.pk
# Remove any exist model definition from Django's cache
try:
del cache.app_models[_app_label][_model_name.lower()]
except KeyError:
pass
# We'll build the class attributes here
attrs = {}
# Store a link to the definition for convenience
attrs['car_model_definition'] = car_model_definition
# Create the relevant meta information
class Meta:
app_label = _app_label
db_table = _db_table
managed = False
verbose_name = 'Dynamic Car %s' % car_model_definition
verbose_name_plural = 'Dynamic Cars for %s' % car_model_definition
ordering = ('my_field',)
attrs['__module__'] = 'path.to.your.apps.module'
attrs['Meta'] = Meta
# All of that was just getting the class ready, here is the magic
# Build your model by adding django database Field subclasses to the attrs dict
# What this looks like depends on how you store the users's definitions
# For now, I'll just make them all CharFields
for field in car_model_definition.fields.all():
attrs[field.name] = models.CharField(max_length=50, db_index=True)
# Create the new model class
model_class = type(_model_name, (CustomCarModelBase,), attrs)
return model_class
4. El código para actualizar las tablas de la base
El código anterior generará un modelo dinámico para ti, pero no va a crear las tablas de base de datos. Recomiendo usar el sur para la manipulación de la mesa. Aquí hay un par de funciones, que puede conectar a las señales de pre/post-guardado:
import logging
from south.db import db
from django.db import connection
def create_db_table(model_class):
""" Takes a Django model class and create a database table, if necessary.
"""
table_name = model_class._meta.db_table
if (connection.introspection.table_name_converter(table_name)
not in connection.introspection.table_names()):
fields = [(f.name, f) for f in model_class._meta.fields]
db.create_table(table_name, fields)
logging.debug("Creating table '%s'" % table_name)
def add_necessary_db_columns(model_class):
""" Creates new table or relevant columns as necessary based on the model_class.
No columns or data are renamed or removed.
XXX: May need tweaking if db_column != field.name
"""
# Create table if missing
create_db_table(model_class)
# Add field columns if missing
table_name = model_class._meta.db_table
fields = [(f.column, f) for f in model_class._meta.fields]
db_column_names = [row[0] for row in connection.introspection.get_table_description(connection.cursor(), table_name)]
for column_name, field in fields:
if column_name not in db_column_names:
logging.debug("Adding field '%s' to table '%s'" % (column_name, table_name))
db.add_column(table_name, column_name, field)
¡Y ya lo tiene! Puede llamar get_custom_car_model()
entregar un modelo de Django, que se puede utilizar para realizar consultas django normales:
CarModel = get_custom_car_model(my_definition)
CarModel.objects.all()
problemas
- Sus modelos están ocultas a Django hasta que el código de la creación de ellos está dirigido. Sin embargo, puede ejecutar
get_custom_car_model
para cada instancia de sus definiciones en la señal class_prepared
para su modelo de definición.
ForeignKeys
/ManyToManyFields
no pueden trabajar (no he probado)
- Usted tendrá que usar caché modelo de Django por lo que no tiene que ejecutar consultas y crear el modelo cada vez que se desea utilizar este. Lo he omitido anteriormente por simplicidad
- Puede obtener sus modelos dinámicos en el administrador, pero también deberá crear dinámicamente la clase de administrador y registrar/volver a registrar/anular el registro de forma apropiada mediante señales.
general
Si estás bien con la complicación añadida y problemas, disfrutar! Uno se está ejecutando, funciona exactamente como se esperaba gracias a la flexibilidad de Django y Python. Puede alimentar su modelo con ModelForm
de Django para permitir que el usuario edite sus instancias y realizar consultas utilizando directamente los campos de la base de datos. Si hay algo que no entiendes en lo anterior, es mejor que no tomes este enfoque (no he explicado intencionadamente algunos de los conceptos para principiantes). ¡Mantenlo simple!
Realmente no creo que mucha gente lo necesite, pero yo mismo lo he usado, donde teníamos muchos datos en las tablas y realmente, realmente necesitábamos que los usuarios personalizaran las columnas, lo que cambiaba raramente.
** Puede que quiera echar un vistazo a esta respuesta con información reciente sobre el tema: ** http://stackoverflow.com/q/7933596/497056 –