DevLake's base model structs (common.NoPKModel and common.Model) automatically manage certain columns. These columns are reserved and must not be used to store data originating from external data sources. This page explains which columns are reserved, why they exist, and what naming conventions to follow when your plugin needs to store timestamps from a data source.
The following columns are reserved by DevLake:
| Column | Type | Purpose |
|---|---|---|
created_at | datetime(3) | Automatically set to the time the record is inserted into DevLake's database. |
updated_at | datetime(3) | Automatically set to the time the record is last updated in DevLake's database. |
_raw_data_params | varchar(255) | Links the record back to its raw-layer source. |
_raw_data_table | varchar(255) | Identifies which raw table the record was extracted from. |
_raw_data_id | bigint | References the specific row in the raw table. |
The created_at and updated_at columns are managed by GORM's autoCreateTime and autoUpdateTime tags on the NoPKModel base struct. They reflect when DevLake processed the record, not when the record was created or updated in the original data source.
If a plugin maps a data source‘s creation or update timestamp directly to created_at or updated_at, the value will be silently overwritten by GORM’s auto-timestamp behavior. This means:
When your plugin model needs to store timestamps from an external data source, use a prefixed column name that identifies the source system. Here are examples from existing plugins:
// ✅ Correct — use a source-specific prefix type GithubIssue struct { ConnectionId uint64 `gorm:"primaryKey"` GithubId int `gorm:"primaryKey"` // ... GithubCreatedAt time.Time // timestamp from GitHub GithubUpdatedAt time.Time `gorm:"index"` // timestamp from GitHub common.NoPKModel // provides created_at, updated_at } // ❌ Wrong — overwrites DevLake's auto-managed columns type GithubIssue struct { ConnectionId uint64 `gorm:"primaryKey"` GithubId int `gorm:"primaryKey"` CreatedAt time.Time // conflicts with NoPKModel UpdatedAt time.Time // conflicts with NoPKModel common.NoPKModel }
Domain-layer models follow the same rule. Use descriptive names like CreatedDate, UpdatedDate, or source-prefixed names:
type GitlabDeployment struct { common.NoPKModel ConnectionId uint64 `gorm:"primaryKey"` GitlabId int `gorm:"primaryKey"` CreatedDate time.Time `json:"created_date"` // from GitLab API UpdatedDate *time.Time `json:"updated_date"` // from GitLab API // ... }
The same convention applies to Python plugins. The NoPKModel base class in PyDevLake provides created_at and updated_at automatically:
class MyToolIssue(ToolModel): # ✅ Correct tool_created_at: datetime # timestamp from the external tool tool_updated_at: datetime # timestamp from the external tool # ❌ Wrong — these conflict with NoPKModel # created_at: datetime # updated_at: datetime
| Plugin | Prefix Pattern | Example |
|---|---|---|
| GitHub | Github | GithubCreatedAt, GithubUpdatedAt |
| GitLab | Gitlab | GitlabCreatedAt, GitlabUpdatedAt |
| Jira | Created, Updated | Created, Updated (Jira-specific fields) |
| Generic | CreatedDate / UpdatedDate | CreatedDate, UpdatedDate |
Pick whichever prefix is most natural for your data source, but never use the bare names created_at / updated_at (or their Go equivalents CreatedAt / UpdatedAt without a prefix).
When defining a new tool-layer or domain-layer model:
common.NoPKModel (Go) or extend NoPKModel / ToolModel (Python) — this gives you created_at and updated_at for free.CreatedAt or UpdatedAt fields without a source-specific prefix.GithubCreatedAt) or use a generic alternative like CreatedDate.gorm:"comment:..." tag so that other contributors understand which system the timestamp originates from.models/common