go-django-queries

module
v1.0.7 Latest Latest
Warning

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

Go to latest
Published: Apr 29, 2025 License: GPL-2.0

README

go-django-queries

While Go-Django tries to do as little as possible with the database, sometimes helper functions make working with models easier.

This package provides simple ways to build queries, inserts, updates, deletes, and fetch related models automatically.


Table of Contents

Latest version: v1.0.7


Installation

go get github.com/Nigel2392/go-django-queries@latest

Defining your models

The models are defined using the attrs package.

type Profile struct {
    ID    int
    Name  string
    Email string
}

func (m *Profile) FieldDefs() attrs.Definitions {
    return attrs.Define(m,
        attrs.NewField(m, "ID", &attrs.FieldConfig{
            Primary:  true,
            ReadOnly: true,
        }),
        attrs.NewField(m, "Name", &attrs.FieldConfig{}),
        attrs.NewField(m, "Email", &attrs.FieldConfig{}),
    ).WithTableName("profiles")
}

type User struct {
    ID      int
    Name    string
    Profile *Profile
}

func (m *User) FieldDefs() attrs.Definitions {
    return attrs.Define(m,
        attrs.NewField(m, "ID", &attrs.FieldConfig{
            Primary:  true,
            ReadOnly: true,
        }),
        attrs.NewField(m, "Name", &attrs.FieldConfig{}),
        attrs.NewField(m, "Profile", &attrs.FieldConfig{
            RelForeignKey: &Profile{},
            Column:        "profile_id",
        }),
    ).WithTableName("users")
}

type Todo struct {
    ID          int
    Title       string
    Description string
    Done        bool
    User        *User
}

func (m *Todo) FieldDefs() attrs.Definitions {
    return attrs.Define(m,
        attrs.NewField(m, "ID", &attrs.FieldConfig{
            Primary:  true,
            ReadOnly: true,
        }),
        attrs.NewField(m, "Title", &attrs.FieldConfig{}),
        attrs.NewField(m, "Description", &attrs.FieldConfig{}),
        attrs.NewField(m, "Done", &attrs.FieldConfig{}),
        attrs.NewField(m, "User", &attrs.FieldConfig{
            Column:        "user_id",
            RelForeignKey: &User{},
        }),
    ).WithTableName("todos")
}

Usage Examples

Querying records

Queries are built using the queries.Objects function, which takes a model type as an argument.

We will explain the following example query:

  query := queries.Objects(&Todo{}).
    // Select the user and profile fields, append a star to
    // automatically select all related fields, User.Profile would always result in a join
    Select("ID", "Title", "Description", "Done", "User.*", "User.Profile.*").

    // Select the user and profile fields, leaving out star operator
    // This would not result in a join, and only fetch the user's ID
    Select("ID", "Title", "Description", "Done", "User").
    
    // Generate a WHERE clause with the given conditions
    Filter(
      queries.Q("Title__icontains", "new test"),
      queries.Q("Done", true),
      queries.Q("User.Name__icontains", "test"),
      queries.Q("User.ID", user.ID),
      queries.Q("User.Profile.Email__icontains", profile.Email),
      queries.Q("User.Profile.ID", profile.ID),
    ).
    // Generate an ORDER BY clause with the given conditions
    OrderBy("-ID", "-User.Name", "-User.Profile.Email").
    Limit(5).
    All()


    // todos is of type Query[[]*Todo, *Todo]
    todos, err := query.Exec() // / []*Todo, error
    if err != nil {
      t.Fatalf("Failed to query todos: %v", err)
    }

    fmt.Printf("Queried todos: %v\n", todos)
    fmt.Printf("Executed SQL: %s\n", query.SQL())

The above query will generate the following SQL:

SELECT "todos"."ID", "todos"."Title", "todos"."Description", "todos"."Done", "users"."id", "users"."name", "profiles"."id", "profiles"."name", "profiles"."email"
FROM "todos"
LEFT JOIN "users" ON "todos"."user_id" = "users"."id"
LEFT JOIN "profiles" ON "users"."profile_id" = "profiles"."id"
WHERE LOWER("todos"."Title", 'new test') LIKE LOWER('%new test%')
AND "todos"."Done" = true
AND "users"."name" LIKE LOWER('%test%')
AND "users"."id" = 1
AND "profiles"."email" LIKE LOWER('%example.com%')
AND "profiles"."id" = 1
ORDER BY "todos"."ID" DESC, "users"."name" DESC, "profiles"."email" DESC
LIMIT 5

The query will return a slice of attrs.Definer objects, which can be cast to the appropriate type.

Create a new record
todo := &Todo{
    Title: "Finish task",
    Description: "Write documentation",
    Done: false,
}

created, err := queries.Objects(&Todo{}).Create(todo).Exec()

Update a record
todo.Title = "Update documentation"
updatedRows, err := queries.Objects(&Todo{}).
    Filter("ID", todo.ID).
    Update(todo).Exec()

Delete a record
deletedRows, err := queries.Objects(&Todo{}).
    Filter("ID", todo.ID).
    Delete().Exec()

Count records
count, err := queries.Objects(&Todo{}).
    Filter("Done", false).
    Count().Exec()

Get a single record
todo, err := queries.Objects(&Todo{}).
    Filter(queries.Q("Title__istartswith", "Finish ta")).
    Get().Exec()

Get first or last record
firstTodo, err := queries.Objects(&Todo{}).
    OrderBy("ID").
    First().Exec()

lastTodo, err := queries.Objects(&Todo{}).
    OrderBy("ID").
    Last().Exec()

Get or create

GetOrCreate is the only queryset function that will execute, and not return a Query[T] object.

It will always return the (retrieved or created) object and an error.

todo := &Todo{ Title: "Unique task" }

dbTodo, err := queries.Objects(&Todo{}).
    Filter(queries.Q("Title", todo.Title)).
    GetOrCreate(todo)

Exists
exists, err := queries.Objects(&Todo{}).
    Filter(queries.Q("Title__icontains", "task")).
    Exists().Exec()

ValuesList query
values, err := queries.Objects(&Todo{}).
    Select("ID", "Title").
    Filter(queries.Q("Done", false)).
    ValuesList().Exec()

for _, row := range values {
    fmt.Println(row) // []interface{} { ID, Title }
}

Filter with Or
todos, err := queries.Objects(&Todo{}).
    Filter(
        queries.Or(
            queries.Q("Title__icontains", "urgent"),
            queries.Q("Title__icontains", "important"),
        ),
    ).
    OrderBy("-ID").
    All().Exec()

todos, err := queries.Objects(&Todo{}).
    Select("ID", "Title", "Done", "User.*", "User.Profile.*").
    Filter(
        queries.Q("Done", false),
        queries.Q("User.Profile.Email__icontains", "example.com"),
    ).
    OrderBy("-ID").
    All().Exec()

Query Interface

The Query represents a query that can be executed against the database.

type Query[T any] interface {
    // SQL returns the SQL string
    SQL() string

    // Args returns the query arguments
    Args() []any

    // Model returns the model instance
    Model() attrs.Definer

    // Exec executes the query
    Exec() (T, error)
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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