How to use AddMethod method of model Package

Best Mock code snippet using model.AddMethod

base_model.go

Source:base_model.go Github

copy

Full Screen

1// Copyright 2016 NDP Systèmes. All Rights Reserved.2//3// Licensed under the Apache License, Version 2.0 (the "License");4// you may not use this file except in compliance with the License.5// You may obtain a copy of the License at6//7//      http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// distributed under the License is distributed on an "AS IS" BASIS,11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12// See the License for the specific language governing permissions and13// limitations under the License.14package models15import (16	"database/sql"17	"fmt"18	"reflect"19	"strings"20	"github.com/gleke/hexya/src/i18n"21	"github.com/gleke/hexya/src/models/fieldtype"22	"github.com/gleke/hexya/src/models/operator"23	"github.com/gleke/hexya/src/models/types"24	"github.com/gleke/hexya/src/models/types/dates"25	"github.com/gleke/hexya/src/tools/nbutils"26	"github.com/google/uuid"27)28const (29	// TransientModel means that the records of this model will be automatically30	// removed periodically. Transient models are mainly used for wizards.31	TransientModel Option = 1 << iota32	// MixinModel means that this model will not be accessible like a regular model33	// but is meant to be mixed in other models.34	MixinModel35	// Many2ManyLinkModel is a model that abstracts the link36	// table of a many2many relationship37	Many2ManyLinkModel38	// ContextsModel is a model for holding fields values that depend on contexts39	ContextsModel40	// ManualModel is a model whose table is not automatically generated in the41	// database. Such models include SQL views and materialized SQL views.42	ManualModel43	// SystemModel is a model that is used internally by the Hexya Framework44	SystemModel45)46//  declareCommonMixin creates the common mixin that is needed for all models47func declareCommonMixin() {48	commonMixin := NewMixinModel("CommonMixin")49	commonMixin.addMethod("New", commonMixinNew)50	commonMixin.addMethod("Create", commonMixinCreate)51	commonMixin.addMethod("Read", commonMixinRead)52	commonMixin.addMethod("Load", commonMixinLoad)53	commonMixin.addMethod("Write", commonMixinWrite)54	commonMixin.addMethod("Unlink", commonMixinUnlink)55	commonMixin.addMethod("CopyData", commonMixinCopyData)56	commonMixin.addMethod("Copy", commonMixinCopy)57	commonMixin.addMethod("NameGet", commonMixinNameGet)58	commonMixin.addMethod("SearchByName", commonMixinSearchByName)59	commonMixin.addMethod("FieldsGet", commonMixinFieldsGet)60	commonMixin.addMethod("FieldGet", commonMixinFieldGet)61	commonMixin.addMethod("DefaultGet", commonMixinDefaultGet)62	commonMixin.addMethod("CheckRecursion", commonMixinCheckRecursion)63	commonMixin.addMethod("Onchange", commonMixinOnChange)64	commonMixin.addMethod("Search", commonMixinSearch)65	commonMixin.addMethod("Browse", commonMixinBrowse)66	commonMixin.addMethod("BrowseOne", commonMixinBrowseOne)67	commonMixin.addMethod("SearchCount", commonMixinSearchCount)68	commonMixin.addMethod("Fetch", commonMixinFetch)69	commonMixin.addMethod("SearchAll", commonMixinSearchAll)70	commonMixin.addMethod("GroupBy", commonMixinGroupBy)71	commonMixin.addMethod("Limit", commonMixinLimit)72	commonMixin.addMethod("Offset", commonMixinOffset)73	commonMixin.addMethod("OrderBy", commonMixinOrderBy)74	commonMixin.addMethod("Union", commonMixinUnion)75	commonMixin.addMethod("Subtract", commonMixinSubtract)76	commonMixin.addMethod("Intersect", commonMixinIntersect)77	commonMixin.addMethod("CartesianProduct", commonMixinCartesianProduct)78	commonMixin.addMethod("Equals", commonMixinEquals)79	commonMixin.addMethod("Sorted", commonMixinSorted)80	commonMixin.addMethod("SortedDefault", commonMixinSortedDefault)81	commonMixin.addMethod("SortedByField", commonMixinSortedByField)82	commonMixin.addMethod("Filtered", commonMixinFiltered)83	commonMixin.addMethod("GetRecord", commonMixinGetRecord)84	commonMixin.addMethod("CheckExecutionPermission", commonMixinCheckExecutionPermission)85	commonMixin.addMethod("SQLFromCondition", commonMixinSQLFromCondition)86	commonMixin.addMethod("WithEnv", commonMixinWithEnv)87	commonMixin.addMethod("WithContext", commonMixinWithContext)88	commonMixin.addMethod("WithNewContext", commonMixinWithNewContext)89	commonMixin.addMethod("Sudo", commonMixinSudo)90}91// New creates a memory only record from the given data.92// Such a record has a negative ID and cannot be loaded from database.93//94// Note that New does not work with embedded records.95func commonMixinNew(rc *RecordCollection, data RecordData) *RecordCollection {96	return rc.new(data)97}98// Create inserts a record in the database from the given data.99// Returns the created RecordCollection.100func commonMixinCreate(rc *RecordCollection, data RecordData) *RecordCollection {101	return rc.create(data)102}103// Read reads the database and returns a slice of FieldMap of the given model.104func commonMixinRead(rc *RecordCollection, fields FieldNames) []RecordData {105	var res []RecordData106	// Check if we have id in fields, and add it otherwise107	fields = addIDIfNotPresent(fields)108	// Do the actual reading109	for _, rec := range rc.Records() {110		fData := NewModelData(rc.model)111		for _, fName := range fields {112			fData.Underlying().Set(fName, rec.Get(fName))113		}114		res = append(res, fData)115	}116	return res117}118// Load looks up cache for fields of the RecordCollection and119// query database for missing values.120// fields are the fields to retrieve in the expression format,121// i.e. "User.Profile.Age" or "user_id.profile_id.age".122// If no fields are given, all DB columns of the RecordCollection's123// model are retrieved.124func commonMixinLoad(rc *RecordCollection, fields ...FieldName) *RecordCollection {125	return rc.Load(fields...)126}127// Write is the base implementation of the 'Write' method which updates128// records in the database with the given data.129// Data can be either a struct pointer or a FieldMap.`,130func commonMixinWrite(rc *RecordCollection, data RecordData) bool {131	return rc.update(data)132}133// Unlink deletes the given records in the database.134func commonMixinUnlink(rc *RecordCollection) int64 {135	return rc.unlink()136}137// CopyData copies given record's data with all its fields values.138//139// overrides contains field values to override in the original values of the copied record.140func commonMixinCopyData(rc *RecordCollection, overrides RecordData) *ModelData {141	rc.EnsureOne()142	// Handle case when overrides is nil143	oVal := reflect.ValueOf(overrides)144	if !oVal.IsValid() || (oVal.Kind() != reflect.Struct && oVal.IsNil()) {145		overrides = NewModelDataFromRS(rc)146	}147	// Create the RecordData148	res := NewModelDataFromRS(rc)149	for _, fi := range rc.model.fields.registryByName {150		fName := rc.model.FieldName(fi.name)151		if overrides.Underlying().Has(fName) {152			// Overrides are applied below153			continue154		}155		if fi.noCopy || fi.isComputedField() {156			continue157		}158		switch fi.fieldType {159		case fieldtype.One2One:160			// One2one related records must be copied to avoid duplicate keys on FK161			res = res.Create(fName, rc.Get(fName).(RecordSet).Collection().Call("CopyData", nil).(RecordData).Underlying())162		case fieldtype.One2Many, fieldtype.Rev2One:163			for _, rec := range rc.Get(fName).(RecordSet).Collection().Records() {164				res = res.Create(fName, rec.Call("CopyData", nil).(RecordData).Underlying().Unset(fi.relatedModel.FieldName(fi.reverseFK)))165			}166		default:167			res.Set(fName, rc.Get(fName))168		}169	}170	// Apply overrides171	res.RemovePK()172	res.MergeWith(overrides.Underlying())173	return res174}175// Copy duplicates the given records.176//177// overrides contains field values to override in the original values of the copied record.`,178func commonMixinCopy(rc *RecordCollection, overrides RecordData) *RecordCollection {179	rc.EnsureOne()180	data := rc.Call("CopyData", overrides).(RecordData).Underlying()181	newRs := rc.Call("Create", data).(RecordSet).Collection()182	return newRs183}184// NameGet retrieves the human readable name of this record.`,185func commonMixinNameGet(rc *RecordCollection) string {186	if _, nameExists := rc.model.fields.Get("Name"); nameExists {187		switch name := rc.Get(rc.model.FieldName("Name")).(type) {188		case string:189			return name190		case fmt.Stringer:191			return name.String()192		default:193			log.Panic("Name field is neither a string nor a fmt.Stringer", "model", rc.model)194		}195	}196	return rc.String()197}198// SearchByName searches for records that have a display name matching the given199// "name" pattern when compared with the given "op" operator, while also200// matching the optional search condition ("additionalCond").201//202// This is used for example to provide suggestions based on a partial203// value for a relational field. Sometimes be seen as the inverse204// function of NameGet but it is not guaranteed to be.205func commonMixinSearchByName(rc *RecordCollection, name string, op operator.Operator, additionalCond Conditioner, limit int) *RecordCollection {206	if op == "" {207		op = operator.IContains208	}209	cond := rc.Model().Field(rc.model.FieldName("Name")).AddOperator(op, name)210	if !additionalCond.Underlying().IsEmpty() {211		cond = cond.AndCond(additionalCond.Underlying())212	}213	return rc.Model().Search(rc.Env(), cond).Limit(limit)214}215// FieldsGet returns the definition of each field.216// The embedded fields are included.217// The string, help, and selection (if present) attributes are translated.218//219// The result map is indexed by the fields JSON names.220func commonMixinFieldsGet(rc *RecordCollection, args FieldsGetArgs) map[string]*FieldInfo {221	// Get the field informations222	res := rc.model.FieldsGet(args.Fields...)223	// Translate attributes when required224	lang := rc.Env().Context().GetString("lang")225	for fName, fInfo := range res {226		res[fName].Help = i18n.Registry.TranslateFieldHelp(lang, rc.model.name, fInfo.Name, fInfo.Help)227		res[fName].String = i18n.Registry.TranslateFieldDescription(lang, rc.model.name, fInfo.Name, fInfo.String)228		res[fName].Selection = i18n.Registry.TranslateFieldSelection(lang, rc.model.name, fInfo.Name, fInfo.Selection)229	}230	return res231}232// FieldGet returns the definition of the given field.233// The string, help, and selection (if present) attributes are translated.234func commonMixinFieldGet(rc *RecordCollection, field FieldName) *FieldInfo {235	args := FieldsGetArgs{236		Fields: []FieldName{field},237	}238	return rc.Call("FieldsGet", args).(map[string]*FieldInfo)[field.JSON()]239}240// DefaultGet returns a Params map with the default values for the model.241func commonMixinDefaultGet(rc *RecordCollection) *ModelData {242	res := rc.getDefaults(rc.Env().Context().GetBool("hexya_ignore_computed_defaults"))243	return res244}245// CheckRecursion verifies that there is no loop in a hierarchical structure of records,246// by following the parent relationship using the 'Parent' field until a loop is detected or247// until a top-level record is found.248//249// It returns true if no loop was found, false otherwise`,250func commonMixinCheckRecursion(rc *RecordCollection) bool {251	if _, exists := rc.model.fields.Get("Parent"); !exists {252		// No Parent field in model, so no loop253		return true254	}255	if rc.hasNegIds {256		// We have a negative id, so we can't have a loop257		return true258	}259	// We use direct SQL query to bypass access control260	query := fmt.Sprintf(`SELECT parent_id FROM %s WHERE id = ?`, adapters[db.DriverName()].quoteTableName(rc.model.tableName))261	rc.Load(rc.model.FieldName("Parent"))262	for _, record := range rc.Records() {263		currentID := record.ids[0]264		for {265			var parentID sql.NullInt64266			rc.env.cr.Get(&parentID, query, currentID)267			if !parentID.Valid {268				break269			}270			currentID = parentID.Int64271			if currentID == record.ids[0] {272				return false273			}274		}275	}276	return true277}278// Onchange returns the values that must be modified according to each field's Onchange279// method in the pseudo-record given as params.Values`,280func commonMixinOnChange(rc *RecordCollection, params OnchangeParams) OnchangeResult {281	var retValues *ModelData282	var warnings []string283	filters := make(map[FieldName]Conditioner)284	err := SimulateInNewEnvironment(rc.Env().Uid(), func(env Environment) {285		values := params.Values.Underlying().FieldMap286		data := NewModelDataFromRS(rc.WithEnv(env), values)287		if rc.IsNotEmpty() {288			data.Set(ID, rc.ids[0])289		}290		retValues = NewModelDataFromRS(rc.WithEnv(env))291		var rs *RecordCollection292		if id, _ := nbutils.CastToInteger(data.Get(ID)); id != 0 {293			rs = rc.WithEnv(env).withIds([]int64{id})294			rs = rs.WithContext("hexya_onchange_origin", rs.First().Wrap())295			rs.WithContext("hexya_force_compute_write", true).update(data)296		} else {297			rs = rc.WithEnv(env).WithContext("hexya_force_compute_write", true).create(data)298		}299		// Set inverse fields300		for field := range values {301			fName := rs.model.FieldName(field)302			fi := rs.model.getRelatedFieldInfo(fName)303			if fi.inverse != "" {304				fVal := data.Get(fName)305				rs.Call(fi.inverse, fVal)306			}307		}308		todo := params.Fields309		done := make(map[string]bool)310		// Apply onchanges or compute311		for len(todo) > 0 {312			field := todo[0]313			todo = todo[1:]314			if done[field.JSON()] {315				continue316			}317			done[field.JSON()] = true318			if params.Onchange[field.Name()] == "" && params.Onchange[field.JSON()] == "" {319				continue320			}321			fi := rs.model.getRelatedFieldInfo(field)322			fnct := fi.onChange323			if fnct == "" {324				fnct = fi.compute325			}326			rrs := rs327			toks := splitFieldNames(field, ExprSep)328			if len(toks) > 1 {329				rrs = rs.Get(joinFieldNames(toks[:len(toks)-1], ExprSep)).(RecordSet).Collection()330			}331			// Values332			if fnct != "" {333				vals := rrs.Call(fnct).(RecordData)334				for _, f := range vals.Underlying().FieldNames() {335					if !done[f.JSON()] {336						todo = append(todo, f)337					}338				}339				rrs.WithContext("hexya_force_compute_write", true).Call("Write", vals)340			}341			// Warning342			if fi.onChangeWarning != "" {343				w := rrs.Call(fi.onChangeWarning).(string)344				if w != "" {345					warnings = append(warnings, w)346				}347			}348			// Filters349			if fi.onChangeFilters != "" {350				ff := rrs.Call(fi.onChangeFilters).(map[FieldName]Conditioner)351				for k, v := range ff {352					filters[k] = v353				}354			}355		}356		// Collect modified values357		for field, val := range values {358			fName := rs.model.FieldName(field)359			if fName.JSON() == "__last_update" {360				continue361			}362			fi := rs.Collection().Model().getRelatedFieldInfo(fName)363			newVal := rs.Get(fName)364			switch {365			case fi.fieldType.IsRelationType():366				v := rs.convertToRecordSet(val, fi.relatedModelName)367				nv := rs.convertToRecordSet(newVal, fi.relatedModelName)368				if !v.Equals(nv) {369					retValues.Set(fName, newVal)370				}371			default:372				if val != newVal {373					retValues.Set(fName, newVal)374				}375			}376		}377	})378	if err != nil {379		panic(err)380	}381	retValues.Unset(ID)382	return OnchangeResult{383		Value:   retValues,384		Warning: strings.Join(warnings, "\n\n"),385		Filters: filters,386	}387}388// Search returns a new RecordSet filtering on the current one with the389// additional given Condition.390func commonMixinSearch(rc *RecordCollection, cond Conditioner) *RecordCollection {391	return rc.Search(cond.Underlying())392}393// Browse returns a new RecordSet with only the records with the given ids.394// Note that this function is just a shorcut for Search on a list of ids.395func commonMixinBrowse(rc *RecordCollection, ids []int64) *RecordCollection {396	return rc.Call("Search", rc.Model().Field(ID).In(ids)).(RecordSet).Collection()397}398// BrowseOne returns a new RecordSet with only the record with the given id.399// Note that this function is just a shorcut for Search on a given id.400func commonMixinBrowseOne(rc *RecordCollection, id int64) *RecordCollection {401	return rc.Call("Search", rc.Model().Field(ID).Equals(id)).(RecordSet).Collection()402}403// SearchCount fetch from the database the number of records that match the RecordSet conditions.404func commonMixinSearchCount(rc *RecordCollection) int {405	return rc.SearchCount()406}407// Fetch query the database with the current filter and returns a RecordSet408// with the queries ids.409//410// Fetch is lazy and only return ids. Use Load() instead if you want to fetch all fields.411func commonMixinFetch(rc *RecordCollection) *RecordCollection {412	return rc.Fetch()413}414// SearchAll returns a RecordSet with all items of the table, regardless of the415// current RecordSet query. It is mainly meant to be used on an empty RecordSet.416func commonMixinSearchAll(rc *RecordCollection) *RecordCollection {417	return rc.SearchAll()418}419// GroupBy returns a new RecordSet grouped with the given GROUP BY expressions.420func commonMixinGroupBy(rc *RecordCollection, exprs ...FieldName) *RecordCollection {421	return rc.GroupBy(exprs...)422}423// Limit returns a new RecordSet with only the first 'limit' records.424func commonMixinLimit(rc *RecordCollection, limit int) *RecordCollection {425	return rc.Limit(limit)426}427// Offset returns a new RecordSet with only the records starting at offset428func commonMixinOffset(rc *RecordCollection, offset int) *RecordCollection {429	return rc.Offset(offset)430}431// OrderBy returns a new RecordSet ordered by the given ORDER BY expressions.432// Each expression contains a field name and optionally one of "asc" or "desc", such as:433//434// rs.OrderBy("Company", "Name desc")435func commonMixinOrderBy(rc *RecordCollection, exprs ...string) *RecordCollection {436	return rc.OrderBy(exprs...)437}438// Union returns a new RecordSet that is the union of this RecordSet and the given439// "other" RecordSet. The result is guaranteed to be a set of unique records.440func commonMixinUnion(rc *RecordCollection, other RecordSet) *RecordCollection {441	return rc.Union(other)442}443// Subtract returns a RecordSet with the Records that are in this444// RecordCollection but not in the given 'other' one.445// The result is guaranteed to be a set of unique records.446func commonMixinSubtract(rc *RecordCollection, other RecordSet) *RecordCollection {447	return rc.Subtract(other)448}449// Intersect returns a new RecordCollection with only the records that are both450// in this RecordCollection and in the other RecordSet.451func commonMixinIntersect(rc *RecordCollection, other RecordSet) *RecordCollection {452	return rc.Intersect(other)453}454// CartesianProduct returns the cartesian product of this RecordCollection with others.455func commonMixinCartesianProduct(rc *RecordCollection, other ...RecordSet) []*RecordCollection {456	return rc.CartesianProduct(other...)457}458// Equals returns true if this RecordSet is the same as other459// i.e. they are of the same model and have the same ids460func commonMixinEquals(rc *RecordCollection, other RecordSet) bool {461	return rc.Equals(other)462}463// Sorted returns a new RecordCollection sorted according to the given less function.464//465// The less function should return true if rs1 < rs2`,466func commonMixinSorted(rc *RecordCollection, less func(rs1 RecordSet, rs2 RecordSet) bool) *RecordCollection {467	return rc.Sorted(less)468}469// SortedDefault returns a new record set with the same records as rc but sorted according470// to the default order of this model471func commonMixinSortedDefault(rc *RecordCollection) *RecordCollection {472	return rc.SortedDefault()473}474// SortedByField returns a new record set with the same records as rc but sorted by the given field.475// If reverse is true, the sort is done in reversed order476func commonMixinSortedByField(rc *RecordCollection, namer FieldName, reverse bool) *RecordCollection {477	return rc.SortedByField(namer, reverse)478}479// Filtered returns a new record set with only the elements of this record set480// for which test is true.481//482// Note that if this record set is not fully loaded, this function will call the database483// to load the fields before doing the filtering. In this case, it might be more efficient484// to search the database directly with the filter condition.485func commonMixinFiltered(rc *RecordCollection, test func(rs RecordSet) bool) *RecordCollection {486	return rc.Filtered(test)487}488// GetRecord returns the Recordset with the given externalID. It panics if the externalID does not exist.489func commonMixinGetRecord(rc *RecordCollection, externalID string) *RecordCollection {490	return rc.GetRecord(externalID)491}492// CheckExecutionPermission panics if the current user is not allowed to execute the given method.493//494// If dontPanic is false, this function will panic, otherwise it returns true495// if the user has the execution permission and false otherwise.496func commonMixinCheckExecutionPermission(rc *RecordCollection, method *Method, dontPanic ...bool) bool {497	return rc.CheckExecutionPermission(method, dontPanic...)498}499// SQLFromCondition returns the WHERE clause sql and arguments corresponding to500// the given condition.`,501func commonMixinSQLFromCondition(rc *RecordCollection, c *Condition) (string, SQLParams) {502	return rc.SQLFromCondition(c)503}504// WithEnv returns a copy of the current RecordSet with the given Environment.505func commonMixinWithEnv(rc *RecordCollection, env Environment) *RecordCollection {506	return rc.WithEnv(env)507}508// WithContext returns a copy of the current RecordSet with509// its context extended by the given key and value.510func commonMixinWithContext(rc *RecordCollection, key string, value interface{}) *RecordCollection {511	return rc.WithContext(key, value)512}513// WithNewContext returns a copy of the current RecordSet with its context514// replaced by the given one.515func commonMixinWithNewContext(rc *RecordCollection, context *types.Context) *RecordCollection {516	return rc.WithNewContext(context)517}518// Sudo returns a new RecordSet with the given userID519// or the superuser ID if not specified520func commonMixinSudo(rc *RecordCollection, userID ...int64) *RecordCollection {521	return rc.Sudo(userID...)522}523// declareBaseMixin creates the mixin that implements all the necessary base methods of a model524func declareBaseMixin() {525	baseMixin := NewMixinModel("BaseMixin")526	baseMixin.InheritModel(Registry.MustGet("CommonMixin"))527	baseMixin.addMethod("ComputeLastUpdate", baseMixinComputeLastUpdate)528	baseMixin.addMethod("ComputeDisplayName", baseMixinComputeDisplayName)529	baseMixin.fields.add(&Field{530		model:       baseMixin,531		name:        "CreateDate",532		description: "Created On",533		json:        "create_date",534		fieldType:   fieldtype.DateTime,535		structField: reflect.StructField{Type: reflect.TypeOf(dates.DateTime{})},536		noCopy:      true,537	})538	baseMixin.fields.add(&Field{539		model:       baseMixin,540		name:        "CreateUID",541		description: "Created By",542		json:        "create_uid",543		fieldType:   fieldtype.Integer,544		structField: reflect.StructField{Type: reflect.TypeOf(int64(0))},545		noCopy:      true,546		defaultFunc: func(env Environment) interface{} {547			return env.uid548		},549	})550	baseMixin.fields.add(&Field{551		model:       baseMixin,552		name:        "WriteDate",553		description: "Updated On",554		json:        "write_date",555		fieldType:   fieldtype.DateTime,556		structField: reflect.StructField{Type: reflect.TypeOf(dates.DateTime{})},557		noCopy:      true,558	})559	baseMixin.fields.add(&Field{560		model:       baseMixin,561		name:        "WriteUID",562		description: "UpdatedBy",563		json:        "write_uid",564		fieldType:   fieldtype.Integer,565		structField: reflect.StructField{Type: reflect.TypeOf(int64(0))},566		noCopy:      true,567		defaultFunc: func(env Environment) interface{} {568			return env.uid569		},570	})571	baseMixin.fields.add(&Field{572		model:       baseMixin,573		name:        "LastUpdate",574		description: "Last Updated On",575		json:        "__last_update",576		fieldType:   fieldtype.DateTime,577		structField: reflect.StructField{Type: reflect.TypeOf(dates.DateTime{})},578		compute:     "ComputeLastUpdate",579		depends:     []string{"WriteDate", "CreateDate"},580	})581	baseMixin.fields.add(&Field{582		model:       baseMixin,583		name:        "DisplayName",584		description: "Display Name",585		json:        "display_name",586		fieldType:   fieldtype.Char,587		structField: reflect.StructField{Type: reflect.TypeOf("")},588		compute:     "ComputeDisplayName",589		depends:     []string{""},590	})591}592// ComputeLastUpdate returns the last datetime at which the record has been updated.593func baseMixinComputeLastUpdate(rc *RecordCollection) *ModelData {594	res := NewModelData(rc.model)595	if !rc.Get(rc.model.FieldName("WriteDate")).(dates.DateTime).IsZero() {596		res.Set(rc.model.FieldName("LastUpdate"), rc.Get(rc.model.FieldName("WriteDate")).(dates.DateTime))597		return res598	}599	if !rc.Get(rc.model.FieldName("CreateDate")).(dates.DateTime).IsZero() {600		res.Set(rc.model.FieldName("LastUpdate"), rc.Get(rc.model.FieldName("CreateDate")).(dates.DateTime))601		return res602	}603	res.Set(rc.model.FieldName("LastUpdate"), dates.Now())604	return res605}606// ComputeDisplayName updates the DisplayName field with the result of NameGet607func baseMixinComputeDisplayName(rc *RecordCollection) *ModelData {608	res := NewModelData(rc.model).Set(rc.model.FieldName("DisplayName"), rc.Call("NameGet"))609	return res610}611func declareModelMixin() {612	modelMixin := NewMixinModel("ModelMixin")613	modelMixin.InheritModel(Registry.MustGet("BaseMixin"))614	modelMixin.fields.add(&Field{615		model:       modelMixin,616		name:        "HexyaExternalID",617		description: "Record External ID",618		json:        "hexya_external_id",619		fieldType:   fieldtype.Char,620		structField: reflect.StructField{Type: reflect.TypeOf("")},621		noCopy:      true,622		unique:      true,623		index:       true,624		required:    true,625		readOnly:    true,626		defaultFunc: func(env Environment) interface{} {627			return uuid.New().String()628		},629	})630	modelMixin.fields.add(&Field{631		model:       modelMixin,632		name:        "HexyaVersion",633		description: "Data Version",634		json:        "hexya_version",635		fieldType:   fieldtype.Integer,636		structField: reflect.StructField{Type: reflect.TypeOf(0)},637		noCopy:      true,638		defaultFunc: DefaultValue(0),639	})640}641// ConvertLimitToInt converts the given limit as interface{} to an int642func ConvertLimitToInt(limit interface{}) int {643	if l, ok := limit.(bool); ok && !l {644		return -1645	}646	val, err := nbutils.CastToInteger(limit)647	if err != nil {648		return 80649	}650	return int(val)651}652// FieldInfo is the exportable field information struct653type FieldInfo struct {654	ChangeDefault    bool                                  `json:"change_default"`655	Help             string                                `json:"help"`656	Searchable       bool                                  `json:"searchable"`657	Views            map[string]interface{}                `json:"views"`658	Required         bool                                  `json:"required"`659	Manual           bool                                  `json:"manual"`660	ReadOnly         bool                                  `json:"readonly"`661	Depends          []string                              `json:"depends"`662	CompanyDependent bool                                  `json:"company_dependent"`663	Sortable         bool                                  `json:"sortable"`664	Translate        bool                                  `json:"translate"`665	Type             fieldtype.Type                        `json:"type"`666	Store            bool                                  `json:"store"`667	String           string                                `json:"string"`668	Relation         string                                `json:"relation"`669	Selection        types.Selection                       `json:"selection"`670	Domain           interface{}                           `json:"domain"`671	OnChange         bool                                  `json:"-"`672	ReverseFK        string                                `json:"-"`673	Name             string                                `json:"-"`674	JSON             string                                `json:"-"`675	ReadOnlyFunc     func(Environment) (bool, Conditioner) `json:"-"`676	RequiredFunc     func(Environment) (bool, Conditioner) `json:"-"`677	InvisibleFunc    func(Environment) (bool, Conditioner) `json:"-"`678	DefaultFunc      func(Environment) interface{}         `json:"-"`679	GoType           reflect.Type                          `json:"-"`680	Index            bool                                  `json:"-"`681}682// FieldsGetArgs is the args struct for the FieldsGet method683type FieldsGetArgs struct {684	// Fields is a list of fields to document, all if empty or not provided685	Fields FieldNames `json:"allfields"`686}687// OnchangeParams is the args struct of the Onchange function688type OnchangeParams struct {689	Values   RecordData        `json:"values"`690	Fields   FieldNames        `json:"field_name"`691	Onchange map[string]string `json:"field_onchange"`692}693// OnchangeResult is the result struct type of the Onchange function694type OnchangeResult struct {695	Value   RecordData                `json:"value"`696	Warning string                    `json:"warning"`697	Filters map[FieldName]Conditioner `json:"domain"`698}...

