Skip to content

Commit

Permalink
Merge branch 'main' into feat/many_to_many_use_select2_multiple
Browse files Browse the repository at this point in the history
  • Loading branch information
ElfenSterben authored Jul 3, 2024
2 parents 0559382 + 32637d1 commit 3e2bc8b
Show file tree
Hide file tree
Showing 43 changed files with 683 additions and 377 deletions.
64 changes: 34 additions & 30 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,40 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).


## Version 0.18.0 - 2024-07-01

### Added

* Add `form_rules`, `form_create_rules`, `form_edit_rules` by @aminalaee in https://github.com/aminalaee/sqladmin/pull/779
* Add more docs for overriding default tempates by @jonocodes in https://github.com/aminalaee/sqladmin/pull/769

### Fixed
* Fix edit_form_query documentation example by @lukeclimen in https://github.com/aminalaee/sqladmin/pull/777

**Full Changelog**: https://github.com/aminalaee/sqladmin/compare/0.17.0...0.18.0

## Version 0.17.0 - 2024-05-13

### Added

* Add field description to Create/Edit templates by @ngaranko in https://github.com/aminalaee/sqladmin/pull/722
* Add edit_form_query method by @lukeclimen in https://github.com/aminalaee/sqladmin/pull/745
* Validate page and pageSize query parameters by @BhuwanPandey in https://github.com/aminalaee/sqladmin/pull/752

### Fixed

* Hide save and add another button from edit.html if can_create is False by @MaximZemskov in https://github.com/aminalaee/sqladmin/pull/742
* Fix list page sort symbol by @aminalaee in https://github.com/aminalaee/sqladmin/pull/744
* Move template files from `templates` to `templates/sqladmin` by @hasansezertasan in https://github.com/aminalaee/sqladmin/pull/748
* Fix `form_args` default by @aminalaee in https://github.com/aminalaee/sqladmin/pull/756
* Fix getting column python type by @aminalaee in https://github.com/aminalaee/sqladmin/pull/757
* Fix File and Image fields checkbox and input by @aminalaee in https://github.com/aminalaee/sqladmin/pull/761
* Switch relationship loading to selectionload by @aminalaee in https://github.com/aminalaee/sqladmin/pull/758
* Fix DELETE call query params by @aminalaee in https://github.com/aminalaee/sqladmin/pull/763

**Full Changelog**: https://github.com/aminalaee/sqladmin/compare/0.16.1...0.17.0

## Version 0.16.1 - 2024-02-20

### Fixed
Expand All @@ -13,12 +47,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Fix sort by model attribute in https://github.com/aminalaee/sqladmin/pull/713
* Fix Category not respecting is_visible and is_accessible in https://github.com/aminalaee/sqladmin/pull/698

## New Contributors
* @kostyaten made their first contribution in https://github.com/aminalaee/sqladmin/pull/677
* @EnotShow made their first contribution in https://github.com/aminalaee/sqladmin/pull/703
* @jonocodes made their first contribution in https://github.com/aminalaee/sqladmin/pull/707
* @Neverfan1 made their first contribution in https://github.com/aminalaee/sqladmin/pull/698

**Full Changelog**: https://github.com/aminalaee/sqladmin/compare/0.16.0...0.16.1

