如何使用对任意数量的任意字段类型的引用来建模对象? (django orm)

时间:2009-07-09 23:52:40

标签: python django orm

我想定义一组模型/对象,允许一个表示关系:field_set有许多字段,其中字段是django.db.model字段对象(IPAddressField,FilePathField等)。

我的目标是拥有支持以下类型的'api'的ORM模型。

从控制器视图中可以说:

# Desired api below
def homepage(request):
  from mymodels.models import ProfileGroup, FieldSet, Field

  group = ProfileGroup()
  group.name = 'Profile Information'
  group.save()

  geographicFieldSet = FieldSet()
  # Bind this 'field set' to the 'profile group'
  geographicFieldSet.profilegroup = group
  address_field = Field()
  address_field.name = 'Street Address'
  address_field.f = models.CharField(max_length=100)
  # Bind this field to the geo field set
  address_field.fieldset = geographicFieldSet 

  town_field = Field()
  town_field.name = 'Town / City'
  town_field.f = models.CharField(max_length=100)
  # Bind this field to the geo field set
  town_field.fieldset = geographicFieldSet 

  demographicFieldSet = FieldSet()
  demographicFieldSet.profilegroup = group
  age_field = Field()
  age_field.name = 'Age'
  age_field.f = models.IntegerField()
  # Bind this field to the demo field set
  age_field.fieldset = demographicFieldSet 

  # Define a 'weight_field' here similar to 'age' above.

  for obj in [geographicFieldSet, town_field, address_field, 
              demographicFieldSet, age_field, weight_field]:
    obj.save()


  # Id also add some methods to these model objects so that they 
  # know how to render themselves on the page...

  return render_to_response('page.templ', {'profile_group':group})

基本上我想支持'逻辑分组的字段',因为我看到自己支持许多不同类型的“字段集”,因此我希望有意义的抽象。

我想定义这个模型,以便我可以定义一组字段,其中字段数是任意的,字段类型也是如此。所以我可能有一个字段组'Geographic',其中包括字段'Sta​​te'(CharField w / choices),'Town'(TextField)等。

到目前为止,我想出了什么:

class ProfileGroup(models.Model):
  name = models.CharField(max_length=200)

# FieldSets have many Fields
class FieldSet(models.Model):
  name = models.CharField(max_length=200)
  profilegroup = models.ForeignKey(ProfileGroup)

class Field(models.Model):
  f = models.Field()
  fieldset = models.ForeignKey(FieldSet)

虽然使用这些模型会在shell中产生错误,但最终不允许我存储任意字段。

In [1]: from splink.profile_accumulator.models import Field, FieldSet, ProfileGroup
In [2]: import django.db
In [3]: profile_group = ProfileGroup()
In [4]: profile_group.name = 'profile group name'
In [5]: profile_group.save()
In [6]: field_set = FieldSet()
In [7]: field_set.name = 'field set name'
In [8]: field_set.profilegroup = profile_group
In [9]: field_set.save()
In [10]: field = Field()
In [11]: field.name = 'field name'
In [12]: field.f = django.db.models.FileField()
In [13]: field.save()
---------------------------------------------------------------------------
ProgrammingError                          Traceback (most recent call last)

/var/www/splinkpage.com/splinkpage.pinax/splink/<ipython console> in <module>()

/usr/lib/pymodules/python2.5/django/db/models/base.pyc in save(self, force_insert, force_update)
    309             raise ValueError("Cannot force both insert and updating in "
    310                     "model saving.")
--> 311         self.save_base(force_insert=force_insert, force_update=force_update)
    312
    313     save.alters_data = True

/usr/lib/pymodules/python2.5/django/db/models/base.pyc in save_base(self, raw, cls, force_insert, force_update)
    381             if values:
    382                 # Create a new record.
--> 383                 result = manager._insert(values, return_id=update_pk)
    384             else:
    385                 # Create a new record with defaults for everything.

/usr/lib/pymodules/python2.5/django/db/models/manager.pyc in _insert(self, values, **kwargs)
    136 
    137     def _insert(self, values, **kwargs):
--> 138         return insert_query(self.model, values, **kwargs)
    139 
    140     def _update(self, values, **kwargs):