Full Screen

Full Screen

model.go

Source:model.go Github

copy

Full Screen

1package model2import (3	"fmt"4	"io/ioutil"5	"os"6	"path/filepath"7	"strings"8	"go.knocknote.io/eevee/code"9	"go.knocknote.io/eevee/config"10	"go.knocknote.io/eevee/dao"11	"go.knocknote.io/eevee/renderer"12	"go.knocknote.io/eevee/types"13	"golang.org/x/tools/imports"14	"golang.org/x/xerrors"15)16type Generator struct {17	packageName  string18	receiverName string19	daoPath      string20	importList   types.ImportList21	cfg          *config.Config22}23func NewGenerator(cfg *config.Config) *Generator {24	return &Generator{25		packageName:  cfg.ModelPackageName(),26		receiverName: "m",27		importList:   types.DefaultImportList(cfg.ModulePath, cfg.ContextImportPath()),28		cfg:          cfg,29	}30}31func (g *Generator) helper(class *types.Class) *types.ModelMethodHelper {32	return &types.ModelMethodHelper{33		Class:        class,34		ReceiverName: g.receiverName,35		ImportList:   g.importList,36	}37}38func (g *Generator) addMethod(f *code.File, mtd *types.Method) {39	if mtd == nil {40		return41	}42	f.Line()43	f.Add(mtd.Generate(g.importList))44}45func (g *Generator) addRendererMethod(f *code.File, class *types.Class, r renderer.Renderer) {46	g.addMethod(f, r.Render(g.helper(class)))47	g.addMethod(f, r.RenderWithOption(g.helper(class)))48	g.addMethod(f, r.RenderCollection(g.helper(class)))49	g.addMethod(f, r.RenderCollectionWithOption(g.helper(class)))50	g.addMethod(f, r.Marshaler(g.helper(class)))51	g.addMethod(f, r.MarshalerContext(g.helper(class)))52	g.addMethod(f, r.MarshalerCollection(g.helper(class)))53	g.addMethod(f, r.MarshalerCollectionContext(g.helper(class)))54	g.addMethod(f, r.Unmarshaler(g.helper(class)))55	g.addMethod(f, r.UnmarshalerCollection(g.helper(class)))56}57func (g *Generator) generate(class *types.Class, path string) ([]byte, error) {58	f := code.NewFile(g.packageName)59	f.HeaderComment(code.GeneratedMarker)60	for _, importDeclare := range g.importList {61		f.ImportName(importDeclare.Path, importDeclare.Name)62	}63	daoGenerator := dao.NewGenerator(g.cfg)64	daoPackageDecl, err := daoGenerator.PackageDeclare(class, g.daoPath)65	if err != nil {66		return nil, xerrors.Errorf("cannot create package declaration for dao(%s): %w", class.Name.SnakeName(), err)67	}68	interfaceBody := []code.Code{}69	for _, method := range daoPackageDecl.Methods {70		if !strings.HasPrefix(method.MethodName, "Find") {71			continue72		}73		decl := &types.MethodDeclare{74			MethodName: method.MethodName,75			Args:       method.Args,76			Return:     types.ValueDeclares{},77		}78		for _, retDecl := range method.Return {79			name := retDecl.Type.Type.Name80			if name == class.Name.CamelName() || name == class.Name.PluralCamelName() {81				decl.Return = append(decl.Return, &types.ValueDeclare{82					Type: &types.TypeDeclare{83						Type: &types.Type{84							Name: name,85						},86						IsPointer: true,87					},88				})89			} else {90				decl.Return = append(decl.Return, &types.ValueDeclare{91					Type: types.TypeDeclareWithType(&types.Type{92						Name: name,93					}),94				})95			}96		}97		interfaceBody = append(interfaceBody, decl.Interface(g.importList))98	}99	f.Add(code.GoType().Id(fmt.Sprintf("%sFinder", class.Name.CamelName())).Interface(interfaceBody...))100	structFields := []code.Code{code.Op("*").Qual(g.importList.Package("entity"), class.Name.CamelName())}101	structFields = append(structFields,102		code.Id(fmt.Sprintf("%sDAO", class.Name.CamelLowerName())).Qual(g.importList.Package("dao"), class.Name.CamelName()),103	)104	for _, member := range class.ExtendMembers() {105		structFields = append(structFields, code.Id(member.Name.CamelName()).Add(member.Type.Code(g.importList)))106	}107	for _, member := range class.RelationMembers() {108		if member.Relation.Custom {109			continue110		}111		var rtype string112		if member.HasMany || member.Relation.All {113			rtype = member.Type.Class().Name.PluralCamelName()114		} else {115			rtype = member.Type.Class().Name.CamelName()116		}117		structFields = append(structFields,118			code.Id(member.Name.CamelName()).Func().Params(code.Qual(g.importList.Package("context"), "Context")).119				Params(code.Op("*").Id(rtype), code.Id("error")),120		)121	}122	structFields = append(structFields, code.Id("isAlreadyCreated").Bool())123	structFields = append(structFields, code.Id("savedValue").Qual(g.importList.Package("entity"), class.Name.CamelName()))124	structFields = append(structFields, code.Id("conv").Id("ModelConverter"))125	f.Line()126	f.Add(code.GoType().Id(class.Name.CamelName()).Struct(structFields...))127	collectionStructFields := []code.Code{code.Id("values").Index().Op("*").Id(class.Name.CamelName())}128	for _, value := range g.helper(class).CollectionProperties() {129		collectionStructFields = append(collectionStructFields, value.Code(g.importList))130	}131	f.Line()132	f.Add(code.GoType().Id(class.Name.PluralCamelName()).Struct(collectionStructFields...))133	f.Line()134	f.Add(code.GoType().Id(fmt.Sprintf("%sCollection", class.Name.PluralCamelName())).Index().Op("*").Id(class.Name.PluralCamelName()))135	f.Line()136	f.Add(g.Constructor(g.helper(class)))137	f.Line()138	f.Add(g.CollectionConstructor(g.helper(class)))139	for _, mtdFn := range g.Methods() {140		g.addMethod(f, mtdFn(g.helper(class)))141	}142	g.addRendererMethod(f, class, &renderer.JSONRenderer{})143	g.addRendererMethod(f, class, &renderer.MapRenderer{})144	g.addMethod(f, g.SetConverter(g.helper(class)))145	if !class.ReadOnly {146		g.addMethod(f, g.Create(g.helper(class)))147		g.addMethod(f, g.Update(g.helper(class)))148		if class.MemberByName("id") != nil {149			g.addMethod(f, g.Delete(g.helper(class)))150		}151		g.addMethod(f, g.SetAlreadyCreated(g.helper(class)))152		g.addMethod(f, g.SetSavedValue(g.helper(class)))153		g.addMethod(f, g.Save(g.helper(class)))154		g.addMethod(f, g.CreateForCollection(g.helper(class)))155		g.addMethod(f, g.UpdateForCollection(g.helper(class)))156		g.addMethod(f, g.SaveForCollection(g.helper(class)))157	}158	for _, member := range class.Members {159		if member.Relation == nil && !member.Extend {160			g.addMethod(f, g.Unique(g.helper(class), member))161			g.addMethod(f, g.GroupBy(g.helper(class), member))162			g.addMethod(f, g.Collection(g.helper(class), member))163		}164		if member.Relation != nil && !member.Relation.All {165			if !member.Relation.Custom {166				g.addMethod(f, g.findBy(g.helper(class), member))167			}168			g.addMethod(f, g.Collection(g.helper(class), member))169		} else if member.Relation != nil && member.Relation.All {170			g.addMethod(f, g.findAll(g.helper(class), member))171		}172	}173	primaryKey := class.PrimaryKey()174	definedKeyPair := map[string]struct{}{}175	if primaryKey != nil {176		definedKeyPair[primaryKey.Name.SnakeName()] = struct{}{}177		g.addMethod(f, g.FirstBy(g.helper(class), types.Members{primaryKey}))178		g.addMethod(f, g.FilterBy(g.helper(class), types.Members{primaryKey}))179	}180	for _, uniqueKey := range class.UniqueKeys() {181		if _, exists := definedKeyPair[uniqueKey.JoinedName()]; !exists {182			g.addMethod(f, g.FirstBy(g.helper(class), uniqueKey))183			g.addMethod(f, g.FilterBy(g.helper(class), uniqueKey))184			definedKeyPair[uniqueKey.JoinedName()] = struct{}{}185		}186		if len(uniqueKey) < 2 {187			continue188		}189		uniqueKey = uniqueKey[:len(uniqueKey)-1]190		for i := len(uniqueKey); i > 0; i-- {191			if _, exists := definedKeyPair[uniqueKey.JoinedName()]; !exists {192				g.addMethod(f, g.FirstBy(g.helper(class), uniqueKey))193				g.addMethod(f, g.FilterBy(g.helper(class), uniqueKey))194				definedKeyPair[uniqueKey.JoinedName()] = struct{}{}195			}196			uniqueKey = uniqueKey[:len(uniqueKey)-1]197		}198	}199	for _, key := range class.Keys() {200		if _, exists := definedKeyPair[key.JoinedName()]; !exists {201			g.addMethod(f, g.FirstBy(g.helper(class), key))202			g.addMethod(f, g.FilterBy(g.helper(class), key))203			definedKeyPair[key.JoinedName()] = struct{}{}204		}205		if len(key) < 2 {206			continue207		}208		key = key[:len(key)-1]209		for i := len(key); i > 0; i-- {210			if _, exists := definedKeyPair[key.JoinedName()]; !exists {211				g.addMethod(f, g.FirstBy(g.helper(class), key))212				g.addMethod(f, g.FilterBy(g.helper(class), key))213				definedKeyPair[key.JoinedName()] = struct{}{}214			}215			key = key[:len(key)-1]216		}217	}218	bytes := []byte(fmt.Sprintf("%#v", f))219	source, err := imports.Process("", bytes, nil)220	if err != nil {221		return nil, xerrors.Errorf("cannot format by goimport: %w", err)222	}223	return source, nil224}225func (g *Generator) generateModelClass(path string, classes []*types.Class) error {226	f := code.NewFile(g.packageName)227	f.HeaderComment(code.GeneratedMarker)228	for _, importDeclare := range g.importList {229		f.ImportName(importDeclare.Path, importDeclare.Name)230	}231	interfaceBody := []code.Code{}232	for _, class := range classes {233		decl := &types.MethodDeclare{234			Class:      class,235			ImportList: g.importList,236			MethodName: fmt.Sprintf("To%s", class.Name.CamelName()),237			Args: types.ValueDeclares{238				{239					Name: "value",240					Type: &types.TypeDeclare{241						Type: &types.Type{242							PackageName: g.importList.Package("entity"),243							Name:        class.Name.CamelName(),244						},245						IsPointer: true,246					},247				},248			},249			Return: types.ValueDeclares{250				{251					Type: &types.TypeDeclare{252						Type: &types.Type{253							Name: class.Name.CamelName(),254						},255						IsPointer: true,256					},257				},258			},259		}260		interfaceBody = append(interfaceBody, decl.Interface(g.importList))261	}262	f.Add(code.GoType().Id("ModelConverter").Interface(interfaceBody...))263	g.generateRenderOption(f)264	source := []byte(fmt.Sprintf("%#v", f))265	modelGoPath := filepath.Join(path, "model.go")266	if g.existsFile(modelGoPath) {267		if err := os.Remove(modelGoPath); err != nil {268			return xerrors.Errorf("failed to remove file %s: %w", modelGoPath, err)269		}270	}271	if err := ioutil.WriteFile(modelGoPath, source, 0444); err != nil {272		return xerrors.Errorf("cannot write file %s: %w", path, err)273	}274	return nil275}276func (g *Generator) existsFile(path string) bool {277	_, err := os.Stat(path)278	return err == nil279}280func (g *Generator) writeFile(class *types.Class, basePath string, source []byte) error {281	path := filepath.Join(basePath, fmt.Sprintf("%s.go", class.Name.SnakeName()))282	if g.existsFile(path) {283		if err := os.Remove(path); err != nil {284			return xerrors.Errorf("failed to remove file %s: %w", path, err)285		}286	}287	if err := ioutil.WriteFile(path, source, 0444); err != nil {288		return xerrors.Errorf("cannot write file %s: %w", path, err)289	}290	return nil291}292func (g *Generator) Generate(classes []*types.Class) error {293	path := g.cfg.OutputPathWithPackage(g.packageName)294	if err := os.MkdirAll(path, 0755); err != nil {295		return xerrors.Errorf("cannot create directory to %s: %w", path, err)296	}297	splittedPaths := strings.Split(path, string(filepath.Separator))298	daoPaths := splittedPaths[:len(splittedPaths)-1]299	daoPaths = append(daoPaths, "dao")300	daoPath := filepath.Join(daoPaths...)301	g.daoPath = daoPath302	for _, class := range classes {303		source, err := g.generate(class, path)304		if err != nil {305			return xerrors.Errorf("cannot generate model for %s: %w", class.Name.SnakeName(), err)306		}307		if err := g.writeFile(class, path, source); err != nil {308			return xerrors.Errorf("cannot write file to %s for %s: %w", path, class.Name.SnakeName(), err)309		}310	}311	if err := g.generateModelClass(path, classes); err != nil {312		return xerrors.Errorf("cannot generate model.go: %w", err)313	}314	return nil315}...

