attrs

package
v1.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 12, 2025 License: GPL-2.0 Imports: 28 Imported by: 23

Documentation

Index

Constants

View Source
const (
	// AttrNameKey (string) is the name of the field.
	AttrNameKey = "field.name"

	// AttrMaxLengthKey (int64) is the maximum length of the field.
	AttrMaxLengthKey = "field.max_length"

	// AttrMinLengthKey (int64) is the minimum length of the field.
	AttrMinLengthKey = "field.min_length"

	// AttrMinValueKey (float64) is the minimum value of the field.
	AttrMinValueKey = "field.min_value"

	// AttrMaxValueKey (float64) is the maximum value of the field.
	AttrMaxValueKey = "field.max_value"

	// AttrAllowNullKey (bool) is whether the field allows null values.
	AttrAllowNullKey = "field.allow_null"

	// AttrAllowBlankKey (bool) is whether the field allows blank values.
	AttrAllowBlankKey = "field.allow_blank"

	// AttrAllowEditKey (bool) is whether the field is read-only.
	AttrAllowEditKey = "field.read_only"

	// AttrIsPrimaryKey (bool) is whether the field is a primary key.
	AttrIsPrimaryKey = "field.primary"

	// AttrAutoIncrementKey (bool) is whether the field is an auto-incrementing field.
	AttrAutoIncrementKey = "field.auto_increment"

	// AttrUniqueKey (bool) is whether the field is a unique field.
	AttrUniqueKey = "field.unique"

	// AttrReverseAliasKey (string) is the reverse alias of the field.
	AttrReverseAliasKey = "field.reverse_alias"
)

Keys of attributes defined with the `Attrs()` method on fields.

These are used to store extra information about the field.

We provide some default keys which might be useful for implementing an ORM, but any keys can be used.

View Source
const (
	HookFormFieldForType = "attrs.FormFieldForType"
	DefaultForType       = "attrs.DefaultForType"
)
View Source
const ATTR_TAG_NAME = "attrs"

Variables

View Source
var (

	// A signal that is called before a model is registered.
	//
	// This can be used to add custom logic before a model is registered.
	OnBeforeModelRegister = modelSignalPool.Get("attrs.OnBeforeModelRegister")

	// A signal that is called after a model is registered.
	//
	// This can be used to add custom logic after a model is registered.
	OnModelRegister = modelSignalPool.Get("attrs.OnModelRegister")
)

The following signals are available for hooking into the `attrs` package's model registration process.

It can be hooked into to add custom logic before a model is registered.

Example usage:

func init() {
	attrs.OnBeforeModelRegister.Listen(func(s signals.Signal[attrs.Definer], obj attrs.Definer) error {
		// Do something before the model is registered
		return nil
	})
}
View Source
var (
	ErrEmptyString      = errors.New("empty string")
	ErrConvertingString = errors.New("error converting string to number")
)

Functions

func CastToNumber added in v1.7.0

func CastToNumber[T any](v any) (T, error)

CastToNumber converts a value of any type (int, float, string) to a number of type T. It returns the converted value and an error if the conversion fails.

func FieldNames

func FieldNames(d any, exclude []string) []string

A shortcut for getting the names of all fields in a Definer.

The exclude parameter can be used to exclude certain fields from the result.

This function is useful when you need to get the names of all fields in a model, but you want to exclude certain fields (e.g. fields that are not editable).

func ForceSet

func ForceSet(d Definer, name string, value interface{}) error

ForceSet sets the value of a field on a Definer.

If the field is not found, the value is not of the correct type or another constraint is violated, this function will panic.

This function will allow setting the value of a field that is marked as not editable.

func Get

func Get[T any](d Definer, name string) T

Get retrieves the value of a field on a Definer.

If the field is not found, this function will panic.

Type assertions are used to ensure that the value is of the correct type, as well as providing less work for the caller.

func InterfaceList added in v1.7.0

func InterfaceList[T any](list []T) []any

InterfaceList converts a slice of []T to []any.

