Self ERP   Installation und Modulentwicklung mit OpenERP

Inhalt

5 Feldtypen 5.1 Einfache Datentypen 5.1.1 Boolean 5.1.2 Integer 5.1.3 Float 5.1.4 Char 5.1.5 Text 5.1.6 Select 5.1.7 Date 5.1.8 Datetime 5.2 Relationale Datentypen 5.2.1 One2Many und Many2One 5.3 Optionale Feldeigenschaften 5.3.1 required 5.3.2 readonly 5.3.3 domain 5.3.4 states 5.3.5 change_default 5.3.6 size 5.3.7 ondelete 5.3.8 translate 5.3.9 select

5 Feldtypen

Die meisten Feldtypen wurden in den vorherigen Kapiteln bereits verwendet. Dieses Kapitel enthält eine Auflistung und Beschreibung der vorgestellten Typen, sowie die Beschreibung der relationalen Feld-Typen.

Neben den angegebenen Pflicht-Feldeigenschaften, wie sie in der Auflistung beschrieben sind, gibt es auch optionale Feldeigenschaften, denen ein eigenes Unterkapitel gewidmet ist.

5.1 Einfache Datentypen

5.1.1 Boolean

'bestfriend' : fields.boolean('Bester Freund')
    

Das Boolean-Feld erhält lediglich einen beschreibenden Text als Parameter. Es wird als Checkbox dargestellt und kann lediglich die Werte "wahr" oder "nicht wahr" annehmen.

5.1.2 Integer

'friendshipyears' : fields.integer('Wieviele Jahre befreundet')
    

Das Integer-Feld erhält lediglich einen beschreibenden Text als Parameter. Es wird als Eingabefeld dargestellt und speichert ganzzahlige Wert. Standard gemäß kann das Feld auch keinen Wert (NULL) haben.

5.1.3 Float

'size' : fields.float('Groesse (meter)',digits=(12,2) )
    

Das Float-Feld erhält 2 Parameter, den beschreibenden Text sowie eine Angabe zu der Anzahl an Nach- sowie Vorkommastellen. In der angegebenen Form hat das Feld also keine Gleit- sondern Festkommaeigenschaften, mit 12 Vorkommastellen und 2 Nachkommastellen.

Der Parameter digits ist optional, wird er weggelassen, so hat das Feld Gleitkomma-Eigenschaften.

Achtung !
Benutzen Sie für Preisangaben immer die Festkommavariante ! Denn Gleitkommazahlen sind nur eine Annäherung an die gewünschte Zahl und somit nicht präzise.

5.1.4 Char

'name' : fields.char('Name', size=64),
'forename' : fields.char('Vorname', size=32)
    

Das Char-Feld erhält zwei Parameter, den beschreibenden Text sowie eine Angabe zu der Anzahl an Zeichen, die das Feld enthalten darf. Char-Felder werden als einzeilige Eingabefelder dargestellt.

Der Parameter size gibt die Anzahl der Zeichen an.

5.1.5 Text

'description' : fields.text('Kommentar')
    

Das Text-Feld erhält lediglich einen beschreibenden Text als Parameter. Es ist hinsichtlich der Zeichenanzahl nicht begrenzt und wird in einem mehrzeiligen Textfeld dargestellt.

5.1.6 Select

'gender' : fields.selection((('male','Maennlich'),('female','Weiblich')),'Geschlecht')
    

Das Select-Feld erhält zwei Parameter und wird als Listenauswahlfeld dargestellt. Die einzelnen Punkte, die zur Auswahl stehen, sind fest definiert.

Der erste Parameter beschreibt die Auswahlliste in Form eines Tupels im Tupel. Das innere Tupel enthält immer 2 Werte wobei der erste dem Wert in der Datenbank entspricht und der zweite ein darstellender Text ist.

5.1.7 Date

'birthday' : fields.date('Naechstes Treffen')
    

Das Date-Feld erhält lediglich einen beschreibenden Text als Parameter. Es wird als Eingabefeld dargestellt und ermöglicht die Datumseingabe über einen Kalender. Standard gemäß kann das Feld auch keinen Wert (NULL) haben.

5.1.8 Datetime

'meeting' : fields.datetime('Naechstes Treffen')
    