## Version 0.16.0 - 2023-11-14
Expand Down Expand Up @@ -47,10 +75,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
* Add customized sort query signature (#624) by @YarLikviD in https://github.com/aminalaee/sqladmin/pull/625

## New Contributors
* @Toshakins made their first contribution in https://github.com/aminalaee/sqladmin/pull/626
* @YarLikviD made their first contribution in https://github.com/aminalaee/sqladmin/pull/625

**Full Changelog**: https://github.com/aminalaee/sqladmin/compare/0.15.0...0.15.1

## Version 0.15.0 - 2023-09-19
Expand Down Expand Up @@ -334,10 +358,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Fix missing browser tab title by @cuamckuu in https://github.com/aminalaee/sqladmin/pull/229
* Remove sourceMappingURL in JS files by @aminalaee in https://github.com/aminalaee/sqladmin/pull/231

### New Contributors
* @ischaojie made their first contribution in https://github.com/aminalaee/sqladmin/pull/214
* @cuamckuu made their first contribution in https://github.com/aminalaee/sqladmin/pull/222

**Full Changelog**: https://github.com/aminalaee/sqladmin/compare/0.1.11...0.1.12

## Version 0.1.11 - 2022-06-23
Expand Down Expand Up @@ -371,10 +391,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Fix form fields order when specifying columns by @okapies in https://github.com/aminalaee/sqladmin/pull/184
* Fix ModelConverter when `impl` is not callable by @aminalaee in https://github.com/aminalaee/sqladmin/pull/186

### New Contributors
* @pgrimaud made their first contribution in https://github.com/aminalaee/sqladmin/pull/161
* @okapies made their first contribution in https://github.com/aminalaee/sqladmin/pull/183

**Full Changelog**: https://github.com/aminalaee/sqladmin/compare/0.1.9...0.1.10

## Version 0.1.9 - 2022-05-27
Expand All @@ -391,10 +407,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Function signature typing, and renames by @dwreeves in https://github.com/aminalaee/sqladmin/pull/116
* Fix SQLModel UUID type by @aminalaee in https://github.com/aminalaee/sqladmin/pull/158

### New Contributors
* @skarrok made their first contribution in https://github.com/aminalaee/sqladmin/pull/140
* @colin99d made their first contribution in https://github.com/aminalaee/sqladmin/pull/150

**Full Changelog**: https://github.com/aminalaee/sqladmin/compare/0.1.8...0.1.9

## Version 0.1.8 - 2022-04-19
Expand All @@ -412,10 +424,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Fix get_model_attr with column labels by @aminalaee in https://github.com/aminalaee/sqladmin/pull/128
* Delay call to `self.get_converter` to use `form_overrides` by @lovetoburnswhen in https://github.com/aminalaee/sqladmin/pull/129

### New Contributors
* @tr11 made their first contribution in https://github.com/aminalaee/sqladmin/pull/114
* @lovetoburnswhen made their first contribution in https://github.com/aminalaee/sqladmin/pull/129

**Full Changelog**: https://github.com/aminalaee/sqladmin/compare/0.1.7...0.1.8

## Version 0.1.7 - 2022-03-22
Expand All @@ -433,10 +441,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Fix PostgreSQL UUID PrimaryKey by @aminalaee in https://github.com/aminalaee/sqladmin/pull/92
* Fix Source Code Link by @baurt in https://github.com/aminalaee/sqladmin/pull/95

### New Contributors
* @baurt made their first contribution in https://github.com/aminalaee/sqladmin/pull/95
* @dwreeves made their first contribution in https://github.com/aminalaee/sqladmin/pull/97

**Full Changelog**: https://github.com/aminalaee/sqladmin/compare/0.1.6...0.1.7

## Version 0.1.6 - 2022-03-09
Expand Down
4 changes: 4 additions & 0 deletions docs/api_reference/model_view.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
- form_include_pk
- form_ajax_refs
- form_converter
- form_edit_query
- form_rules
- form_create_rules
- form_edit_rules
- column_type_formatters
- list_query
- count_query
Expand Down
14 changes: 10 additions & 4 deletions docs/configurations.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,10 @@ The forms are based on `WTForms` package and include the following options:
* `form_include_pk`: Control if primary key column should be included in create/edit forms. Default is `False`.
* `form_ajax_refs`: Use Ajax with Select2 for loading relationship models async. This is use ful when the related model has a lot of records.
* `form_converter`: Allow adding custom converters to support additional column types.
* `form_edit_query`: A method with the signature of `(request) -> stmt` which can customize the edit form data.
* `form_rules`: List of form rules to manage rendering and behaviour of form.
* `form_create_rules`: List of form rules to manage rendering and behaviour of form in create page.
* `form_edit_rules`: List of form rules to manage rendering and behaviour of form in edit page.

!!! example

Expand All @@ -216,6 +220,8 @@ The forms are based on `WTForms` package and include the following options:
"order_by": ("id",),
}
}
form_create_rules = ["name", "password"]
form_edit_rules = ["name"]
```

## Export options
Expand All @@ -234,10 +240,10 @@ The export options can be set per model and includes the following options:
The template files are built using Jinja2 and can be completely overridden in the configurations.
The pages available are:

* `list_template`: Template to use for models list page. Default is `list.html`.
* `create_template`: Template to use for model creation page. Default is `create.html`.
* `details_template`: Template to use for model details page. Default is `details.html`.
* `edit_template`: Template to use for model edit page. Default is `edit.html`.
* `list_template`: Template to use for models list page. Default is `sqladmin/list.html`.
* `create_template`: Template to use for model creation page. Default is `sqladmin/create.html`.
* `details_template`: Template to use for model details page. Default is `sqladmin/details.html`.
* `edit_template`: Template to use for model edit page. Default is `sqladmin/edit.html`.

!!! example

Expand Down
18 changes: 18 additions & 0 deletions docs/cookbook/optimize_relationship_loading.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,21 @@ which should be available in the form.
class ParentAdmin(ModelView, model=Parent):
form_excluded_columns = [Parent.children]
```

