Best Mock code snippet using model.AddMethod
base_model.go
Source:base_model.go
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}...
model.go
Source:model.go
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}...
AddMethod
Using AI Code Generation
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: %
AddMethod
Using AI Code Generation
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 {
AddMethod
Using AI Code Generation
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) {
AddMethod
Using AI Code Generation
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()
AddMethod
Using AI Code Generation
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
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.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!