Das Date-Feld erhält lediglich einen beschreibenden Text als Parameter. Es wird als Eingabefeld dargestellt und ermöglicht die Datumseingabe über einen Kalender. Im Gegensatz zu date speichert dieses Feld eine Uhrzeit zusätzlich zum Datum. Standard gemäß kann das Feld auch keinen Wert (NULL) haben.

5.3 Relationale Datentypen

5.2.1 One2Many und Many2One

Nachdem im vorherigen Kapitel eine Freundesliste definiert wurde, wäre es interessant, jedem Freund eine Reihe von Interessen / Hobbys zuordnen zu können. Also 1 Freund : N Interessen. Wie von verschiedenen Datenbanksystem gewohnt geschieht dies, in dem eine weitere Tabelle - Namens favorites - angelegt wird, die ein Referenzfeld auf den Freund enthält.

Kopieren Sie das example_simple_table modul und benennen Sie alle nötigen Dateien - wie gewohnt - nach example_one2many um. Passen Sie __init__.py und __terp__.py entsprechend an. Die XML Datei und die Controller-Klassen-Datei ändern Sie wie im folgenden Kapitel beschrieben.

Es werden zwei Controller-Klassen definiert, die eine für die Freunde, die andere für die Interessen. Neben den Feldern, die die Relation abbilden, kommen noch zwei Datenfelder hinzu, die der Veranschaulichung dienen : name und weight.

from osv import osv, fields

class example_one2many_friends(osv.osv):
       _name = 'example.one2many.friends'
       _columns = {'name'            : fields.char('Name', size=64, required=True),
                   'forename'        : fields.char('Vorname', size=32, required=True),
                   'gender'          : fields.selection((('male','Maennlich'),('female','Weiblich')),'Geschlecht'),
                   'birthday'        : fields.date('Geburtstag'),
                   'bestfriend'      : fields.boolean('Bester Freund'),
                   'friendshipyears' : fields.integer('Wieviele Jahre befreundet'),
                   'size'            : fields.float('Groesse (meter)',digits=(12,2) ),
                   'meeting'         : fields.datetime('Naechstes Treffen'),
                   'description'     : fields.text('Kommentar'),
                   'ref_favorites'   : fields.one2many('example.one2many.favorites', 'friends_id', 'Interessen')
                  }
       _defaults = {}
example_one2many_friends()


class example_one2many_favorites(osv.osv):
       _name = 'example.one2many.favorites'
       _columns = {'name'       : fields.char('Interesse', size=64, required=True),
                   'weight'     : fields.selection((('dislike','Mag nicht'),('normal','Egal'),('like','Mag')),'Einstellung'),
                   'friends_id' : fields.many2one('example.one2many.friends','Freund',ondelete="cascade"),
		  }
       _defaults = {}
example_one2many_favorites()
		

Zuletzt muß noch in der XML-View-Datei angegeben werden, wo die Interessenliste darstellst werden soll.

<?xml version="1.0" encoding="utf-8"?>
<openerp>
    <data>
        <menuitem id="example_one2many_menu" name="Meine Freunde" sequence="0"/>

        <record model="ir.ui.view" id="example_one2many_friends_tree">
            <field name="name">example.one2many.friends.tree</field>
            <field name="model">example.one2many.friends</field>
            <field name="type">tree</field>
            <field name="arch" type="xml">
                <tree string="Alle meine Freunde">
                     ... wie gehabt ...
               </tree>
            </field>
        </record>
	
        <record model="ir.ui.view" id="example_one2many_friends_form">
            <field name="name">example.one2many.friends.form</field>
            <field name="model">example.one2many.friends</field>
            <field name="type">form</field>
            <field name="arch" type="xml">
                <form string="Freund bearbeiten" col="2">
                    <group col="2">
                     ... wie gehabt ...
                    </group>
                    <group col="1">
                        <separator string="Beschreibung"/>
                        <field name="description" nolabel="1"/>
                        <field name="ref_favorites" nolabel="1" mode="tree"/>
                    </group>
                </form>
            </field>
        </record>

        <record id="example_one2many_friends_menu_view_action" model="ir.actions.act_window">
            <field name="name">Zeige alle meine Freunde</field>
            <field name="res_model">example.one2many.friends</field>
            <field name="view_type">form</field>
        </record>
        <menuitem action="example_one2many_friends_menu_view_action" 
                     id="example_one2many_friends_menu_view" 
                     parent="example_one2many_menu"/>
    </data>