Full Screen

Full Screen

AddMethod

Using AI Code Generation

copy

Full Screen

1import (2type User struct {3    Name string `orm:"size(100)"`4}5func init() {6    orm.RegisterDataBase("default", "mysql", "root:root@/test?charset=utf8", 30)7    orm.RegisterModel(new(User))8    orm.RunSyncdb("default", false, true)9}10func main() {11    o := orm.NewOrm()12    user := User{Name: "slene"}13    id, err := o.Insert(&user)14    fmt.Printf("ID: %d, ERR: %v\n", id, err)15    num, err := o.Update(&user)16    fmt.Printf("NUM: %d, ERR: %v\n", num, err)17    u := User{Id: user.Id}18    err = o.Read(&u)19    fmt.Printf("ERR: %v\n", err)20    num, err = o.Delete(&u)21    fmt.Printf("NUM: %d, ERR: %v\n", num, err)22}23import (24type User struct {25    Name string `orm:"size(100)"`26}27func init() {28    orm.RegisterDataBase("default", "mysql", "root:root@/test?charset=utf8", 30)29    orm.RegisterModel(new(User))30    orm.RunSyncdb("default", false, true)31}32func (u *User) TableName() string {33}34func main() {35    o := orm.NewOrm()36    user := User{Name: "slene"}37    id, err := o.Insert(&user)38    fmt.Printf("ID: %d, ERR: %

Full Screen

Full Screen

AddMethod

Using AI Code Generation

copy

Full Screen

1import (2func init() {3    orm.RegisterDataBase("default", "mysql", "root:root@/test?charset=utf8", 30)4    orm.RegisterModel(new(User))5    orm.RunSyncdb("default", false, true)6}7type User struct {8    Name string `orm:"size(100)"`9}10func (u *User) TableName() string {11}12func main() {13    o := orm.NewOrm()14    user := User{Name: "slene"}15    id, err := o.Insert(&user)16    fmt.Printf("ID: %d, ERR: %v17    num, err := o.Update(&user)18    fmt.Printf("NUM: %d, ERR: %v19    u := User{Id: user.Id}20    err = o.Read(&u)21    fmt.Printf("ERR: %v22    num, err = o.Delete(&u)23    fmt.Printf("NUM: %d, ERR: %v24}25import (26func init() {27    orm.RegisterDataBase("default", "mysql", "root:root@/test?charset=utf8", 30)28    orm.RegisterModel(new(User))29    orm.RunSyncdb("default", false, true)30}31type User struct {32    Name string `orm:"size(100)"`33}34func (u *User) TableName() string {35}36func (u *User) Insert() error {

Full Screen

Full Screen

AddMethod

Using AI Code Generation

copy

Full Screen

1import (2func main() {3    beego.Get("/api/v1", func(ctx *context.Context) {4        ctx.Output.Body([]byte("hello world"))5    })6    beego.Run()7}8import (9func main() {10    beego.Get("/api/v1", func(ctx *context.Context) {11        ctx.Output.Body([]byte("hello world"))12    })13    beego.Run()14}15import (16func main() {17    beego.Get("/api/v1", func(ctx *context.Context) {18        ctx.Output.Body([]byte("hello world"))19    })20    beego.Run()21}22import (23func main() {24    beego.Get("/api/v1", func(ctx *context.Context) {25        ctx.Output.Body([]byte("hello world"))26    })27    beego.Run()28}29import (30func main() {31    beego.Get("/api/v1", func(ctx *context.Context) {32        ctx.Output.Body([]byte("hello world"))33    })34    beego.Run()35}36import (37func main() {38    beego.Get("/api/v1", func(ctx *context.Context) {

Full Screen

Full Screen

AddMethod

Using AI Code Generation

copy

Full Screen

1import (2func main() {3	model.AddMethod()4	fmt.Println("Hello World!")5}6import (7func main() {8	model.AddMethod()9	fmt.Println("Hello World!")10}11import (12func main() {13	model.AddMethod()14	fmt.Println("Hello World!")15}16import (17func main() {18	model.AddMethod()19	fmt.Println("Hello World!")20}21import (22func main() {23	model.AddMethod()24	fmt.Println("Hello World!")25}26import (27func main() {28	model.AddMethod()29	fmt.Println("Hello World!")30}31import (32func main() {33	model.AddMethod()34	fmt.Println("Hello World!")35}36import (37func main() {38	model.AddMethod()39	fmt.Println("Hello World!")40}41import (42func main() {43	model.AddMethod()44	fmt.Println("Hello World!")45}46import (47func main() {48	model.AddMethod()49	fmt.Println("Hello World!")50}51import (52func main()

Full Screen

Full Screen

AddMethod

Using AI Code Generation

copy

Full Screen

1m := new(gorm.Model)2m.AddMethod("MyMethod", func() {3    fmt.Println("MyMethod called!")4})5m.MyMethod()6m := new(gorm.Model)7m.AddMethod("MyMethod", func() {8    fmt.Println("MyMethod called!")9})10m.MyMethod()11m := new(gorm.Model)12m.AddMethod("MyMethod", func() {13    fmt.Println("MyMethod called!")14})15m.MyMethod()16m := new(gorm.Model)17m.AddMethod("MyMethod", func() {18    fmt.Println("MyMethod called!")19})20m.MyMethod()21m := new(gorm.Model)22m.AddMethod("MyMethod", func() {23    fmt.Println("MyMethod called!")24})25m.MyMethod()26m := new(gorm.Model)27m.AddMethod("MyMethod", func() {28    fmt.Println("MyMethod called!")29})30m.MyMethod()31m := new(gorm.Model)32m.AddMethod("MyMethod", func() {33    fmt.Println("MyMethod called!")34})35m.MyMethod()36m := new(gorm.Model)37m.AddMethod("MyMethod", func

Full Screen

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run Mock automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful