3 Stimmen

Wie modelliert man ein Objekt mit Referenzen auf eine beliebige Anzahl beliebiger Feldtypen? (django orm)

Ich möchte eine Reihe von Modell/Objekte definieren, die für eine die Beziehung darstellen können: field_set hat viele Felder, wo Felder django.db.model Feldobjekte (IPAddressField, FilePathField etc.) sind.

Mein Ziel ist es, ein ORM-Modell zu haben, das die folgende Art von "api" unterstützt.

Von einem Controller-Ansicht lässt sagen:

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

Im Wesentlichen möchte ich "logisch gruppierte Felder" unterstützen, da ich mir vorstellen kann, viele "Feldsätze" verschiedener Typen zu unterstützen, daher mein Wunsch nach einer sinnvollen Abstraktion.

Ich möchte dieses Modell so definieren, dass ich eine Gruppe von Feldern definieren kann, wobei die Anzahl der Felder beliebig ist, ebenso wie der Feldtyp. So könnte ich eine Feldgruppe "Geographic" haben, die die Felder "State" (CharField w/ choices), "Town" (TextField) usw. enthält.

Hier ist, was ich bis jetzt gefunden habe:

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)

Allerdings führt die Verwendung dieser Modelle zu einem Fehler in der Shell und erlaubt es mir letztendlich nicht, beliebige Felder zu speichern.

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   

So Im wundern, wenn dies völlig der falsche Ansatz ist oder wenn ich django's Modellklassen ein bisschen anders verwenden müssen, um zu erhalten, was ich will.

1voto

Ich sehe mehrere Probleme mit dem Code. Erstens, mit dieser Klassendefinition:

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

Die Klasse models.Field soll nicht direkt für eine Felddefinition verwendet werden. Sie ist eine Basisklasse für alle Feldtypen in Django, so dass es ihr an Spezifika für einen bestimmten Feldtyp fehlt, um nützlich zu sein.

Das zweite Problem betrifft die folgende Zeile:

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

Wenn Sie dem Attribut f Ihrer Field sollen Sie einen bestimmten Wert angeben, der in der Datenbank gespeichert werden soll. Zum Beispiel, wenn Sie CharField para Field.f Definition würden Sie hier eine Zeichenkette zuweisen. models.Field hat jedoch keine spezifischen zuweisbaren Werte. Sie versuchen, etwas zuzuweisen, das eindeutig nicht in der DB gespeichert werden kann. Es ist modles.FileField Definition.

Django hat es also schwer, den Wert, den Sie dem Feldattribut zuweisen, aus zwei Gründen "anzupassen". Erstens, es sind keine Werte definiert für models.Field Typ zuweisen, da es sich um eine "abstrakte Klasse" oder eine Basisklasse für spezifische Feldtypdefinitionen handelt. Zweitens können Sie einem Attribut keine "Felddefinition" zuweisen und hoffen, dass es in einer DB gespeichert wird.

Ich verstehe Ihre Verwirrung. Sie versuchen im Grunde genommen, sowohl aus der Sicht der DB als auch aus der Sicht von Django etwas Unmögliches zu tun.

Ich vermute jedoch, dass es eine Lösung für Ihr Designproblem geben könnte. Wenn Sie detailliert beschreiben, was Sie erreichen wollen, kann Ihnen vielleicht jemand einen Tipp geben.

0voto

Evgeny Punkte 10456

In SQL gibt es keine Tabelle mit einer variablen Spaltenanzahl oder einem variablen Spaltentyp. Außerdem ändert Django das Datenbanklayout zur Laufzeit nicht, d.h. es ruft nicht ALTER TABLE Aussagen - soweit ich weiß.

In Django müssen Datenmodelle vollständig definiert werden, bevor Sie Ihre Anwendung ausführen.

Sie finden vielleicht diese Doku-Seite relevant für die Verwendung von "Many-to-One"-Beziehungen.

Beispiel:

#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.

Felder befüllen und aufrufen:

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

Sets emulieren in diesem Fall variable Felder in einem Tabellenprofil. In der Datenbank werden die Zins- und Adressdaten jedoch in separaten Tabellen mit Fremdschlüsselverknüpfungen zum Profil gespeichert.

Wenn Sie auf all das mit einer Accessor-Funktion zugreifen möchten, könnten Sie etwas schreiben, das Aufrufe an alle zugehörigen Sets umschließt.

Auch wenn Sie die Modelle nicht im laufenden Betrieb ändern können, können Sie neue Modelle hinzufügen und dann die

manage.py syncdb

Dadurch werden neue Tabellen in der Datenbank erstellt. Sie werden jedoch nicht in der Lage sein, Felder in bestehenden Tabellen mit "syncdb" zu ändern - Django kann das nicht. Dazu müssen Sie SQL-Befehle manuell eingeben. ( Die web2py-Plattform erledigt dies angeblich automatisch aber leider ist web2py noch nicht gut dokumentiert, aber es könnte in Bezug auf die Qualität der API besser sein als django und ist einen Blick wert)

0voto

qwarky Punkte 1

Warum sollte man so etwas nicht machen?

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

Je nachdem, welchen Typ wir wählen, wissen wir also, in welchem Feld wir den Wert finden können

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X