Как смоделировать объект со ссылками на произвольное количество произвольных типов полей? (Джанго Орм) - PullRequest
2 голосов
/ 10 июля 2009

Я хотел бы определить набор моделей / объектов, которые позволяют одному представлять отношение: field_set имеет много полей, где поля являются объектами поля django.db.model (IPAddressField, FilePathField и т. Д.).

Моя цель - создать модель ORM, которая поддерживает следующий тип «api».

В виде контроллера скажем:

# 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})

По сути, я хочу поддерживать «логически сгруппированные поля», так как я вижу, что поддерживаю множество «наборов полей» разных типов, таким образом, мое стремление к содержательной абстракции.

Я хотел бы определить эту модель, чтобы я мог определить группу полей, где число полей произвольно, как и тип поля. Поэтому у меня может быть группа полей «Географическая», которая включает в себя поля «Штат» (CharField w / choices), «Город» (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)

Хотя использование этих моделей приводит к ошибке в оболочке и в конечном итоге не позволяет мне хранить произвольные поля.

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 ]

1 голос
/ 10 июля 2009

Я вижу несколько проблем с кодом. Во-первых, с этим определением класса:

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 не определены значения, которые можно назначить, поскольку это «абстрактный класс» или базовый класс для определений определенного типа поля. Во-вторых, вы не можете присвоить «определение поля» атрибуту и ​​надеяться, что он будет сохранен в БД.

Я понимаю ваше замешательство. Вы в основном пытаетесь сделать невозможное как с точки зрения БД, так и с точки зрения Джанго.

Я полагаю, что может быть решение вашей проблемы дизайна. Если вы подробно опишете, чего пытаетесь достичь, возможно, кто-то может дать вам подсказку.

0 голосов
/ 25 мая 2014

Почему бы не сделать что-то подобное?

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='')

поэтому в зависимости от того, какой тип мы выберем, мы знаем, в каком поле мы можем найти значение

0 голосов
/ 10 июля 2009

В SQL нет такой вещи как таблица с переменным числом столбцов или столбцов переменного типа. Кроме того, Django не изменяет структуру базы данных во время выполнения - то есть не вызывает ALTER TABLE операторов - насколько я знаю.

В django модели данных должны быть полностью определены до запуска приложения.

Вы можете найти эту страницу документа релевантной для использования отношений "многие к одному".

Пример:

#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

Это создаст новые таблицы в БД. Однако вы не сможете изменять поля в существующих таблицах с помощью 'syncdb' - django этого не делает. Для этого вам придется вводить команды SQL вручную. ( предположительно, платформа web2py обрабатывает это автоматически , но, к сожалению, web2py еще недостаточно хорошо документирована, но это может быть намного ниже django с точки зрения качества API и стоит посмотреть)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...