### Using `form_edit_query` to customize the edit form data

If you would like to fully customize the query to populate the edit object form, you may override
the `form_edit_query` function with your own SQLAlchemy query. In the following example, overriding
the default query will allow you to filter relationships to show only related children of the parent.

```py
class ParentAdmin(ModelView, model=Parent):
def form_edit_query(self, request: Request) -> Select:
parent_id = request.path_params["pk"]
return (
self._stmt_by_identifier(parent_id)
.join(Child)
.options(contains_eager(Parent.children))
.filter(Child.parent_id == parent_id)
)
```
2 changes: 1 addition & 1 deletion docs/cookbook/using_wysiwyg.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Post(Base):
- First create a `templates` directory in your project.
- Then add a file `custom_edit.html` there with the following content:
```html title="custom_edit.html"
{% extends "edit.html" %}
{% extends "sqladmin/edit.html" %}
{% block tail %}
<script src="https://cdn.ckeditor.com/ckeditor5/39.0.1/classic/ckeditor.js"></script>
<script>
Expand Down
48 changes: 48 additions & 0 deletions docs/cookbook/working_with_passwords.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
It's a comment use-case that you have a model
with a `Password` field which needs a custom behaviour.

Let's say you have the following `User` model:

```py
class User(Base):
__tablename__ = "user"

id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(50))
hashed_password: Mapped[str] = mapped_column(String)
```

In this specific case we want the following features to be available in the Admin console:

- We only want `hashed_password` when creating a new `User`, and we want to hide it when editing a `User`.
- When a User is created, the password should be hashed and stored in the `hashed_password` column.

So we define the following `UserAdmin` class for it:

```py
class UserAdmin(ModelView, model=User):
column_labels = {"hashed_password": "password"}
form_create_rules = ["name", "hashed_password"]
form_edit_rules = ["name"]

async def on_model_change(self, data, model, is_created, request) -> None:
if is_created:
# Hash the password before saving into DB !
data["hashed_password"] = data["hashed_password"] + "_hashed"

```

So let's see what is happening.

The `column_labels` is just saying to rename `hashed_password` to `password` when displaying or creating a form for the `User`.

Next we have defined two extra attributes called `form_create_rules` and `form_edit_rules` which
controls how the create and edit forms are created.

In the `form_create_rules` declaration we specify we want `name` and `hashed_password` when creating a `User`.

But in `form_edit_rules` we specifically excluded `hashed_password` so we only want to edit `name` of the `User`.

And finally the last step is to hash the password before saving into the database.
There could be a few options to do this, but in this case we are overriding `on_model_change` and only hashing the password
when we are creating a `User`.
35 changes: 25 additions & 10 deletions docs/working_with_templates.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
The template uses `Jinja2` template engine and by default looks for a `templates` directory in your project.
The template uses `Jinja2` template engine and by default looks for a `templates` directory in your project. As the first step you should create a `templates` directory in you project.

If your `templates` directory has the default template files like `list.html` or `create.html` then they wiill be used.
Otherwise you can create custom templates and use them.

## Customizing templates

As the first step you should create a `templates` directory in you project.
## Customizing specific templates

Since `Jinja2` is modular, you can override your specific template file and do your changes.
For example you can create a `custom_details.html` file which overrides the `details.html` from
For example you can create a `custom_details.html` file which overrides the `sqladmin/details.html` from
SQLAdmin and in the `content` block it adds custom HTML tags:

!!! example

```html title="custom_details.html"
{% extends "details.html" %}
{% extends "sqladmin/details.html" %}
{% block content %}
{{ super() }}
<p>Custom HTML</p>
Expand All @@ -26,6 +21,26 @@ SQLAdmin and in the `content` block it adds custom HTML tags:
details_template = "custom_details.html"
```

## Overriding default templates

If you need to change one of the existing default templates in SQLAdmin such that it affects multiple pages, you can do so by copying the existing template from `templates/sqladmin` into your `templates/sqladmin` directory. It will then be used instead of the one in the package. For example if there is some Javascript you want to run on every page you may want to do it in layout.html like so:

!!! example

```html title="myproject/templates/sqladmin/layout.html"
...
</div>
</div>
{% endblock %}