func IsModelRegistered added in v1.7.0

func IsModelRegistered(model Definer) bool

func Method

func Method[T any](obj interface{}, name string) (n T, ok bool)

Method retrieves a method from an object.

The generic type parameter must be the type of the method.

func PrimaryKey added in v1.6.6

func PrimaryKey(d Definer) interface{}

PrimaryKey returns the primary key field of a Definer.

If the primary key field is not found, this function will panic.

func RegisterDefaultType added in v1.7.0

func RegisterDefaultType(valueOfType any, getDefault func(f Field, new_field_t_indirected reflect.Type, field_v reflect.Value) (interface{}, bool))

RegisterDefaultType registers a default value to be used for that specific type.

This is useful when implementing custom types.

Example usage:

RegisterDefaultType(
	json.RawMessage{},
	func(f Field, new_field_t_indirected reflect.Type, field_v reflect.Value) (interface{}, bool) {
		if field_v.IsValid() && field_v.Type() == reflect.TypeOf(json.RawMessage{}) {
			return json.RawMessage{}, true
		}
		return nil, false
	},
)

func RegisterFormFieldType

func RegisterFormFieldType(valueOfType any, getField func(opts ...func(fields.Field)) fields.Field)

RegisterFormFieldType registers a field type for a given valueOfType.

getField is a function that returns a fields.Field for the given valueOfType.

The valueOfType can be a reflect.Type or any value, in which case the reflect.TypeOf(valueOfType) will be used.

This is a shortcut function for the `HookFormFieldForType` hook.

Example usage:

	RegisterFormFieldType(
		json.RawMessage{},
		func(opts ...func(fields.Field)) fields.Field {
			return fields.JSONField[json.RawMessage](opts...)
		},
 	)

func RegisterModel added in v1.7.0

func RegisterModel(model Definer)

RegisterModel registers a model to be used for any ORM- type operations.

Models are registered automatically in [django.Initialize], but you can also register them manually if needed.

func Set

func Set(d Definer, name string, value interface{}) error

Set sets the value of a field on a Definer.

If the field is not found, the value is not of the correct type or another constraint is violated, this function will panic.

If the field is marked as non editable, this function will panic.

func SetMany

func SetMany(d Definer, values map[string]interface{}) error

SetMany sets multiple fields on a Definer.

The values parameter is a map where the keys are the names of the fields to set.

The values must be of the correct type for the fields.

func SetPrimaryKey added in v1.7.0

func SetPrimaryKey(d Definer, value interface{}) error

SetPrimaryKey sets the primary key field of a Definer.

If the primary key field is not found, this function will panic.

func StoreOnMeta added in v1.7.0

func StoreOnMeta(m Definer, key string, value any)

func ToString

func ToString(v any) string

ToString converts a value to a string.

This should be the human-readable representation of the value.

If the value is a struct with a content type, it will use the content type's InstanceLabel method to convert it to a string.

time.Time, mail.Address, and error types are handled specially.

If the value is a slice or array, it will convert each element to a string and join them with ", ".

If all else fails, it will use fmt.Sprintf to convert the value to a string.

Types

type CanModelInfo added in v1.7.0

type CanModelInfo interface {
	// ModelMetaInfo returns the meta information for the model.
	//
	// This is used to store information about the model, such as relational information,
	ModelMetaInfo() map[string]any
}

CanMeta is an interface for defining a model that can have meta information.

This meta information is then stored on the ModelMeta interface.

type CanRelatedName added in v1.7.0

type CanRelatedName interface {
	Field
	RelatedName() string
}

type DefaultGetter

type DefaultGetter func(f Field, new_field_t_indirected reflect.Type, field_v reflect.Value) (interface{}, bool)

type Definer

type Definer interface {
	// Retrieves the field definitions for the model.
	FieldDefs() Definitions
}

Definer is the interface that wraps the FieldDefs method.

FieldDefs retrieves the field definitions for the model.

func DefinerList added in v1.6.7

func DefinerList[T Definer](list []T) []Definer

DefinerList converts a slice of []T where the underlying type is of type Definer to []Definer.

type Definitions

type Definitions interface {

	// Set sets the value of the field with the given name (or panics if not found).
	Set(name string, value interface{}) error

	// Retrieves the value of the field with the given name (or panics if not found).
	Get(name string) interface{}
	// contains filtered or unexported methods
}

Definitions is the interface that wraps the methods for a model's field definitions.

This is some sort of management- interface which allows for simpler and more uniform management of model fields.

func AutoDefinitions

func AutoDefinitions[T Definer](instance T, include ...any) Definitions

AutoDefinitions automatically generates definitions for a struct.

It does this by iterating over the fields of the struct and checking for the `attrs` tag. If the tag is present, it will parse the tag and generate the definition.

If the `include` parameter is provided, it will only generate definitions for the fields that are included.

type Field

type Field interface {
	FieldDefinition

	// Scan the value of the field into your model.
	//
	// This allows for reading the value easily from the database.
	sql.Scanner

	// Return the value of the field as a driver.Value.
	//
	// This value should be used for storing the field in a database.
	//
	// If the field is nil or the zero value, the default value should be returned.
	driver.Valuer

	// ToString returns a string representation of the value.
	//
	// This should be the human-readable version of the value, for example for a list display.
	ToString() string

	// Retrieves the value of the field.
	GetValue() interface{}

	// Retrieves the default value of the field.
	//
	// Fields should also check the main model instance for methods like `GetDefault<FieldName>` to retrieve the default value.
	GetDefault() interface{}

	// Sets the value of the field.
	//
	// If the field is not allowed to be edited, this method should panic.
	// If the field is not allowed to be null, this method should panic when trying to set the value to nil / a reflect.Invalid value.
	// If the field is not allowed to be blank, this method should panic when trying to set the value to a blank value if the field is not of types:
	SetValue(v interface{}, force bool) error
}

type FieldConfig

type FieldConfig struct {
	Null                 bool                                                // Whether the field allows null values
	Blank                bool                                                // Whether the field allows blank values
	ReadOnly             bool                                                // Whether the field is read-only
	Primary              bool                                                // Whether the field is a primary key
	NameOverride         string                                              // An optional override for the field name
	Label                string                                              // The label for the field
	HelpText             string                                              // The help text for the field
	Column               string                                              // The name of the column in the database
	MinLength            int64                                               // The minimum length of the field
	MaxLength            int64                                               // The maximum length of the field
	MinValue             float64                                             // The minimum value of the field
	MaxValue             float64                                             // The maximum value of the field
	Attributes           map[string]interface{}                              // The attributes for the field
	RelForeignKey        Relation                                            // The related object for the field (foreign key)
	RelManyToMany        Relation                                            // The related objects for the field (many to many, not implemented
	RelOneToOne          Relation                                            // The related object for the field (one to one, not implemented)
	RelForeignKeyReverse Relation                                            // The reverse foreign key for the field (not implemented)
	Default              any                                                 // The default value for the field (or a function that takes in the object type and returns the default value)
	Validators           []func(interface{}) error                           // Validators for the field
	FormField            func(opts ...func(fields.Field)) fields.Field       // The form field for the field
	WidgetAttrs          map[string]string                                   // The attributes for the widget
	FormWidget           func(FieldConfig) widgets.Widget                    // The form widget for the field
	Setter               func(Definer, interface{}) error                    // A custom setter for the field
	Getter               func(Definer) (interface{}, bool)                   // A custom getter for the field
	OnInit               func(Definer, *FieldDef, *FieldConfig) *FieldConfig // A function that is called when the field is initialized
}

FieldConfig is a configuration for a field.

This defines how a field should behave and how it should be displayed in a form.

type FieldDef

type FieldDef struct {
	// contains filtered or unexported fields
}

func NewField

func NewField(instance any, name string, conf *FieldConfig) *FieldDef

NewField creates a new field definition for the given instance.

This can then be used for managing the field in a more abstract way.

func (*FieldDef) AllowBlank

func (f *FieldDef) AllowBlank() bool

func (*FieldDef) AllowEdit

func (f *FieldDef) AllowEdit() bool

func (*FieldDef) AllowNull

func (f *FieldDef) AllowNull() bool

func (*FieldDef) Attrs added in v1.7.0

func (f *FieldDef) Attrs() map[string]interface{}

func (*FieldDef) ColumnName added in v1.7.0

func (f *FieldDef) ColumnName() string

func (*FieldDef) FormField

func (f *FieldDef) FormField() fields.Field

func (*FieldDef) GetDefault

func (f *FieldDef) GetDefault() interface{}

func (*FieldDef) GetValue

func (f *FieldDef) GetValue() interface{}

func (*FieldDef) HelpText

func (f *FieldDef) HelpText() string

func (*FieldDef) Instance

func (f *FieldDef) Instance() Definer

func (*FieldDef) IsPrimary

func (f *FieldDef) IsPrimary() bool

func (*FieldDef) Label

func (f *FieldDef) Label() string

func (*FieldDef) Name

func (f *FieldDef) Name() string

func (*FieldDef) Rel added in v1.6.7

func (f *FieldDef) Rel() Relation

func (*FieldDef) Scan added in v1.7.0

func (f *FieldDef) Scan(value any) error

func (*FieldDef) SetValue

func (f *FieldDef) SetValue(v interface{}, force bool) error

func (*FieldDef) Tag added in v1.7.0

func (f *FieldDef) Tag(name string) string

func (*FieldDef) ToString

func (f *FieldDef) ToString() string

func (*FieldDef) Type added in v1.7.0

func (f *FieldDef) Type() reflect.Type

func (*FieldDef) Validate

func (f *FieldDef) Validate() error

func (*FieldDef) Value added in v1.7.0

func (f *FieldDef) Value() (driver.Value, error)

Returns the value of the field as a driver.Value.

This value should be used for storing the field in a database.

If the field is nil or the zero value, the default value is returned.

type FieldDefinition added in v1.7.0

type FieldDefinition interface {
	Name() string
	Labeler
	Helper

	// Tag retrieves the tag value for the field with the given name.
	Tag(name string) string

	// Retrieves the underlying model instance.
	Instance() Definer

	// ColumnName retrieves the name of the column in the database.
	//
	// This can be used to generate the SQL for the field.
	ColumnName() string

	// Type returns the reflect.Type of the field.
	Type() reflect.Type

	// Attrs returns any extra attributes for the field, these can be used for multiple purposes.
	//
	// Additional info can be stored here, for example - if the field has a min / max length.
	Attrs() map[string]any

	// Rel etrieves the related model instance for a foreign key field.
	//
	// This could be used to generate the SQL for the field.
	Rel() Relation

	// Reports whether the field is the primary field.
	//
	// A model can technically have multiple primary fields, but this is not recommended.
	//
	// When for example, calling `Primary()` on the `Definitions` interface - only one will be returned.
	IsPrimary() bool

	// Reports whether the field is allowed to be null.
	//
	// If not, the field should panic when trying to set the value to nil / a reflect.Invalid value.
	AllowNull() bool

	// Reports whether the field is allowed to be blank.
	//
	// If not, the field should panic when trying to set the value to a blank value if the field is not of types:
	// bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, complex64, complex128.
	//
	// this means that for example, a string field should panic when trying to set the value to an empty string.
	AllowBlank() bool

	// Reports whether the field is allowed to be edited.
	//
	// If not, the field should panic when trying to set the value, unless the force parameter passed to the `SetValue` method is true.
	AllowEdit() bool
	// Retrieves the form field for the field.
	//
	// This is used to generate forms for the field.
	FormField() fields.Field

	// Validates the field's value.
	Validate() error
}

type FormFieldGetter

type FormFieldGetter func(f Field, new_field_t_indirected reflect.Type, field_v reflect.Value, opts ...func(fields.Field)) (fields.Field, bool)