</openerp>
    

Betrachtet man die Änderungen an den Controller-Klassen, so fällt auf, das one2many und many2one als Gegenstücke arbeiten. One2many ist kein phsikalisches Feld in der Datenbank, sondern dient dazu einen Feldnamen (und die dazugehörigen Eigenschaften) in der XML-View-Datei zur Verfügung zu stellen. Ein one2many Feld wird innerhalb einer Formularansicht, als ein mehrzeiliges Listenfeld dargestellt.

Um das Verhalten des many2one Feldes beobachten zu können, wählen Sie einen Freund-Datensatz in der Formularansicht aus. Dort legen Sie ein neues Interesse an. Hierbei fällt das many2one-Feld auf, das Ihnen eine Liste mit allen möglichen Freunden anbietet. Wobei Sie sich für einen Freund entscheiden müssen, der in Zukunft zu diesem Interesse gehört. Ein Freund kann mehrere Interessen haben, aber ein Interesse kann immer nur zu einem Freund gehören.

Dieses Verhalten kann unter Umständen sehr unvorteilhaft sein, da zwei Freunde auch das gleiche Interesse haben können. Die Konsequenz ist, das das Interesse zweilmal in der Datenbank angelegt werden muß.

5.3 Optionale Feldeigenschaften

Jedes Feld kann verschiedene zusätzliche Eigenschaften erhalten, die in folgender Tabelle mit dem dazugehörigen Standardwert abgebildet sind . Manche der Feldeigenschaften lassen sich nicht im Zusammenhang mit jedem Feldtyp verwenden, solche Fälle sind in der Tabelle mit --- gekennzeichnet. Auf diese Weise können Sie der Tabelle sowohl die Standardwerte einer Eigenschaft, als auch ihren Einsatzbereich entnehmen.

Manche der Feldeigentschafften lassen sich auch über die XML-View-Datei steuern. Wobei die Eigenschaften die in der Controller-Klasse angegeben wurden überschrieben werden.

  boolean integer float char text select date datetime o2o o2m m2o m2m related
required False False False False False False False False False False False False False
readonly False False False False False False False False False False False False False
domain --- --- --- --- --- --- --- --- --- --- [] [] ---
context "" "" "" "" "" "" "" "" "" "" "" "" ""
states {} {} {} {} {} {} {} {} {} {} {} {} {}
priority 0 0 0 0 0 0 0 0 0 0 0 0 0
change_default False False False False False False False False False --- False ??? ???
size --- ??? ??? None ??? ??? --- --- --- --- --- --- ---
ondelete --- --- --- --- --- --- --- --- "set null" ??? "set null" ??? ???
translate --- --- --- False False --- --- --- --- --- --- --- ---
select False False False False False False False False False False False False False

Um mit den verschiedenen Feldtypen zu experimentieren, ist es sinnvoll, den gesamten example_one2many Ordner zu kopieren und eine neue Datenbank anzulegen. Wobei in den abgebildeten Beispielen darauf verzichtet wird, die Bezeichner auf den neuen Modulnamen anzupassen. In der Testdatenbank darf somit nur die example_one2many Kopie installiert werden um Namenskonflikte mit dem originalmodul zu verhindern.

5.3.1 required

Es handelt sich um ein Pflichtfeld, das vom Benutzer ausgefüllt werden muß.

5.3.2 readonly

Das Feld kann vom Benutzer nicht verändert werden.

5.3.3 domain

Domain grenzt die Auswahlmöglichkeiten, eines relationalen Feldes ein. Demonstrieren läst sich das Verhalten an dem many2one Feld "friends_id" der favorites Tabelle. Auch wenn es in diesem Beispiel wenig Sinn ergibt.

'friends_id' : fields.many2one('example.one2many.friends',ondelete='cascade',domain="[('bestfriend','=',True)]"),
		

