diff --git a/README.md b/README.md index c9f2531..f6eee39 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,22 @@ class Author: name: str ``` +### Add a Suffix to Generated Models/Types + +Option: `output_models_suffix` + +By default, generated models and query-specific types keep their standard names (`Author`, `GetAuthorRow`, `CreateAuthorParams`, etc). If you want to avoid naming collisions with existing application types, you can add a suffix. + +with `output_models_suffix: DTO` + +```py +class AuthorDTO: + ... + +class CreateAuthorParamsDTO: + ... +``` + ### Use `enum.StrEnum` for Enums Option: `emit_str_enum` diff --git a/internal/config.go b/internal/config.go index 1a8a565..447d009 100644 --- a/internal/config.go +++ b/internal/config.go @@ -8,6 +8,7 @@ type Config struct { Out string `json:"out"` EmitPydanticModels bool `json:"emit_pydantic_models"` EmitStrEnum bool `json:"emit_str_enum"` + OutputModelsSuffix string `json:"output_models_suffix"` QueryParameterLimit *int32 `json:"query_parameter_limit"` InflectionExcludeTableNames []string `json:"inflection_exclude_table_names"` } diff --git a/internal/endtoend/testdata/output_models_suffix/python/models.py b/internal/endtoend/testdata/output_models_suffix/python/models.py new file mode 100644 index 0000000..a6ceb65 --- /dev/null +++ b/internal/endtoend/testdata/output_models_suffix/python/models.py @@ -0,0 +1,10 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.28.0 +import dataclasses + + +@dataclasses.dataclass() +class BarDTO: + id: int + name: str diff --git a/internal/endtoend/testdata/output_models_suffix/python/query.py b/internal/endtoend/testdata/output_models_suffix/python/query.py new file mode 100644 index 0000000..9a42675 --- /dev/null +++ b/internal/endtoend/testdata/output_models_suffix/python/query.py @@ -0,0 +1,63 @@ +# Code generated by sqlc. DO NOT EDIT. +# versions: +# sqlc v1.28.0 +# source: query.sql +import dataclasses +from typing import Iterator, Optional + +import sqlalchemy + +from querytest import models + + +DELETE_BAR_BY_ID_AND_NAME = """-- name: delete_bar_by_id_and_name \\:execrows +DELETE FROM bar WHERE id = :p1 AND name = :p2 +""" + + +@dataclasses.dataclass() +class DeleteBarByIDAndNameParamsDTO: + id: int + name: str + + +GET_BAR = """-- name: get_bar \\:one +SELECT id, name FROM bar WHERE id = :p1 LIMIT 1 +""" + + +LIST_BAR_UPPER_NAMES = """-- name: list_bar_upper_names \\:many +SELECT id, name AS upper_name FROM bar ORDER BY id +""" + + +@dataclasses.dataclass() +class ListBarUpperNamesRowDTO: + id: int + upper_name: str + + +class Querier: + def __init__(self, conn: sqlalchemy.engine.Connection): + self._conn = conn + + def delete_bar_by_id_and_name(self, arg: DeleteBarByIDAndNameParamsDTO) -> int: + result = self._conn.execute(sqlalchemy.text(DELETE_BAR_BY_ID_AND_NAME), {"p1": arg.id, "p2": arg.name}) + return result.rowcount + + def get_bar(self, *, id: int) -> Optional[models.BarDTO]: + row = self._conn.execute(sqlalchemy.text(GET_BAR), {"p1": id}).first() + if row is None: + return None + return models.BarDTO( + id=row[0], + name=row[1], + ) + + def list_bar_upper_names(self) -> Iterator[ListBarUpperNamesRowDTO]: + result = self._conn.execute(sqlalchemy.text(LIST_BAR_UPPER_NAMES)) + for row in result: + yield ListBarUpperNamesRowDTO( + id=row[0], + upper_name=row[1], + ) diff --git a/internal/endtoend/testdata/output_models_suffix/query.sql b/internal/endtoend/testdata/output_models_suffix/query.sql new file mode 100644 index 0000000..75321f6 --- /dev/null +++ b/internal/endtoend/testdata/output_models_suffix/query.sql @@ -0,0 +1,8 @@ +-- name: DeleteBarByIDAndName :execrows +DELETE FROM bar WHERE id = $1 AND name = $2; + +-- name: GetBar :one +SELECT * FROM bar WHERE id = $1 LIMIT 1; + +-- name: ListBarUpperNames :many +SELECT id, name AS upper_name FROM bar ORDER BY id; diff --git a/internal/endtoend/testdata/output_models_suffix/schema.sql b/internal/endtoend/testdata/output_models_suffix/schema.sql new file mode 100644 index 0000000..42f134f --- /dev/null +++ b/internal/endtoend/testdata/output_models_suffix/schema.sql @@ -0,0 +1 @@ +CREATE TABLE bar (id serial not null, name text not null, primary key (id)); diff --git a/internal/endtoend/testdata/output_models_suffix/sqlc.yaml b/internal/endtoend/testdata/output_models_suffix/sqlc.yaml new file mode 100644 index 0000000..26ae796 --- /dev/null +++ b/internal/endtoend/testdata/output_models_suffix/sqlc.yaml @@ -0,0 +1,18 @@ +version: '2' +plugins: +- name: py + wasm: + url: file://../../../../bin/sqlc-gen-python.wasm + sha256: "d6846ffad948181e611e883cedd2d2be66e091edc1273a0abc6c9da18399e0ca" +sql: +- schema: schema.sql + queries: query.sql + engine: postgresql + codegen: + - plugin: py + out: python + options: + package: querytest + emit_sync_querier: true + query_parameter_limit: 1 + output_models_suffix: DTO diff --git a/internal/gen.go b/internal/gen.go index 6e50fae..4f401a9 100644 --- a/internal/gen.go +++ b/internal/gen.go @@ -207,6 +207,13 @@ func modelName(name string, settings *plugin.Settings) string { return out } +func withSuffix(name, suffix string) string { + if suffix == "" { + return name + } + return name + suffix +} + var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)") var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])") @@ -226,7 +233,7 @@ func pyEnumValueName(value string) string { return strings.ToUpper(id) } -func buildEnums(req *plugin.GenerateRequest) []Enum { +func buildEnums(conf Config, req *plugin.GenerateRequest) []Enum { var enums []Enum for _, schema := range req.Catalog.Schemas { if schema.Name == "pg_catalog" || schema.Name == "information_schema" { @@ -240,7 +247,7 @@ func buildEnums(req *plugin.GenerateRequest) []Enum { enumName = schema.Name + "_" + enum.Name } e := Enum{ - Name: modelName(enumName, req.Settings), + Name: withSuffix(modelName(enumName, req.Settings), conf.OutputModelsSuffix), Comment: enum.Comment, } for _, v := range enum.Vals { @@ -281,7 +288,7 @@ func buildModels(conf Config, req *plugin.GenerateRequest) []Struct { } s := Struct{ Table: plugin.Identifier{Schema: schema.Name, Name: table.Rel.Name}, - Name: modelName(structName, req.Settings), + Name: withSuffix(modelName(structName, req.Settings), conf.OutputModelsSuffix), Comment: table.Comment, } for _, column := range table.Columns { @@ -406,7 +413,7 @@ func buildQueries(conf Config, req *plugin.GenerateRequest, structs []Struct) ([ gq.Args = []QueryValue{{ Emit: true, Name: "arg", - Struct: columnsToStruct(req, query.Name+"Params", cols), + Struct: columnsToStruct(req, withSuffix(query.Name+"Params", conf.OutputModelsSuffix), cols), }} } else { args := make([]QueryValue, 0, len(query.Params)) @@ -461,7 +468,7 @@ func buildQueries(conf Config, req *plugin.GenerateRequest, structs []Struct) ([ Column: c, }) } - gs = columnsToStruct(req, query.Name+"Row", columns) + gs = columnsToStruct(req, withSuffix(query.Name+"Row", conf.OutputModelsSuffix), columns) emit = true } gq.Ret = QueryValue{ @@ -1089,7 +1096,7 @@ func Generate(_ context.Context, req *plugin.GenerateRequest) (*plugin.GenerateR } } - enums := buildEnums(req) + enums := buildEnums(conf, req) models := buildModels(conf, req) queries, err := buildQueries(conf, req, models) if err != nil {