/usr/lib/pymodules/python2.5/django/db/models/query.pyc in insert_query(model, values, return_id, raw_values)
    890     part of the public API.
    891     """
    892     query = sql.InsertQuery(model, connection)
    893     query.insert_values(values, raw_values)
--> 894     return query.execute_sql(return_id)

/usr/lib/pymodules/python2.5/django/db/models/sql/subqueries.pyc in execute_sql(self, return_id)
    307
    308     def execute_sql(self, return_id=False):
--> 309         cursor = super(InsertQuery, self).execute_sql(None)
    310         if return_id:
    311             return self.connection.ops.last_insert_id(cursor,

/usr/lib/pymodules/python2.5/django/db/models/sql/query.pyc in execute_sql(self, result_type)
   1732
   1733         cursor = self.connection.cursor()
-> 1734         cursor.execute(sql, params)
   1735
   1736         if not result_type:

/usr/lib/pymodules/python2.5/django/db/backends/util.pyc in execute(self, sql, params)
     17         start = time()
     18         try:
---> 19             return self.cursor.execute(sql, params)
     20         finally:
     21             stop = time()

ProgrammingError: can't adapt   

所以我想知道这是完全错误的方法,还是我需要使用django的模型类来获得我想要的东西。

3 个答案:

答案 0 :(得分:1)

我发现代码有几个问题。首先,使用此类定义:

class Field(models.Model):
  f = models.Field()
  fieldset = models.ForeignKey(FieldSet)

类models.Field不应该直接用于字段定义。它是Django中所有字段类型的基类,因此缺少特定字段类型的特定功能。

第二个问题是以下一行:

In [12]: field.f = django.db.models.FileField()

当您分配f实例的属性Field时,您应该提供要保存到数据库的特定值。例如,如果您使用CharField进行Field.f定义,则可以在此处指定字符串。 models.Field没有特定的可分配值。您正在尝试分配一些明显无法保存到数据库的内容。这是modles.FileField定义。

因此,由于两个原因,Django很难“调整”您分配给字段属性的值。首先,没有为models.Field类型定义的值,因为它是“抽象类”,或者是特定字段类型定义的基类。其次,您不能将“字段定义”分配给属性,并希望将其保存到数据库。

我理解你的困惑。从DB和Django的角度来看,你基本上都在尝试做不可能的事情。

我想虽然可以解决您的设计问题。如果你详细描述了你想要实现的目标,那么有人可能会给你一个提示。

答案 1 :(得分:0)

在SQL中,不存在具有可变列数或变量类型列的表。另外,据我所知,Django不会在运行时修改数据库布局 - 即不会调用ALTER TABLE语句。

在django中,必须在运行应用程序之前完全定义数据模型。

您可能会发现this doc page与“多对一”关系的使用相关。

示例:

#profile
class ProfileGroup(models.Model):
    ...

#fieldset
class FieldSet(models.Model):
    profile = models.ForeignKey(ProfileGroup)

#field 1
class Address(models.Model)
    owner = models.ForeignKey(FieldSet,related_name='address')
    #related name above adds accessor function address_set to Profile
    #more fields like street address, zip code, etc

#field 2
class Interest(models.Model)
    owner = models.ForeignKey(FieldSet,related_name='interest')
    description = models.CharField()

#etc.

填充并访问字段:

f = FieldSet()
f.interest_set.create(description='ping pong')
f.address_set.create(street='... ', zip='... ')
f.save()

addresses = f.address_set.all()
interests = f.interest_set.all()
#there are other methods to work with sets
在这种情况下,

设置模拟表Profile中的变量字段。但是,在数据库中,兴趣和地址数据存储在单独的表中,其中包含指向Profile的外键链接。

如果你想访问具有一个访问者功能的所有内容 - 你可以写一些包含所有相关集的调用。

即使您无法动态修改模型,也可以添加新模型然后发出

manage.py syncdb

这将在db中创建新表。但是,您将无法使用“syncdb”修改现有表中的字段 - django不会这样做。您必须手动输入SQL命令。 (据说web2py平台会自动处理这个,但遗憾的是web2py还没有很好的文档记录,但它在API的质量方面可能会超过django,值得一看)

答案 2 :(得分:0)

为什么不这样做?

class Info(models.Model):
    info_type = models.ForeignKey('InfoType', blank=False, null=False, default='')
    info_int = models.IntegerField(null=True, blank=True)
    info_img = models.ImageField(upload_to='info',null=True, blank=True)
    info_date = models.DateTimeField(null=True, blank=True)
    info_text = models.TextField(null=True, blank=True)
    info_bool = models.BooleanField(null=True, blank=True)
    info_char = models.CharField(max_length=128,null=True, blank=True)
    info_dec = models.DecimalField(max_digits=20, decimal_places=12, null=True, blank=True)
    info_float = models.FloatField(null=True, blank=True)
    parent_info = models.ForeignKey('self', blank=True, null=True)
class InfoType(models.Model):
    type = models.CharField(max_length=64, blank=False, null=False, default='')
    info_field = models.CharField(max_length=32, blank=False, null=False, default='')

因此,根据我们选择的类型,我们知道在哪个字段中我们可以找到值