在 Django 中创建动态模型字段 [英] Creation of dynamic model fields in django

查看:51
本文介绍了在 Django 中创建动态模型字段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个关于django的问题.我有一个模型说汽车".这将包含一些基本字段,例如颜色"、车辆所有者名称"、车辆成本".

This is a problem concerning django. I have a model say "Automobiles". This will have some basic fields like "Color","Vehicle Owner Name", "Vehicle Cost".

我想提供一个表单,用户可以根据他添加的汽车添加额外的字段.例如,如果用户添加汽车",他将在运行时动态地在表单中添加额外的字段,例如汽车里程"、Cal 制造商".假设用户要添加卡车",他会添加可携带的负载",许可证"等.

I want to provide a form where the user can add extra fields depending on the automobile that he is adding. For example, if the user is adding a "Car", he will extra fields in the form, dynamically at run time, like "Car Milage", "Cal Manufacturer". Suppose if the user wants to add a "Truck", he will add "Load that can be carried", "Permit" etc.

我如何在 Django 中实现这一点?

How do I achieve this in django?

这里有两个问题:

  1. 如何提供一种用户可以在运行时添加新字段的表单?
  2. 如何将字段添加到数据库中以便以后可以检索/查询?

推荐答案

有几种方法:

  • 键/值模型(简单,支持良好)
  • TextField 中的 JSON 数据(简单、灵活,无法轻松搜索/索引)
  • 动态模型定义(没那么容易,很多隐藏的问题)

听起来您想要最后一个,但我不确定它是否最适合您.Django 很容易更改/更新,如果系统管理员想要额外的字段,只需为它们添加它们并使用南迁移.我不喜欢通用的键/值数据库模式,像 Django 这样强大的框架的全部意义在于,您可以轻松编写和重写自定义模式,而无需求助于通用方法.

It sounds like you want the last one, but I'm not sure it's the best for you. Django is very easy to change/update, if system admins want extra fields, just add them for them and use south to migrate. I don't like generic key/value database schemas, the whole point of a powerful framework like Django is that you can easily write and rewrite custom schemas without resorting to generic approaches.

如果您必须允许站点用户/管理员直接定义他们的数据,我相信其他人会向您展示如何执行上述前两种方法.第三种方法是你所要求的,更疯狂一点,我会告诉你怎么做.我不建议在几乎所有情况下都使用它,但有时它是合适的.

If you must allow site users/administrators to directly define their data, I'm sure others will show you how to do the first two approaches above. The third approach is what you were asking for, and a bit more crazy, I'll show you how to do. I don't recommend using it in almost all cases, but sometimes it's appropriate.

动态模型

一旦您知道该做什么,这相对简单.你需要:

Once you know what to do, this is relatively straightforward. You'll need:

  • 1 或 2 个模型来存储字段的名称和类型
  • (可选)为您的(子类)动态模型定义通用功能的抽象模型
  • 在需要时构建(或重建)动态模型的函数
  • 在添加/删除/重命名字段时构建或更新数据库表的代码

1.存储模型定义

这取决于你.我想你会有一个模型 CustomCarModelCustomField 来让用户/管理员定义和存储你想要的字段的名称和类型.不必直接镜像 Django 字段,可以自己制作类型,让用户更容易理解.

This is up to you. I imagine you'll have a model CustomCarModel and CustomField to let the user/admin define and store the names and types of the fields you want. You don't have to mirror Django fields directly, you can make your own types that the user may understand better.

使用带有内联表单集的 forms.ModelForm 来让用户构建他们的自定义类.

Use a forms.ModelForm with inline formsets to let the user build their custom class.

2.抽象模型

同样,这很简单,只需为所有动态模型创建一个具有通用字段/方法的基本模型.使这个模型抽象.

Again, this is straightforward, just create a base model with the common fields/methods for all your dynamic models. Make this model abstract.

3.构建动态模型

定义一个函数,该函数获取所需的信息(可能是#1 中的类的实例)并生成模型类.这是一个基本示例:

Define a function that takes the required information (maybe an instance of your class from #1) and produces a model class. This is a basic example:

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.更新数据库表的代码

上面的代码将为您生成一个动态模型,但不会创建数据库表.我建议使用 South 进行表格操作.这里有几个函数,您可以将它们连接到保存前/保存后信号:

The code above will generate a dynamic model for you, but won't create the database tables. I recommend using South for table manipulation. Here are a couple of functions, which you can connect to pre/post-save signals:

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)

这就给你了!你可以调用 get_custom_car_model() 来提供一个 django 模型,你可以用它来做普通的 django 查询:

And there you have it! You can call get_custom_car_model() to deliver a django model, which you can use to do normal django queries:

CarModel = get_custom_car_model(my_definition)
CarModel.objects.all()

问题

  • 在运行创建它们的代码之前,您的模型对 Django 是隐藏的.但是,您可以为定义模型的 class_prepared 信号中的每个定义实例运行 get_custom_car_model.
  • ForeignKeys/ManyToManyFields 可能不起作用(我没试过)
  • 您将希望使用 Django 的模型缓存,这样您就不必每次使用它时都运行查询并创建模型.为简单起见,我在上面省略了这一点
  • 您可以将动态模型放入管理员,但您还需要动态创建管理员类,并使用信号适当地注册/重新注册/取消注册.
  • Your models are hidden from Django until the code creating them is run. You can however run get_custom_car_model for every instance of your definitions in the class_prepared signal for your definition model.
  • ForeignKeys/ManyToManyFields may not work (I haven't tried)
  • You will want to use Django's model cache so you don't have to run queries and create the model every time you want to use this. I've left this out above for simplicity
  • You can get your dynamic models into the admin, but you'll need to dynamically create the admin class as well, and register/reregister/unregister appropriately using signals.

概述

如果您对增加的复杂性和问题感到满意,那就尽情享受吧!一个它正在运行,由于 Django 和 Python 的灵活性,它完全按预期工作.您可以将您的模型输入 Django 的 ModelForm 以让用户编辑他们的实例,并直接使用数据库的字段执行查询.如果上面有任何你不明白的地方,你可能最好不要采用这种方法(我故意没有解释一些初学者的概念).保持简单!

If you're fine with the added complication and problems, enjoy! One it's running, it works exactly as expected thanks to Django and Python's flexibility. You can feed your model into Django's ModelForm to let the user edit their instances, and perform queries using the database's fields directly. If there is anything you don't understand in the above, you're probably best off not taking this approach (I've intentionally not explained what some of the concepts are for beginners). Keep it Simple!

我真的不认为很多人需要这个,但我自己使用过它,我们在表格中有很多数据,真的,真的需要让用户自定义列,很少改变.

I really don't think many people need this, but I have used it myself, where we had lots of data in the tables and really, really needed to let the users customise the columns, which changed rarely.

这篇关于在 Django 中创建动态模型字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