Die Bedingung die mit domain angegeben wird folgt der Python Syntax. Es handelt sich um eine Tupel in Tupel Struktur wobei das innere Tupel die Bedinung darstellt. Es können mehrere Bedingungen angegeben werden. Die einzelnen Bedingungen werden innerhalb der SQL-Abfrage UND- Verknüpft.

Das erste Element des inneren Tuples ist der Name des Datenbankfeldes das für die Bedingung herangezogen werden soll.

Das zweite Element ist der Vergleichsoperator der angewendet werden soll. Zur Verfügung stehen :

Das dritte Element ist der Wert mit dem verglichen wird. Hierbei kann es sich um einen python-Ausdruck handeln. Beispiele :

'friends_id' : fields.many2one('example.one2many.friends',ondelete='cascade',domain="[('friendshipyears','=',3+5)]"),
		

Nur Freunde mit denen der Nutzer 8 Jahre befreundet ist.

'friends_id' : fields.many2one('example.one2many.friends',ondelete='cascade',domain="[('id','=',friends_id)]"),
		

Auch Felder der favorites Tabelle lassen sich im Ausdruck verwenden. In diesem Beispiel zeigt das many2One Feld immer nur den aktuellen Freund an (was in der Praxis keinen sinn ergibt). Um das Verhalten zu testen, lassen Sie das many2one Feld in favorites einfach leer und speichern den Freund-Datensatz. Öffnen sie anschließend den favorites Datensatz erneut und betätigen das many2one Feld, sie werden immer nur den aktuellen Freund angezeigt bekommen.

5.3.4 states

Mit Hilfe von states lassen sich die Feldeigenschaften in Abhängigkeit zu einem state-Feld verändern. Der Feldname "state" ist hierbei festvorgegeben. In dem folgenden Beispiel soll der Geburtstag nur dann eingebbar sein, wenn der zustand auf "gut bekannt" steht.

... wie gehabt ...

_columns = {'name'            : fields.char('Name', size=64, required=True),
           'forename'        : fields.char('Vorname', size=32, required=True,change_default=True),
           'gender'          : fields.selection((('male','Maennlich'),('female','Weiblich')),'Geschlecht'),
           'state'           : fields.selection([('met','kennengelernt'),('wellknown','gut bekannt')],'Status'),
           'birthday'        : fields.date('Geburtstag',states={'met':[('readonly',True)],'wellknown':[('readonly',False)]}),
           'bestfriend'      : fields.boolean('Bester Freund'),
           'friendshipyears' : fields.integer('Wieviele Jahre befreundet'),
           'size'            : fields.float('Groesse (meter)',digits=(12,2)),
           'meeting'         : fields.datetime('Naechstes Treffen'),
           'description'     : fields.text('Kommentar'),
           'ref_favorites'   : fields.one2many('example.one2many.favorites', 'friends_id', 'Interessen')
          }

... wie gehabt ...
		

Zunächst wird ein state-Feld angelegt. Es werden -wie gewohnt- zwei Zustände eines Selection-Feldes definiert. Anschließend muß dem birthday-Feld mitgeteilt werden, wie es sich im Falle einer State-Veränderung verhalten soll. Dies geschieht mit Hilfe eines assoziativen Arrays dessen Schlüssel den Werten des state-Feldes entsprechen müssen. Jedem Schlüssel wird ein Tupel im Tupel zugeordnet. Das innere Tupel enthält den Namen und den zukünftigen Wert einer Feldeigenschaft. Auf diese Weise lassen sich auch mehrere Feldeigentschaften verändern.

Um das Beispiel in der Praxis ausprobieren zu können, müssem Sie in der XML-View-Datei das state-Feld dem Freunde-Formular hinzufügen.

... wie gehabt ...
<form string="Freund bearbeiten" col="2">
    <group col="2">
        ... wie gehabt ...
        <field name="state"/>
    </group>
    ... wie gehabt ...
</form>
		

5.3.5 change_default

Wird change_deault auf true gesetzt, so wird das Feld als Bedingung für Standardwerte herangezogen. Verändern Sie die Definition des forename-Feldes :

'forename'        : fields.char('Vorname', size=32, required=True,change_default=True),
		

Öffnen Sie anschließend die Maske zum Anlegen eines neuen Freundes. Setzen Sie einen Haken bei "Bester Freund" und füllen den Vornamen mit dem Wert "Kunibert" aus. Anschließend drücken sie mit der Rechten Maustaste auf die "Bester Freund"-Checkbox. Wählen Sie den Punk "Set as default".