{% block tail %}
<script type="text/javascript">
console.log('hello world');
</script>
{% endblock %}

```

## Customizing Jinja2 environment

You can add custom environment options to use it on your custom templates. First set up a project:
Expand Down Expand Up @@ -90,7 +105,7 @@ Usage in templates:
```python
def value_is_filepath(value: Any) -> bool:
return isinstance(value, str) and os.path.isfile(value)

admin.templates.env.globals["value_is_filepath"] = value_is_filepath
```

Expand Down
2 changes: 1 addition & 1 deletion docs/writing_custom_views.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ Next we update the `report.html` file in the `templates` directory with the foll

!!! example
```html
{% extends "layout.html" %}
{% extends "sqladmin/layout.html" %}
{% block content %}
<div class="col-12">
<div class="card">
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ nav:
- Using a request object: "cookbook/using_request_object.md"
- Multiple databases: "cookbook/multiple_databases.md"
- Using rich text editor: "cookbook/using_wysiwyg.md"
- Working with Passwords: "cookbook/working_with_passwords.md"
- API Reference:
- Application: "api_reference/application.md"
- ModelView: "api_reference/model_view.md"
Expand Down
2 changes: 1 addition & 1 deletion sqladmin/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from sqladmin.application import Admin, action, expose
from sqladmin.models import BaseView, ModelView

__version__ = "0.16.1"
__version__ = "0.18.0"

__all__ = [
"Admin",
Expand Down
20 changes: 11 additions & 9 deletions sqladmin/_menu.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from typing import TYPE_CHECKING, List, Optional, Union
from __future__ import annotations

from typing import TYPE_CHECKING

from starlette.datastructures import URL
from starlette.requests import Request
Expand All @@ -8,11 +10,11 @@


class ItemMenu:
def __init__(self, name: str, icon: Optional[str] = None) -> None:
def __init__(self, name: str, icon: str | None = None) -> None:
self.name = name
self.icon = icon
self.parent: Optional["ItemMenu"] = None
self.children: List["ItemMenu"] = []
self.parent: "ItemMenu" | None = None
self.children: list["ItemMenu"] = []

def add_child(self, item: "ItemMenu") -> None:
item.parent = self
Expand All @@ -27,7 +29,7 @@ def is_accessible(self, request: Request) -> bool:
def is_active(self, request: Request) -> bool:
return False

def url(self, request: Request) -> Union[str, URL]:
def url(self, request: Request) -> str | URL:
return "#"

@property
Expand All @@ -53,9 +55,9 @@ def type_(self) -> str:
class ViewMenu(ItemMenu):
def __init__(
self,
view: Union["BaseView", "ModelView"],
view: "BaseView" | "ModelView",
name: str,
icon: Optional[str] = None,
icon: str | None = None,
) -> None:
super().__init__(name=name, icon=icon)
self.view = view
Expand All @@ -69,7 +71,7 @@ def is_accessible(self, request: Request) -> bool:
def is_active(self, request: Request) -> bool:
return self.view.identity == request.path_params.get("identity")

def url(self, request: Request) -> Union[str, URL]:
def url(self, request: Request) -> str | URL:
if self.view.is_model:
return request.url_for("admin:list", identity=self.view.identity)
return request.url_for(f"admin:{self.view.identity}")
Expand All @@ -85,7 +87,7 @@ def type_(self) -> str:

class Menu:
def __init__(self) -> None:
self.items: List[ItemMenu] = []
self.items: list[ItemMenu] = []

def add(self, item: ItemMenu) -> None:
# Only works for one-level menu
Expand Down
Loading

0 comments on commit 3e2bc8b

Please sign in to comment.