type Helper

type Helper interface {
	// HelpText returns a description of the field.
	//
	// This is displayed to the user in for example, forms.
	HelpText() string
}

type Labeler

type Labeler interface {
	// Label returns the human-readable name of the field.
	//
	// This is the name that is displayed to the user in for example, forms and column headers.
	Label() string
}

type ModelMeta added in v1.7.0

type ModelMeta interface {
	// Model returns the model for this meta
	Model() Definer

	// Forward returns the forward relations for this model
	// which belong to the field with the given name.
	Forward(relField string) (Relation, bool)

	// Reverse returns the reverse relations for this model
	// which belong to the field with the given name.
	Reverse(relField string) (Relation, bool)

	// ForwardMap returns a copy the forward relations map for this model
	ForwardMap() *orderedmap.OrderedMap[string, Relation]

	// ReverseMap returns a copy of the reverse relations map for this model
	ReverseMap() *orderedmap.OrderedMap[string, Relation]

	// Storage returns a value stored on the model meta.
	//
	// This is used to store values that are not part of the model itself,
	// but are needed for the model or possible third party libraries to function.
	//
	// Values can be stored on the model meta using the `attrs.StoreOnMeta` helper function.
	//
	// A model can also implement the `CanModelInfo` interface to store values on the model meta.
	Storage(key string) (any, bool)

	// Definitions returns the field definitions for the model.
	//
	// This is used to retrieve meta information about fields, such as their type,
	// and other information that is not part of the model itself.
	Definitions() StaticDefinitions
}

ModelMeta represents the meta information for a model.

This is used to store information about the model, such as relational information, and other information that is not part of the model itself.

Models which implement the `Definer` interface

func GetModelMeta added in v1.7.0

func GetModelMeta(model Definer) ModelMeta

type ObjectDefinitions

type ObjectDefinitions struct {
	Object       Definer
	PrimaryField string
	Table        string
	ObjectFields *orderedmap.OrderedMap[string, Field]
}

func Define

func Define(d Definer, fieldDefinitions ...Field) *ObjectDefinitions

Define creates a new object definitions.

This can then be returned by the FieldDefs method of a model to make it comply with the Definer interface.

func (*ObjectDefinitions) Field

func (d *ObjectDefinitions) Field(name string) (f Field, ok bool)

func (*ObjectDefinitions) Fields

func (d *ObjectDefinitions) Fields() []Field

func (*ObjectDefinitions) ForceSet

func (d *ObjectDefinitions) ForceSet(name string, value interface{}) error

func (*ObjectDefinitions) Get

func (d *ObjectDefinitions) Get(name string) interface{}

func (*ObjectDefinitions) Instance added in v1.6.9

func (d *ObjectDefinitions) Instance() Definer

func (*ObjectDefinitions) Len

func (d *ObjectDefinitions) Len() int

func (*ObjectDefinitions) Primary

func (d *ObjectDefinitions) Primary() Field

func (*ObjectDefinitions) Set

func (d *ObjectDefinitions) Set(name string, value interface{}) error

func (*ObjectDefinitions) TableName added in v1.7.0

func (d *ObjectDefinitions) TableName() string

func (*ObjectDefinitions) WithTableName added in v1.7.0

func (d *ObjectDefinitions) WithTableName(name string) *ObjectDefinitions

type PathMetaChain added in v1.7.0

type PathMetaChain []*pathMeta

func WalkMetaFields added in v1.7.0

func WalkMetaFields(m Definer, path string) (PathMetaChain, error)

func (PathMetaChain) First added in v1.7.0

func (c PathMetaChain) First() *pathMeta

func (PathMetaChain) Last added in v1.7.0

func (c PathMetaChain) Last() *pathMeta

type Relation added in v1.7.0

type Relation interface {
	RelationTarget

	Type() RelationType

	// A through model for the relationship.
	//
	// This can be nil, but does not have to be.
	// It can support a one to one relationship with or without a through model,
	// or a many to many relationship with a through model.
	Through() Through
}

