Complete ORM core: _inherits, Related write-back, Copy, constraints
Batch 1 (quick-fixes): - Field.Copy attribute + IsCopyable() method for copy control - Constraints now run in Write() (was only Create — bug fix) - Readonly fields silently skipped in Write() - active_test: auto-filter archived records in Search() Batch 2 (Related field write-back): - preprocessRelatedWrites() follows FK chain and writes to target model - Enables Settings page to edit company name/address/etc. - Loop protection via _write_related_depth context counter Batch 3 (_inherits delegation): - SetupAllInherits() generates Related fields from parent models - preprocessInheritsCreate() auto-creates parent records on Create - Declared on res.users, res.company, product.product - Called in LoadModules before compute setup Batch 4 (Copy method): - Recordset.Copy(defaults) with blacklist, IsCopyable check - M2M re-linking, rec_name "(copy)" suffix - Replaces simplified copy case in server dispatch Batch 5 (Onchange compute): - RunOnchangeComputes() triggers dependent computes on field change - Virtual record (ID=-1) with client values in cache - Integrated into onchange RPC handler Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -60,6 +60,9 @@ type Field struct {
|
||||
// Translation
|
||||
Translate bool // Field supports multi-language
|
||||
|
||||
// Copy
|
||||
Copy bool // Whether this field is copied on record duplication
|
||||
|
||||
// Groups (access control)
|
||||
Groups string // Comma-separated group XML IDs
|
||||
|
||||
@@ -97,6 +100,27 @@ func (f *Field) IsStored() bool {
|
||||
return f.Type.IsStored()
|
||||
}
|
||||
|
||||
// IsCopyable returns true if this field should be copied on record duplication.
|
||||
// Mirrors: odoo/orm/fields.py Field.copy
|
||||
func (f *Field) IsCopyable() bool {
|
||||
// If explicitly set, use that
|
||||
if f.Copy {
|
||||
return true
|
||||
}
|
||||
// Defaults: non-copyable for computed, O2M, id, timestamps
|
||||
if f.Name == "id" || f.Name == "create_uid" || f.Name == "create_date" ||
|
||||
f.Name == "write_uid" || f.Name == "write_date" || f.Name == "password" {
|
||||
return false
|
||||
}
|
||||
if f.Compute != "" && !f.Store {
|
||||
return false
|
||||
}
|
||||
if f.Type == TypeOne2many {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsRelational returns true for relational field types.
|
||||
func (f *Field) IsRelational() bool {
|
||||
return f.Type.IsRelational()
|
||||
@@ -130,6 +154,7 @@ type FieldOpts struct {
|
||||
OnDelete OnDelete
|
||||
CurrencyField string
|
||||
Translate bool
|
||||
Copy bool
|
||||
Groups string
|
||||
}
|
||||
|
||||
@@ -160,6 +185,7 @@ func newField(name string, typ FieldType, opts FieldOpts) *Field {
|
||||
OnDelete: opts.OnDelete,
|
||||
CurrencyField: opts.CurrencyField,
|
||||
Translate: opts.Translate,
|
||||
Copy: opts.Copy,
|
||||
Groups: opts.Groups,
|
||||
column: name,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user