Forename erscheint mit dem Wert "Kunibert" in der Default-Bedingungsliste. Setzen Sie den Haken vor der Bedingung.

Wenn Sie in Zukunft einen neuen Freund anlegen, so wird dieser als ein bester Freund voreingestellt, sobald sein Vorname "Kunibert" lautet.

5.3.6 size

Anzahl an Zeichen, die das Feld enthalten darf.

5.3.7 ondelete

Die ondelete-Eigenschaft gibt an, wie vorgegangen werden soll, wenn ein relationaler Datensatz gelöscht wird. Zur Auswahl stehen :

Sie können das Verhalten beobachten, in dem Sie die ondelete Eigenschafft der friends_id auf "set null" setzen und einen weiteren Menüpunkt definieren, über den sich alle vorhandenen Interessen betrachten lassen :


<record id="example_one2many_favorites_menu_view_action" model="ir.actions.act_window">
    <field name="name">Zeige alle Interessen</field>
    <field name="res_model">example.one2many.favorites</field>
    <field name="view_type">form</field>
</record>
<menuitem action="example_one2many_favorites_menu_view_action" 
             id="example_one2many_favorites_menu_view" 
             parent="example_one2many_menu"/>
		

Legen Sie einen Freund an und fügen ihm einige Interessen hinzu. Anschließend löschen sie den Freund und betrachten die "Zeige alle Interessen"-Liste. Sie werden feststellen, das die Interessen nicht zusammen mit dem Freund gelöscht wurden. Stellen Sie den Wert wieder zurück auf cascade, so werden die Interessen automatisch zusammen mit dem Freund gelöscht.

5.3.8 translate

Für einige Felder kann es sinnvoll sein, je nach Sprache einen anderen Wert zu besitzen. Beispielsweise kann es bei einem internationalen Vertrieb von Vorteil sein, das der Produktname jewals in der passenden Sprache angezeigt wird. Um die translate Eigenschaft zu testen, verändern sie die example_one2many.py Datei wie folgt :

_columns = {'name'            : fields.char('Name', size=64, required=True, translate=True),
            'forename'        : fields.char('Vorname', size=32, required=True, translate=True),
            ... wie gehabt ...
            'description'     : fields.text('Kommentar', translate=True),
          }
		

Hinter den Textfeldern wird nun eine Fahne erscheinen, mit deren Hilfe Sie die Übersetzung angeben können. Wechseln Sie die Sprache des aktuellen Benutzers und der Wert des Eingabefelds ändert sich entsprechend.

5.3.9 select

Die Select-Eigenschaft gibt an, ob ein Index über das Feld gelegt werden soll. Sie läst sich auf jeden Feldtyp anwenden und führt automatisch dazu, das das Feld in der Listenansicht auch als Suchfeld herangezogen wird.

_columns = {'name'            : fields.char('Name', size=64, required=True, select=True),
            'forename'        : fields.char('Vorname', size=32, required=True, select=True),
            'gender'          : fields.selection((('male','Maennlich'),('female','Weiblich')),'Geschlecht', select=True),
            'birthday'        : fields.date('Naechstes Treffen', select=True),
            'bestfriend'      : fields.boolean('Bester Freund', select=True),
            'friendshipyears' : fields.integer('Wieviele Jahre befreundet', select=True),
            'size'            : fields.float('Groesse (meter)',digits=(12,2), select=True),
            'meeting'         : fields.datetime('Naechstes Treffen', select=True),
            'description'     : fields.text('Kommentar', select=True),
            'ref_favorites'   : fields.one2many('example.one2many.favorites', 'friends_id', 'Interessen', select=True)
          }
		

Indizes ermöglichen einen beschleunigten Zugriff auf das Feld. Dies bieten sich für relationale Felder und Such-Felder an. Insbesondere bei großen Datenbeständen. Bei kleineren Datenbeständen ist ein Index nicht zwangsweise nötig, in solchen Fällen kann die Suchfunktion ausschließlich über das select- Feld in der XML-View-Datei gesteuert werden, ohne das ein Index erzeugt wird.