Relation is an interface for defining a relation between two models.

This provides a very abstract way of defining relations between models, which can be used to define relations in a more generic way.

func GetRelationMeta added in v1.7.0

func GetRelationMeta(m Definer, name string) (Relation, bool)

func Relate added in v1.7.0

func Relate(target Definer, targetField string, through Through) Relation

Relate creates a new relation between two models.

This can be used to define all kinds of relations between models, such as one to one, one to many, many to many, many to one.

The target model is the model that is being related to. The target field is the field in the target model that is being related to, it can be an empty string, in which case the primary field of the target model is used.

The through model is the model that is used to link the two models together, it can be nil if not needed.

func ReverseRelation added in v1.7.0

func ReverseRelation(
	fromModel Definer,
	fromField Field,
	forward Relation,
) Relation

type RelationTarget added in v1.7.0

type RelationTarget interface {
	// From represents the source model for the relationship.
	//
	// If this is nil then the current interface value is the source model.
	From() RelationTarget

	// The target model for the relationship.
	Model() Definer

	// Field retrieves the field in the target model for the relationship.
	//
	// This can be nil, in such cases the relationship should use the primary field of the target model.
	//
	// If a through model is used, the target field should still target the actual target model,
	// the through model should then use this field to link to the target model.
	Field() FieldDefinition
}

RelationTarget is an interface for defining a relation target.

This is the target model for the relation, which can be used to define the relation in a more generic way.

type RelationType added in v1.7.0

type RelationType int
const (

	// ManyToOne is a many to one relationship, also known as a foreign key relationship.
	//
	// This means that the target model can have multiple instances of the source model,
	// but the source model can only have one instance of the target model.
	// This is the default type for a relation.
	RelManyToOne RelationType = iota

	// OneToOne is a one to one relationship.
	//
	// This means that the target model can only have one instance of the source model.
	// This is the default type for a relation.
	RelOneToOne

	// ManyToMany is a many to many relationship.
	//
	// This means that the target model can have multiple instances of the source model,
	// and the source model can have multiple instances of the target model.
	RelManyToMany

	// OneToMany is a one to many relationship, also known as a reverse foreign key relationship.
	//
	// This means that the target model can only have one instance of the source model,
	// but the source model can have multiple instances of the target model.
	RelOneToMany
)

func (RelationType) MarshalJSON added in v1.7.0

func (r RelationType) MarshalJSON() ([]byte, error)

func (RelationType) String added in v1.7.0

func (r RelationType) String() string

func (*RelationType) UnmarshalJSON added in v1.7.0

func (r *RelationType) UnmarshalJSON(data []byte) error

type Scanner

type Scanner interface {
	// ScanAttribute scans the value of the attribute.
	//
	// This is used to set the value of the field from a raw value.
	ScanAttribute(src any) error
}

type StaticDefinitions added in v1.7.0

type StaticDefinitions = staticDefinitions[FieldDefinition]

type Through added in v1.7.0

type Through interface {
	// The through model itself.
	Model() Definer

	// The source field for the relation - this is a field in the through model linking to the source model.
	SourceField() string

	// The target field for the relation - this is a field in the through model linking to the target model.
	TargetField() string
}

Through is an interface for defining a relation between two models.

This provides a very abstract way of defining relations between models, which can be used to define one to one relations or many to many relations.

type ThroughModel added in v1.7.0

type ThroughModel struct {
	This   Definer
	Source string
	Target string
}

func (*ThroughModel) Model added in v1.7.0

func (t *ThroughModel) Model() Definer

Model returns the through model itself.

func (*ThroughModel) SourceField added in v1.7.0

func (t *ThroughModel) SourceField() string

SourceField returns the source field for the relation - this is the field in the source model.

func (*ThroughModel) TargetField added in v1.7.0

func (t *ThroughModel) TargetField() string

TargetField returns the target field for the relation - this is the field in the target model, or in the next through model.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL