diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 14d970ef..ffd6374e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: strategy: max-parallel: 5 matrix: - python-version: [3.6, 3.7, 3.8, 3.9, '3.10'] + python-version: [3.8, 3.9, '3.10', 3.11, 3.12] env: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} diff --git a/.gitignore b/.gitignore index 189c4239..fbe6215e 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,5 @@ admindocs_templates *.sqlite3 tests/test_app/static/ .python-version +.mypy_cache +.ruff_cache diff --git a/Makefile b/Makefile index f12b83db..cdd5178c 100644 --- a/Makefile +++ b/Makefile @@ -20,13 +20,14 @@ deps: ## Install dependencies poetry install lint: check-venv ## Lint the code - @printf "$(CYAN)Auto-formatting with black$(COFF)\n" - $(environment) black jazzmin tests + @printf "$(CYAN)Auto-formatting with ruff$(COFF)\n" + $(environment) ruff format jazzmin tests + $(environment) ruff check jazzmin tests --fix check: check-venv ## Check code quality @printf "$(CYAN)Running static code analysis$(COFF)\n" - $(environment) flake8 - $(environment) black --check jazzmin tests + $(environment) ruff format --check jazzmin tests + $(environment) ruff check jazzmin tests $(environment) mypy jazzmin tests --ignore-missing-imports test: check-venv ## Run the test suite diff --git a/README.md b/README.md index f67a35f3..818e0c14 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,12 @@ # Django jazzmin (Jazzy Admin) ## Project Status + This project is being actively maintained, though with a reduced feature set, we are looking for contributors to help maintain and improve the project, please get in touch if you would like to help. Help needed with: + - Triaging issues - Frontend fixes and UI improvements - Testing @@ -17,22 +19,24 @@ them as quickly as I would like, but im trying to get through them all now, hope [![Docs](https://readthedocs.org/projects/django-jazzmin/badge/?version=latest)](https://django-jazzmin.readthedocs.io) ![PyPI download month](https://img.shields.io/pypi/dm/django-jazzmin.svg) [![PyPI version](https://badge.fury.io/py/django-jazzmin.svg)](https://pypi.python.org/pypi/django-jazzmin/) -![Python versions](https://img.shields.io/badge/python-%3E%3D3.6-brightgreen) -![Django Versions](https://img.shields.io/badge/django-%3E%3D2-brightgreen) +![Python versions](https://img.shields.io/badge/python-%3E=3.8-brightgreen) +![Django Versions](https://img.shields.io/badge/django-%3E=4.2-brightgreen) [![Coverage Status](https://coveralls.io/repos/github/farridav/django-jazzmin/badge.svg?branch=master)](https://coveralls.io/github/farridav/django-jazzmin?branch=master) Drop-in theme for django admin, that utilises AdminLTE 3 & Bootstrap 4 to make yo' admin look jazzy ## Installation -``` + +```bash pip install django-jazzmin ``` ## Documentation -See [Documentation](https://django-jazzmin.readthedocs.io) or [Test App](https://github.com/farridav/django-jazzmin/tree/master/tests/test_app/library/settings.py) +See [Documentation](https://django-jazzmin.readthedocs.io) or [Test App](https://github.com/farridav/django-jazzmin/tree/master/tests/test_app/library/settings.py) ## Features + - Drop-in admin skin, all configuration optional - Customisable side menu - Customisable top menu @@ -49,59 +53,75 @@ See [Documentation](https://django-jazzmin.readthedocs.io) or [Test App](https:/ ## Screenshots ## Dashboard + ![dashboard](https://django-jazzmin.readthedocs.io/img/dashboard.png) ## List view + ![table list](https://django-jazzmin.readthedocs.io/img/list_view.png) ## Change form templates ### Collapsed side menu + ![form page](https://django-jazzmin.readthedocs.io/img/detail_view.png) ### Expanded side menu + ![Single](https://django-jazzmin.readthedocs.io/img/changeform_single.png) ### Horizontal tabs + ![Horizontal tabs](https://django-jazzmin.readthedocs.io/img/changeform_horizontal_tabs.png) ### Vertical tabs + ![Vertical tabs](https://django-jazzmin.readthedocs.io/img/changeform_vertical_tabs.png) ### Collapsible + ![Collapsible](https://django-jazzmin.readthedocs.io/img/changeform_collapsible.png) ### Carousel + ![Carousel](https://django-jazzmin.readthedocs.io/img/changeform_carousel.png) ### Related modal + ![Related modal](https://django-jazzmin.readthedocs.io/img/related_modal_bootstrap.png) ## History page + ![form page](https://django-jazzmin.readthedocs.io/img/history_page.png) ## Login view + ![login](https://django-jazzmin.readthedocs.io/img/login.png) ## UI Customiser + ![ui_customiser](https://django-jazzmin.readthedocs.io/img/ui_customiser.png) ## Mobile layout + ![mobile](https://django-jazzmin.readthedocs.io/img/dashboard_mobile.png) ## Tablet layout + ![tablet](https://django-jazzmin.readthedocs.io/img/dashboard_tablet.png) ## Admin Docs (if installed) + ![admin_docs](https://django-jazzmin.readthedocs.io/img/admin_docs.png) ## Thanks -This was initially a Fork of https://github.com/wuyue92tree/django-adminlte-ui that we refactored so much we thought it + +This was initially a Fork of that we refactored so much we thought it deserved its own package, big thanks to @wuyue92tree for all of his initial hard work, we are still patching into that project were possible, but this project has taken a different direction. The javascript modal implementation uses some code from [django-admin-interface](https://github.com/fabiocaccamo/django-admin-interface/blob/master/admin_interface/static/admin/js/popup_response.js), so thanks to @fabiocaccamo for original work -- Based on AdminLTE 3: https://adminlte.io/ -- Using Bootstrap 4: https://getbootstrap.com/ -- Using Font Awesome 5: https://fontawesome.com/ +- Based on AdminLTE 3: +- Using Bootstrap 4: +- Using Font Awesome 5: diff --git a/docs/bugs_and_features.md b/docs/bugs_and_features.md index 451ed187..cf998a0f 100644 --- a/docs/bugs_and_features.md +++ b/docs/bugs_and_features.md @@ -27,12 +27,10 @@ We welcome new features, here is a list of guidelines to consider: 5. Ensure that all configuration is optional 6. Ensure that any new strings are translated (but ideally try to use icons, or fallback on the comprehensive translations from Django) - When making changes, see if you can achieve your goal by removing code, failing that, try changing code, failing that, add new code We prefer to use HTML first, failing that, use CSS, failing that, use JavaScript (This approach helps with maintainability) - Some useful links for feature development: - [https://adminlte.io/themes/v3/index3.html](https://adminlte.io/themes/v3/index3.html) diff --git a/docs/configuration.md b/docs/configuration.md index d88cdd7f..eb281bf4 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,9 +1,10 @@ # Configuration -To configure the general behaviour of jazzmin, you can use `JAZZMIN_SETTINGS` within your django settings, below is a +To configure the general behaviour of jazzmin, you can use `JAZZMIN_SETTINGS` within your django settings, below is a full example, with some of the more complex items explained below that. ## Full example + ```python JAZZMIN_SETTINGS = { # title of the window (Will default to current_admin_site.site_title if absent or None) @@ -153,35 +154,36 @@ JAZZMIN_SETTINGS = { You can enable the top menu by specifying `"topmenu_links"` in your `JAZZMIN_SETTINGS`, this is a list made up of one of: - - app (creates a dropdown of modeladmin links) - - model (creates a link to a modeladmin) - - url (url name, or absolute link) +- app (creates a dropdown of modeladmin links) +- model (creates a link to a modeladmin) +- url (url name, or absolute link) The top menu can be styled with the UI Customiser (See below) ## User menu -You can add links to the user menu on the top right of the screen using the `"usermenu_links"` settings key, the format -of these links is the same as with top menu (above), though submenus via "app" are not currently supported and will not + +You can add links to the user menu on the top right of the screen using the `"usermenu_links"` settings key, the format +of these links is the same as with top menu (above), though submenus via "app" are not currently supported and will not be rendered. ![User Menu](./img/user_menu.png) - + ## Side menu ![Side Menu](./img/side_menu.png) ### How its generated -The side menu gets a list of all installed apps and their models that have admin classes, and creates a tree of apps and +The side menu gets a list of all installed apps and their models that have admin classes, and creates a tree of apps and links to model admin pages. -You can omit apps, or models from this generated menu, using `hide_apps` or `hide_models` where app is like `auth` and +You can omit apps, or models from this generated menu, using `hide_apps` or `hide_models` where app is like `auth` and model is like `auth.user` -Ordering of the menu can be done using `order_with_respect_to`, which is a list of apps/models/custom links you want to +Ordering of the menu can be done using `order_with_respect_to`, which is a list of apps/models/custom links you want to base the ordering off of, it can be a full, or partial list, some examples: -``` +```python # Order the auth app before the books app, other apps will be alphabetically placed after these "order_with_respect_to": ["auth", "books"], @@ -208,9 +210,10 @@ Currently, custom links (See below) cannot be ordered ### Side menu custom links -Custom links can be added using `custom_links`, this is a dictionary of links, keyed on the app they will live under. +Custom links can be added using `custom_links`, this is a dictionary of links, keyed on the app they will live under. Example: +```python "custom_links": { "books": [{ # Any Name you like @@ -226,40 +229,48 @@ Example: "permissions": ["books.view_book"] }] }, +``` #### note + The app list you generate for the side menu, is shared with the dashboard, so any changes you make to it, will be reflected there ## Change form templates -We have a few different styles for a model admins change form controlled via the `changeform_format`, this can be applied -globally via `changeform_format`, and overriden on a per model basis using `changeform_format_overrides`, which accepts + +We have a few different styles for a model admins change form controlled via the `changeform_format`, this can be applied +globally via `changeform_format`, and overriden on a per model basis using `changeform_format_overrides`, which accepts a dictionary mapping of model names to changeform templates e.g: +```python "changeform_format": "horizontal_tabs", # override change forms on a per modeladmin basis "changeform_format_overrides": {"auth.user": "collapsible", "auth.group": "vertical_tabs"}, +``` -Will use `horizontal_tabs` throughout the admin, but use the `collapsible` template on the user model admin, and +Will use `horizontal_tabs` throughout the admin, but use the `collapsible` template on the user model admin, and `vertical_tabs` on the group model admin. -The default style is vertical tabs, *unless* you have no fieldsets and no inlines, in which case you will get the basic single form -rendered out, See [Django docs on fieldset](https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.fieldsets) +The default style is vertical tabs, *unless* you have no fieldsets and no inlines, in which case you will get the basic single form +rendered out, See [Django docs on fieldset](https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.fieldsets) on how to add fieldsets to your admin classes. See below for the different styles: ### Single page (`single`) + Render the form out in one page, including inlines, plain and simple, closest to the original Django admin change form ![Single](./img/changeform_single.png) ### Horizontal tabs (`horizontal_tabs`) -Puts all fieldsets and inlines into tab panes with horizontal nav tab controls, this is the default view for change + +Puts all fieldsets and inlines into tab panes with horizontal nav tab controls, this is the default view for change forms that have fieldsets. or an inline ![Horizontal tabs](./img/changeform_horizontal_tabs.png) ### Vertical tabs (`vertical_tabs`) + Puts each fieldset or inline in a separate pane, controlled by vertical tabs on the left hand side. Future enhancement: Allow tabs to be on the left or right @@ -267,17 +278,20 @@ Future enhancement: Allow tabs to be on the left or right ![Vertical tabs](./img/changeform_vertical_tabs.png) ### Collapsible (`collapsible`) -Puts all fieldsets and inlines in bootstrap collapsibles in an collapsible, allows many collapsibles to be open at the + +Puts all fieldsets and inlines in bootstrap collapsibles in an collapsible, allows many collapsibles to be open at the same time, the first collapsible is opened ![Collapsible](./img/changeform_collapsible.png) ### Carousel (`carousel`) + Puts fieldsets and inlines into a bootstrap carousel, and allows paginaton with previous/next buttons, as well as an indicators. ![Carousel](./img/changeform_carousel.png) ## Ordering of page content + If you want to order the sections within your pages, you can specify `jazzmin_section_order` on your model admin class e.g: ```python @@ -295,6 +309,7 @@ class BookAdmin(admin.ModelAdmin): ``` ## Filter perfomance + If your filter will contain a lot of options, like when you use M2M filter or it's a big filter itself, then rendering every option can hurt user perfomance. This is solved by providing `filter_input_length` dictionary with filter name as the key and the value will determine how much characters should be entered before rendering options. ```python @@ -314,7 +329,8 @@ class BookAdmin(admin.ModelAdmin): ``` ## Language Chooser -You can enable a language chooser dropdown using `"language_chooser": True` in your `JAZZMIN_SETTINGS`, we mainly use this for + +You can enable a language chooser dropdown using `"language_chooser": True` in your `JAZZMIN_SETTINGS`, we mainly use this for assisting with translations, but it could be of use to some people in their admin site. To make proper use of this, please ensure you have internationalisation setup properly, See [https://docs.djangoproject.com/en/3.1/topics/i18n/translation/](https://docs.djangoproject.com/en/3.1/topics/i18n/translation/) @@ -326,20 +342,21 @@ Namely: - `LOCALE_DIRS` is setup - `LANGUAGES` have been defined -See our [test app settings](https://github.com/farridav/django-jazzmin/tree/master/tests/test_app/library/settings.py) +See our [test app settings](https://github.com/farridav/django-jazzmin/tree/master/tests/test_app/library/settings.py) for a practical example. ![Language chooser](./img/language_chooser.png) ## Related Modal -Render django related popups inside a modal using `"related_modal_active": True` instead of the old popup window, + +Render django related popups inside a modal using `"related_modal_active": True` instead of the old popup window, defaults to `False` ![Related Modal](./img/related_modal_bootstrap.png) ### Adding extra actions to model's form view -Add a template for your model on your main template directory, +Add a template for your model on your main template directory, e.g [app/templates/admin/app_name/model_name/submit_line.html](https://github.com/farridav/django-jazzmin/tree/master/tests/test_app/library/books/templates/admin/loans/bookloan/submit_line.html) ```djangotemplate @@ -370,6 +387,6 @@ def response_change(self, request, obj): pass return ret ``` - + The implementation might change slightly if your wanting to perform an action on add, or delete, for those, you can override the response_add of response_delete methods instead/as well. diff --git a/docs/development.md b/docs/development.md index 0ac2dd56..bdaa979a 100644 --- a/docs/development.md +++ b/docs/development.md @@ -26,6 +26,7 @@ Run development server (with werkzeug debugger) python tests/test_app/manage.py runserver_plus ## Running the tests + Tests are run via github actions on any pull request into `master`, and are written for use with the [pytest](https://docs.pytest.org/en/latest/) framework, we should have good enough tests for you to base your own off of, though where we are lacking, feel free to contribute, but keep it clean, concise and simple, leave the magic to the wizards. @@ -36,6 +37,7 @@ with `pytest -k my_test_name` Run against all supported Python and Django Versions using `tox` ## Contribution guidelines + - Fork the project - Make a pull request against this repositories `master` branch, - Include tests unless its a trivial change @@ -44,17 +46,18 @@ Run against all supported Python and Django Versions using `tox` - No breaking changes please ## Coding guidelines + - autoformat your code with [black](https://github.com/psf/black) - When fixing something display related, please bear the following in mind: - - Try fixing the problem using HTML, else CSS, else JS - - Try removing code, else changing code, else adding code - + - Try fixing the problem using HTML, else CSS, else JS + - Try removing code, else changing code, else adding code ## Serving documentation locally -You can serve the docs locally using `mkdocs serve -a localhost:8001` and visiting [http://localhost:8001](http://localhost:8001) +You can serve the docs locally using `mkdocs serve -a localhost:8001` and visiting [http://localhost:8001](http://localhost:8001) ## Translations + Working with translations in jazzmin is, a bit unorthodox, as we are overriding djangos templates, so it looks like we have a lot of strings that need translating, but in-fact they already have translation strings in Django, heres the process for dealing with translations, though we recommend not adding new strings that need translating if possible, and use suitable iconography instead (See [Font Awesome 5.13.0 free icons](https://fontawesome.com/icons?d=gallery&m=free&v=5.0.0,5.0.1,5.0.10,5.0.11,5.0.12,5.0.13,5.0.2,5.0.3,5.0.4,5.0.5,5.0.6,5.0.7,5.0.8,5.0.9,5.1.0,5.1.1,5.2.0,5.3.0,5.3.1,5.4.0,5.4.1,5.4.2,5.13.0,5.12.0,5.11.2,5.11.1,5.10.0,5.9.0,5.8.2,5.8.1,5.7.2,5.7.1,5.7.0,5.6.3,5.5.0,5.4.2)), diff --git a/docs/index.md b/docs/index.md index 602f55c2..56981ea4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -18,52 +18,64 @@ customise, including a built-in UI customizer - Based on the latest [adminlte](https://adminlte.io/) + [bootstrap](https://getbootstrap.com/) ## Demo + You can view the demo app by cloning the repository, and running the following commands: ```bash poetry install - ./tests/test_app/manage.py migrate - ./tests/test_app/manage.py reset - ./tests/test_app/manage.py runserver_plus + ./tests/test_app/manage.py migrate + ./tests/test_app/manage.py reset + ./tests/test_app/manage.py runserver_plus ``` ## Screenshots ### Dashboard + [![dashboard](./img/dashboard.png)](./img/dashboard.png) ### List view + [![table list](./img/list_view.png)](./img/list_view.png) ### Detail view + [![form page](./img/detail_view.png)](./img/detail_view.png) ### History page + [![history page](./img/history_page.png)](./img/history_page.png) ### Modal windows + [![Modal windows](./img/related_modal_bootstrap.png)](./img/related_modal_bootstrap.png) ### Login view + [![login](./img/login.png)](./img/login.png) ### UI Customiser + [![UI Customiser](./img/ui_customiser.png)](./img/ui_customiser.png) ### Mobile layout + [![Mobile layout](./img/dashboard_mobile.png)](./img/dashboard_mobile.png) ### Tablet layout + [![Table Layout](./img/dashboard_tablet.png)](./img/dashboard_tablet.png) ### Admin docs (if installed) + [![Admin docs](./img/admin_docs.png)](./img/admin_docs.png) ## Thanks -This was initially a Fork of https://github.com/wuyue92tree/django-adminlte-ui that we refactored so much we thought it + +This was initially a Fork of that we refactored so much we thought it deserved its own package, big thanks to @wuyue92tree for all of his initial hard work, we are still patching into that project were possible, but this project is taking a slightly different direction. -- Based on AdminLTE 3: https://adminlte.io/ -- Using Bootstrap 4: https://getbootstrap.com/ -- Using Font Awesome 5: https://fontawesome.com/ +- Based on AdminLTE 3: +- Using Bootstrap 4: +- Using Font Awesome 5: diff --git a/docs/ui_customisation.md b/docs/ui_customisation.md index b545e4ab..a23e9dd5 100644 --- a/docs/ui_customisation.md +++ b/docs/ui_customisation.md @@ -1,9 +1,9 @@ # UI Tweaks -There are various things you can do to change the look and feel of your admin when using jazzmin, some are structural -changes +There are various things you can do to change the look and feel of your admin when using jazzmin, some are structural +changes -### UI Customizer +## UI Customizer Jazzmin has a built in UI configurator, mimicked + enhanced from [adminlte demo](https://adminlte.io/themes/v3/index3.html), that allows you to customise parts of the interface interactively. @@ -16,11 +16,13 @@ that allows you to customise the interface. When your happy with your customisations, press the "Show Code" button, and it will give you a code snippet to put into your settings that will persist these customisations beyond page refresh. -### Themes -With the ui customiser enabled (see above), you can try out different bootswatch themes, and combine the theme with our +## Themes + +With the ui customiser enabled (see above), you can try out different bootswatch themes, and combine the theme with our other UI tweaks. -#### Dark mode enabled +### Dark mode enabled + If you set `JAZZMIN_UI_TWEAKS["dark_mode_theme"]` to a dark theme, then users that have opted for dark mode on their device will be served this theme instead of the one in `JAZZMIN_UI_TWEAKS["theme"]` @@ -30,7 +32,7 @@ for more information on the web standard for example, to use `flatly` for all users that have no preference or prefer light mode, and `darkly` for those who opt for dark mode on their device: -``` +```python JAZZMIN_UI_TWEAKS = { ... "theme": "flatly", @@ -40,10 +42,11 @@ JAZZMIN_UI_TWEAKS = { To force the use of a single theme regardless, just omit `dark_mode_theme` from your `JAZZMIN_UI_TWEAKS` -You can preview any of the available themes on your site using the UI Customizer (See above), or view them on bootswatch +You can preview any of the available themes on your site using the UI Customizer (See above), or view them on bootswatch below -#### Light themes +### Light themes + - default (Standard theme built on top of bootstrap) - cerulean [preview](https://bootswatch.com/cerulean/) - cosmo [preview](https://bootswatch.com/cosmo/) @@ -62,7 +65,8 @@ below - united [preview](https://bootswatch.com/united/) - yeti [preview](https://bootswatch.com/yeti/) -#### Dark themes +### Dark themes + - darkly [preview](https://bootswatch.com/darkly/) - cyborg [preview](https://bootswatch.com/cyborg/) - slate [preview](https://bootswatch.com/slate/) @@ -71,59 +75,66 @@ below Here are some screenshots of the themes in action, Use the UI Customizer (See above) to test them all -#### Darkly -``` +### Darkly + +```python JAZZMIN_UI_TWEAKS = { ... "theme": "darkly", } ``` + ![icon](./img/theme_darkly.png) -#### Simplex -``` +### Simplex + +```python JAZZMIN_UI_TWEAKS = { ... "theme": "simplex", } ``` + ![icon](./img/theme_simplex.png) -#### Sketchy -``` +### Sketchy + +```python JAZZMIN_UI_TWEAKS = { ... "theme": "sketchy", } ``` + ![icon](./img/theme_sketchy.png) -#### Slate -``` +### Slate + +```python JAZZMIN_UI_TWEAKS = { ... "theme": "slate", } ``` -![icon](./img/theme_slate.png) +![icon](./img/theme_slate.png) -### DIY with custom CSS/JS +## DIY with custom CSS/JS -If there are things you need to do with CSS/JS, but want to avoid overriding the templates yourself, you can include a +If there are things you need to do with CSS/JS, but want to avoid overriding the templates yourself, you can include a custom CSS and/or JS file, just pass a relative path to your files e.g: -``` +```python "custom_css": "common/css/main.css", "custom_js": "common/js/main.js" ``` Into your jazzmin settings (Ensure these files can be found by the static file finder) -If you want to manually tweak CSS styles for a particular theme, you can start your CSS rule with +If you want to manually tweak CSS styles for a particular theme, you can start your CSS rule with `body.theme-` e.g: -``` +```css body.theme-darkly p { color: pink; } @@ -131,7 +142,7 @@ body.theme-darkly p { Or to target your `dark_mode_theme` wrap it like this: -``` +```css @media (prefers-color-scheme: dark) { body.theme-darkly p { color: pink; diff --git a/jazzmin/__init__.py b/jazzmin/__init__.py index b05aa9db..8fa96db4 100644 --- a/jazzmin/__init__.py +++ b/jazzmin/__init__.py @@ -1,4 +1,3 @@ -import django import sys # We automatically grab the version of `django-jazzmin` from the package manager instead of @@ -13,7 +12,3 @@ import pkg_resources version = pkg_resources.get_distribution("django-jazzmin").version - - -if django.VERSION < (3, 2): - default_app_config = "jazzmin.apps.JazzminConfig" diff --git a/jazzmin/settings.py b/jazzmin/settings.py index c5f7d65a..bf57f336 100644 --- a/jazzmin/settings.py +++ b/jazzmin/settings.py @@ -1,6 +1,6 @@ import copy import logging -from typing import Dict, Any +from typing import Any, Dict from django.conf import settings from django.templatetags.static import static @@ -61,7 +61,8 @@ "custom_links": {}, # Custom icons for side menu apps/models See the link below # https://fontawesome.com/icons?d=gallery&m=free&v=5.0.0,5.0.1,5.0.10,5.0.11,5.0.12,5.0.13,5.0.2,5.0.3,5.0.4,5.0.5,5.0.6,5.0.7,5.0.8,5.0.9,5.1.0, - # 5.1.1,5.2.0,5.3.0,5.3.1,5.4.0,5.4.1,5.4.2,5.13.0,5.12.0,5.11.2,5.11.1,5.10.0,5.9.0,5.8.2,5.8.1,5.7.2,5.7.1,5.7.0,5.6.3,5.5.0,5.4.2 + # 5.1.1,5.2.0,5.3.0,5.3.1,5.4.0,5.4.1,5.4.2,5.13.0,5.12.0, + # 5.11.2,5.11.1,5.10.0,5.9.0,5.8.2,5.8.1,5.7.2,5.7.1,5.7.0,5.6.3,5.5.0,5.4.2 # for the full list of 5.13.0 free icon classes "icons": {"auth": "fas fa-users-cog", "auth.user": "fas fa-user", "auth.Group": "fas fa-users"}, # Icons that are used when one is not manually specified @@ -229,11 +230,11 @@ def get_settings() -> Dict: jazzmin_settings["search_models_parsed"].append(jazzmin_search_model) # Deal with single strings in hide_apps/hide_models and make sure we lower case 'em - if type(jazzmin_settings["hide_apps"]) == str: + if isinstance(jazzmin_settings["hide_apps"], str): jazzmin_settings["hide_apps"] = [jazzmin_settings["hide_apps"]] jazzmin_settings["hide_apps"] = [x.lower() for x in jazzmin_settings["hide_apps"]] - if type(jazzmin_settings["hide_models"]) == str: + if isinstance(jazzmin_settings["hide_models"], str): jazzmin_settings["hide_models"] = [jazzmin_settings["hide_models"]] jazzmin_settings["hide_models"] = [x.lower() for x in jazzmin_settings["hide_models"]] diff --git a/jazzmin/static/admin/js/popup_response.js b/jazzmin/static/admin/js/popup_response.js index f669389c..0256abfe 100644 --- a/jazzmin/static/admin/js/popup_response.js +++ b/jazzmin/static/admin/js/popup_response.js @@ -11,10 +11,6 @@ openerRef = windowRef.parent; windowName = windowRef.name; widgetName = windowName.replace(/^(change|add|delete|lookup)_/, ''); - if (typeof(openerRef.id_to_windowname) === 'function') { - // django < 3.1 compatibility - widgetName = openerRef.id_to_windowname(widgetName); - } windowRefProxy = { name: widgetName, location: windowRef.location, @@ -42,10 +38,6 @@ if (typeof(openerRef.dismissAddRelatedObjectPopup) === 'function') { openerRef.dismissAddRelatedObjectPopup(windowRef, initData.value, initData.obj); } - else if (typeof(openerRef.dismissAddAnotherPopup) === 'function') { - // django 1.7 compatibility - openerRef.dismissAddAnotherPopup(windowRef, initData.value, initData.obj); - } break; } diff --git a/jazzmin/templatetags/jazzmin.py b/jazzmin/templatetags/jazzmin.py index d73fd6e8..aa4ff145 100644 --- a/jazzmin/templatetags/jazzmin.py +++ b/jazzmin/templatetags/jazzmin.py @@ -28,7 +28,13 @@ from .. import version from ..settings import CHANGEFORM_TEMPLATES, get_settings, get_ui_tweaks -from ..utils import get_admin_url, get_filter_id, has_fieldsets_check, make_menu, order_with_respect_to +from ..utils import ( + get_admin_url, + get_filter_id, + has_fieldsets_check, + make_menu, + order_with_respect_to, +) User = get_user_model() register = Library() @@ -118,7 +124,13 @@ def get_user_menu(user: AbstractUser, admin_site: str = "admin") -> List[Dict]: Produce the menu for the user dropdown """ options = get_settings() - return make_menu(user, options.get("usermenu_links", []), options, allow_appmenus=False, admin_site=admin_site) + return make_menu( + user, + options.get("usermenu_links", []), + options, + allow_appmenus=False, + admin_site=admin_site, + ) @register.simple_tag @@ -179,7 +191,7 @@ def get_user_avatar(user: AbstractUser) -> str: # If we find the property directly on the user model (imagefield or URLfield) avatar_field = getattr(user, avatar_field_name, None) if avatar_field: - if type(avatar_field) == str: + if isinstance(avatar_field, str): return avatar_field elif hasattr(avatar_field, "url"): return avatar_field.url @@ -208,18 +220,14 @@ def jazzmin_paginator_number(change_list: ChangeList, i: int) -> SafeText: - """.format( - link=link, disabled="disabled" if link == "#" else "" - ) + """.format(link=link, disabled="disabled" if link == "#" else "") if current_page: html_str += """
  • {num}
  • - """.format( - num=i - ) + """.format(num=i) elif spacer: html_str += """
  • @@ -233,9 +241,7 @@ def jazzmin_paginator_number(change_list: ChangeList, i: int) -> SafeText:
  • {num}
  • - """.format( - num=i, query_string=query_string, end=end - ) + """.format(num=i, query_string=query_string, end=end) if end: link = change_list.get_query_string({PAGE_VAR: change_list.page_num + 1}) if change_list.page_num < i else "#" @@ -243,9 +249,7 @@ def jazzmin_paginator_number(change_list: ChangeList, i: int) -> SafeText: - """.format( - link=link, disabled="disabled" if link == "#" else "" - ) + """.format(link=link, disabled="disabled" if link == "#" else "") return format_html(html_str) @@ -256,7 +260,7 @@ def admin_extra_filters(cl: ChangeList) -> Dict: Return the dict of used filters which is not included in list_filters form """ used_parameters = list(itertools.chain(*(s.used_parameters.keys() for s in cl.filter_specs))) - return dict((k, v) for k, v in cl.params.items() if k not in used_parameters) + return {k: v for k, v in cl.params.items() if k not in used_parameters} @register.simple_tag @@ -330,7 +334,7 @@ def get_sections( """ Get and sort all of the sections that need rendering out in a change form """ - fieldsets = [x for x in admin_form] + fieldsets = list(admin_form) # Make inlines behave like formsets for fieldset in inline_admin_formsets: @@ -457,7 +461,7 @@ def app_is_installed(app: str) -> bool: @register.simple_tag -def action_message_to_list(action: LogEntry) -> List[Dict]: +def action_message_to_list(action: LogEntry) -> List[Dict]: # noqa: C901 """ Retrieves a formatted list with all actions taken by a user given a log entry object """ @@ -528,7 +532,7 @@ def style_bold_first_word(message: str) -> SafeText: message_words[0] = "{}".format(message_words[0]) - message = " ".join([word for word in message_words]) + message = " ".join(list(message_words)) return mark_safe(message) diff --git a/jazzmin/utils.py b/jazzmin/utils.py index dd1c0ea1..c1fe8099 100644 --- a/jazzmin/utils.py +++ b/jazzmin/utils.py @@ -1,12 +1,12 @@ import logging -from typing import List, Union, Dict, Set, Callable, Any +from typing import Any, Callable, Dict, List, Set, Union from urllib.parse import urlencode from django.apps import apps from django.contrib.admin import ListFilter from django.contrib.admin.helpers import AdminForm from django.contrib.auth.models import AbstractUser -from django.db.models.base import ModelBase, Model +from django.db.models.base import Model, ModelBase from django.db.models.options import Options from django.utils.translation import gettext @@ -40,8 +40,7 @@ def get_admin_url(instance: Any, admin_site: str = "admin", from_app: bool = Fal url = "#" try: - - if type(instance) == str: + if isinstance(instance, str): app_label, model_name = instance.split(".") model_name = model_name.lower() url = reverse( @@ -165,7 +164,6 @@ def make_menu( menu = [] for link in links: - perm_matches = [] for perm in link.get("permissions", []): perm_matches.append(user.has_perm(perm)) diff --git a/poetry.lock b/poetry.lock index 35409943..c1bec694 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "appnope" @@ -49,9 +49,6 @@ files = [ {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, ] -[package.dependencies] -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} - [package.extras] cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] dev = ["attrs[tests]", "pre-commit"] @@ -71,6 +68,34 @@ files = [ {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, ] +[[package]] +name = "backports-zoneinfo" +version = "0.2.1" +description = "Backport of the standard library zoneinfo module" +optional = false +python-versions = ">=3.6" +files = [ + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:da6013fd84a690242c310d77ddb8441a559e9cb3d3d59ebac9aca1a57b2e18bc"}, + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:89a48c0d158a3cc3f654da4c2de1ceba85263fafb861b98b59040a5086259722"}, + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:1c5742112073a563c81f786e77514969acb58649bcdf6cdf0b4ed31a348d4546"}, + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win32.whl", hash = "sha256:e8236383a20872c0cdf5a62b554b27538db7fa1bbec52429d8d106effbaeca08"}, + {file = "backports.zoneinfo-0.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:8439c030a11780786a2002261569bdf362264f605dfa4d65090b64b05c9f79a7"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:f04e857b59d9d1ccc39ce2da1021d196e47234873820cbeaad210724b1ee28ac"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:17746bd546106fa389c51dbea67c8b7c8f0d14b5526a579ca6ccf5ed72c526cf"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5c144945a7752ca544b4b78c8c41544cdfaf9786f25fe5ffb10e838e19a27570"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win32.whl", hash = "sha256:e55b384612d93be96506932a786bbcde5a2db7a9e6a4bb4bffe8b733f5b9036b"}, + {file = "backports.zoneinfo-0.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a76b38c52400b762e48131494ba26be363491ac4f9a04c1b7e92483d169f6582"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:8961c0f32cd0336fb8e8ead11a1f8cd99ec07145ec2931122faaac1c8f7fd987"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e81b76cace8eda1fca50e345242ba977f9be6ae3945af8d46326d776b4cf78d1"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7b0a64cda4145548fed9efc10322770f929b944ce5cee6c0dfe0c87bf4c0c8c9"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-win32.whl", hash = "sha256:1b13e654a55cd45672cb54ed12148cd33628f672548f373963b0bff67b217328"}, + {file = "backports.zoneinfo-0.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:4a0f800587060bf8880f954dbef70de6c11bbe59c673c3d818921f042f9954a6"}, + {file = "backports.zoneinfo-0.2.1.tar.gz", hash = "sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2"}, +] + +[package.extras] +tzdata = ["tzdata"] + [[package]] name = "beautifulsoup4" version = "4.12.3" @@ -92,42 +117,6 @@ charset-normalizer = ["charset-normalizer"] html5lib = ["html5lib"] lxml = ["lxml"] -[[package]] -name = "black" -version = "22.12.0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.7" -files = [ - {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, - {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, - {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, - {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, - {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, - {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, - {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, - {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, - {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, - {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, - {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, - {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} -typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} -typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - [[package]] name = "certifi" version = "2024.2.2" @@ -251,7 +240,6 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "colorama" @@ -372,19 +360,20 @@ files = [ [[package]] name = "django" -version = "3.2.25" -description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." +version = "4.2.11" +description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "Django-3.2.25-py3-none-any.whl", hash = "sha256:a52ea7fcf280b16f7b739cec38fa6d3f8953a5456986944c3ca97e79882b4e38"}, - {file = "Django-3.2.25.tar.gz", hash = "sha256:7ca38a78654aee72378594d63e51636c04b8e28574f5505dff630895b5472777"}, + {file = "Django-4.2.11-py3-none-any.whl", hash = "sha256:ddc24a0a8280a0430baa37aff11f28574720af05888c62b7cfe71d219f4599d3"}, + {file = "Django-4.2.11.tar.gz", hash = "sha256:6e6ff3db2d8dd0c986b4eec8554c8e4f919b5c1ff62a5b4390c17aff2ed6e5c4"}, ] [package.dependencies] -asgiref = ">=3.3.2,<4" -pytz = "*" -sqlparse = ">=0.2.2" +asgiref = ">=3.6.0,<4" +"backports.zoneinfo" = {version = "*", markers = "python_version < \"3.9\""} +sqlparse = ">=0.3.1" +tzdata = {version = "*", markers = "sys_platform == \"win32\""} [package.extras] argon2 = ["argon2-cffi (>=19.1.0)"] @@ -442,7 +431,6 @@ files = [ [package.dependencies] Faker = ">=0.7.0" -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [package.extras] dev = ["Django", "Pillow", "SQLAlchemy", "coverage", "flake8", "isort", "mongoengine", "sqlalchemy-utils", "tox", "wheel (>=0.32.0)", "zest.releaser[recommended]"] @@ -462,7 +450,6 @@ files = [ [package.dependencies] python-dateutil = ">=2.4" text-unidecode = "1.3" -typing-extensions = {version = ">=3.10.0.2", markers = "python_version < \"3.8\""} [[package]] name = "filelock" @@ -479,23 +466,6 @@ files = [ docs = ["furo (>=2023.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] -[[package]] -name = "flake9" -version = "3.8.3.post2" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.4" -files = [ - {file = "flake9-3.8.3.post2-py3-none-any.whl", hash = "sha256:47dced969a802a8892740bcaa35ae07232709b2ade803c45f48dd03ccb7f825f"}, - {file = "flake9-3.8.3.post2.tar.gz", hash = "sha256:daefdbfb3d320eb215a4a52c62a4b4a027cbe11d39f5dab30df908b40fce5ba7"}, -] - -[package.dependencies] -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} -mccabe = ">=0.6.0,<0.7.0" -pycodestyle = ">=2.6.0a1,<2.7.0" -pyflakes = ">=2.2.0,<2.3.0" - [[package]] name = "ghp-import" version = "2.1.0" @@ -536,7 +506,6 @@ files = [ ] [package.dependencies] -typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] @@ -744,17 +713,6 @@ files = [ [package.dependencies] traitlets = "*" -[[package]] -name = "mccabe" -version = "0.6.1" -description = "McCabe checker, plugin for flake8" -optional = false -python-versions = "*" -files = [ - {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, - {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, -] - [[package]] name = "mergedeep" version = "1.3.4" @@ -791,7 +749,6 @@ pathspec = ">=0.11.1" platformdirs = ">=2.2.0" pyyaml = ">=5.1" pyyaml-env-tag = ">=0.1" -typing-extensions = {version = ">=3.10", markers = "python_version < \"3.8\""} watchdog = ">=2.0" [package.extras] @@ -830,7 +787,6 @@ files = [ [package.dependencies] mypy-extensions = ">=0.4.3" tomli = ">=1.1.0" -typed-ast = {version = ">=1.4.0,<2", markers = "python_version < \"3.8\""} typing-extensions = ">=3.10" [package.extras] @@ -921,9 +877,6 @@ files = [ {file = "platformdirs-4.0.0.tar.gz", hash = "sha256:cb633b2bcf10c51af60beb0ab06d2f1d69064b43abf4c185ca6b28865f3f9731"}, ] -[package.dependencies] -typing-extensions = {version = ">=4.7.1", markers = "python_version < \"3.8\""} - [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] @@ -939,9 +892,6 @@ files = [ {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, ] -[package.dependencies] -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} - [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] @@ -993,28 +943,6 @@ files = [ {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, ] -[[package]] -name = "pycodestyle" -version = "2.6.0" -description = "Python style guide checker" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "pycodestyle-2.6.0-py2.py3-none-any.whl", hash = "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367"}, - {file = "pycodestyle-2.6.0.tar.gz", hash = "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"}, -] - -[[package]] -name = "pyflakes" -version = "2.2.0" -description = "passive checker of Python programs" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "pyflakes-2.2.0-py2.py3-none-any.whl", hash = "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92"}, - {file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"}, -] - [[package]] name = "pygments" version = "2.17.2" @@ -1045,7 +973,6 @@ files = [ atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" @@ -1105,17 +1032,6 @@ files = [ [package.dependencies] six = ">=1.5" -[[package]] -name = "pytz" -version = "2024.1" -description = "World timezone definitions, modern and historical" -optional = false -python-versions = "*" -files = [ - {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, - {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, -] - [[package]] name = "pyyaml" version = "6.0.1" @@ -1211,6 +1127,32 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "ruff" +version = "0.3.4" +description = "An extremely fast Python linter and code formatter, written in Rust." +optional = false +python-versions = ">=3.7" +files = [ + {file = "ruff-0.3.4-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:60c870a7d46efcbc8385d27ec07fe534ac32f3b251e4fc44b3cbfd9e09609ef4"}, + {file = "ruff-0.3.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6fc14fa742e1d8f24910e1fff0bd5e26d395b0e0e04cc1b15c7c5e5fe5b4af91"}, + {file = "ruff-0.3.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3ee7880f653cc03749a3bfea720cf2a192e4f884925b0cf7eecce82f0ce5854"}, + {file = "ruff-0.3.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf133dd744f2470b347f602452a88e70dadfbe0fcfb5fd46e093d55da65f82f7"}, + {file = "ruff-0.3.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f3860057590e810c7ffea75669bdc6927bfd91e29b4baa9258fd48b540a4365"}, + {file = "ruff-0.3.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:986f2377f7cf12efac1f515fc1a5b753c000ed1e0a6de96747cdf2da20a1b369"}, + {file = "ruff-0.3.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fd98e85869603e65f554fdc5cddf0712e352fe6e61d29d5a6fe087ec82b76c"}, + {file = "ruff-0.3.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64abeed785dad51801b423fa51840b1764b35d6c461ea8caef9cf9e5e5ab34d9"}, + {file = "ruff-0.3.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df52972138318bc7546d92348a1ee58449bc3f9eaf0db278906eb511889c4b50"}, + {file = "ruff-0.3.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:98e98300056445ba2cc27d0b325fd044dc17fcc38e4e4d2c7711585bd0a958ed"}, + {file = "ruff-0.3.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:519cf6a0ebed244dce1dc8aecd3dc99add7a2ee15bb68cf19588bb5bf58e0488"}, + {file = "ruff-0.3.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:bb0acfb921030d00070539c038cd24bb1df73a2981e9f55942514af8b17be94e"}, + {file = "ruff-0.3.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cf187a7e7098233d0d0c71175375c5162f880126c4c716fa28a8ac418dcf3378"}, + {file = "ruff-0.3.4-py3-none-win32.whl", hash = "sha256:af27ac187c0a331e8ef91d84bf1c3c6a5dea97e912a7560ac0cef25c526a4102"}, + {file = "ruff-0.3.4-py3-none-win_amd64.whl", hash = "sha256:de0d5069b165e5a32b3c6ffbb81c350b1e3d3483347196ffdf86dc0ef9e37dd6"}, + {file = "ruff-0.3.4-py3-none-win_arm64.whl", hash = "sha256:6810563cc08ad0096b57c717bd78aeac888a1bfd38654d9113cb3dc4d3f74232"}, + {file = "ruff-0.3.4.tar.gz", hash = "sha256:f0f4484c6541a99862b693e13a151435a279b271cff20e37101116a21e2a1ad1"}, +] + [[package]] name = "setuptools" version = "68.0.0" @@ -1312,7 +1254,6 @@ files = [ [package.dependencies] colorama = {version = ">=0.4.1", markers = "platform_system == \"Windows\""} filelock = ">=3.0.0" -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} packaging = ">=14" pluggy = ">=0.12.0" py = ">=1.4.17" @@ -1340,64 +1281,25 @@ docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"] [[package]] -name = "typed-ast" -version = "1.5.5" -description = "a fork of Python 2 and 3 ast modules with type comment support" +name = "typing-extensions" +version = "4.10.0" +description = "Backported and Experimental Type Hints for Python 3.8+" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "typed_ast-1.5.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4bc1efe0ce3ffb74784e06460f01a223ac1f6ab31c6bc0376a21184bf5aabe3b"}, - {file = "typed_ast-1.5.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5f7a8c46a8b333f71abd61d7ab9255440d4a588f34a21f126bbfc95f6049e686"}, - {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:597fc66b4162f959ee6a96b978c0435bd63791e31e4f410622d19f1686d5e769"}, - {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d41b7a686ce653e06c2609075d397ebd5b969d821b9797d029fccd71fdec8e04"}, - {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5fe83a9a44c4ce67c796a1b466c270c1272e176603d5e06f6afbc101a572859d"}, - {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d5c0c112a74c0e5db2c75882a0adf3133adedcdbfd8cf7c9d6ed77365ab90a1d"}, - {file = "typed_ast-1.5.5-cp310-cp310-win_amd64.whl", hash = "sha256:e1a976ed4cc2d71bb073e1b2a250892a6e968ff02aa14c1f40eba4f365ffec02"}, - {file = "typed_ast-1.5.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c631da9710271cb67b08bd3f3813b7af7f4c69c319b75475436fcab8c3d21bee"}, - {file = "typed_ast-1.5.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b445c2abfecab89a932b20bd8261488d574591173d07827c1eda32c457358b18"}, - {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc95ffaaab2be3b25eb938779e43f513e0e538a84dd14a5d844b8f2932593d88"}, - {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61443214d9b4c660dcf4b5307f15c12cb30bdfe9588ce6158f4a005baeb167b2"}, - {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6eb936d107e4d474940469e8ec5b380c9b329b5f08b78282d46baeebd3692dc9"}, - {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e48bf27022897577d8479eaed64701ecaf0467182448bd95759883300ca818c8"}, - {file = "typed_ast-1.5.5-cp311-cp311-win_amd64.whl", hash = "sha256:83509f9324011c9a39faaef0922c6f720f9623afe3fe220b6d0b15638247206b"}, - {file = "typed_ast-1.5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:44f214394fc1af23ca6d4e9e744804d890045d1643dd7e8229951e0ef39429b5"}, - {file = "typed_ast-1.5.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:118c1ce46ce58fda78503eae14b7664163aa735b620b64b5b725453696f2a35c"}, - {file = "typed_ast-1.5.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be4919b808efa61101456e87f2d4c75b228f4e52618621c77f1ddcaae15904fa"}, - {file = "typed_ast-1.5.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:fc2b8c4e1bc5cd96c1a823a885e6b158f8451cf6f5530e1829390b4d27d0807f"}, - {file = "typed_ast-1.5.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:16f7313e0a08c7de57f2998c85e2a69a642e97cb32f87eb65fbfe88381a5e44d"}, - {file = "typed_ast-1.5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:2b946ef8c04f77230489f75b4b5a4a6f24c078be4aed241cfabe9cbf4156e7e5"}, - {file = "typed_ast-1.5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2188bc33d85951ea4ddad55d2b35598b2709d122c11c75cffd529fbc9965508e"}, - {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0635900d16ae133cab3b26c607586131269f88266954eb04ec31535c9a12ef1e"}, - {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57bfc3cf35a0f2fdf0a88a3044aafaec1d2f24d8ae8cd87c4f58d615fb5b6311"}, - {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:fe58ef6a764de7b4b36edfc8592641f56e69b7163bba9f9c8089838ee596bfb2"}, - {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d09d930c2d1d621f717bb217bf1fe2584616febb5138d9b3e8cdd26506c3f6d4"}, - {file = "typed_ast-1.5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:d40c10326893ecab8a80a53039164a224984339b2c32a6baf55ecbd5b1df6431"}, - {file = "typed_ast-1.5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fd946abf3c31fb50eee07451a6aedbfff912fcd13cf357363f5b4e834cc5e71a"}, - {file = "typed_ast-1.5.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ed4a1a42df8a3dfb6b40c3d2de109e935949f2f66b19703eafade03173f8f437"}, - {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:045f9930a1550d9352464e5149710d56a2aed23a2ffe78946478f7b5416f1ede"}, - {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:381eed9c95484ceef5ced626355fdc0765ab51d8553fec08661dce654a935db4"}, - {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bfd39a41c0ef6f31684daff53befddae608f9daf6957140228a08e51f312d7e6"}, - {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8c524eb3024edcc04e288db9541fe1f438f82d281e591c548903d5b77ad1ddd4"}, - {file = "typed_ast-1.5.5-cp38-cp38-win_amd64.whl", hash = "sha256:7f58fabdde8dcbe764cef5e1a7fcb440f2463c1bbbec1cf2a86ca7bc1f95184b"}, - {file = "typed_ast-1.5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:042eb665ff6bf020dd2243307d11ed626306b82812aba21836096d229fdc6a10"}, - {file = "typed_ast-1.5.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:622e4a006472b05cf6ef7f9f2636edc51bda670b7bbffa18d26b255269d3d814"}, - {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1efebbbf4604ad1283e963e8915daa240cb4bf5067053cf2f0baadc4d4fb51b8"}, - {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0aefdd66f1784c58f65b502b6cf8b121544680456d1cebbd300c2c813899274"}, - {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:48074261a842acf825af1968cd912f6f21357316080ebaca5f19abbb11690c8a"}, - {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:429ae404f69dc94b9361bb62291885894b7c6fb4640d561179548c849f8492ba"}, - {file = "typed_ast-1.5.5-cp39-cp39-win_amd64.whl", hash = "sha256:335f22ccb244da2b5c296e6f96b06ee9bed46526db0de38d2f0e5a6597b81155"}, - {file = "typed_ast-1.5.5.tar.gz", hash = "sha256:94282f7a354f36ef5dbce0ef3467ebf6a258e370ab33d5b40c249fa996e590dd"}, + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, ] [[package]] -name = "typing-extensions" -version = "4.7.1" -description = "Backported and Experimental Type Hints for Python 3.7+" +name = "tzdata" +version = "2024.1" +description = "Provider of IANA time zone data" optional = false -python-versions = ">=3.7" +python-versions = ">=2" files = [ - {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, - {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, + {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, + {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, ] [[package]] @@ -1431,7 +1333,6 @@ files = [ [package.dependencies] distlib = ">=0.3.7,<1" filelock = ">=3.12.2,<4" -importlib-metadata = {version = ">=6.6", markers = "python_version < \"3.8\""} platformdirs = ">=3.9.1,<5" [package.extras] @@ -1522,5 +1423,5 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" -python-versions = ">=3.7" -content-hash = "6b202bb41efb2c249a32c76378e1714b019dc9f09f69b9e8358c06867bbf509b" +python-versions = ">=3.8" +content-hash = "ed01c7e83c3ce807f24200501ccb31ec78782437a8a261e837c5aac4e6cb13cc" diff --git a/pyproject.toml b/pyproject.toml index 89b85a30..0b4d6441 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,17 +16,16 @@ classifiers = [ "Operating System :: OS Independent", "Development Status :: 4 - Beta", "Framework :: Django", - "Framework :: Django :: 2.2", - "Framework :: Django :: 3.0", - "Framework :: Django :: 3.1", + "Framework :: Django :: 4.2", + "Framework :: Django :: 5.0", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Internet :: WWW/HTTP :: WSGI", @@ -37,18 +36,13 @@ include = ["jazzmin"] exclude = ["tests", "docs"] [tool.poetry.dependencies] -python = ">=3.7" -django = ">=3" - -[tool.black] -line-length = 120 -target-version = ['py310'] +python = ">=3.8" +django = ">=4.2" [tool.poetry.group.dev.dependencies] django-debug-toolbar = "^3.2.4" # unparallelled debugging in django Werkzeug = "^2.0.2" # support for werkzeug debugger in runserver_plus django-extensions = "^3.1.5" # Django Sugar for development -flake9 = "^3.8.3" # Static code checking (with pyproject.toml support) pytest = "^6.2.5" # Test runner pytest-django = "^4.5.2" # Django integration for pytest pytest-cov = "^4.1.0" # Measure code coverage during tests @@ -62,7 +56,7 @@ factory-boy = "^3.2.1" # Factory generation mypy = "^0.931" # Type checking click = "^8.0.3" # Framework for building cli's ipdb = "^0.13.9" # ipython breakpoints -black = "^22.3.0" # Code formatting and linting (with pyproject.toml support) +ruff = "*" # linter and code formatter [tool.poetry.urls] "Bug Tracker" = "https://github.com/farridav/django-jazzmin/issues" @@ -77,13 +71,27 @@ norecursedirs = ".git .tox _resource .mypy_cache build dist docs" DJANGO_SETTINGS_MODULE = "tests.test_app.library.settings" FAIL_INVALID_TEMPLATE_VARS = 1 -[tool.flake8] +[tool.ruff] +target-version = "py38" +line-length = 120 +extend-exclude = ["migrations"] + +[tool.ruff.lint] +select = [ + "E", # PEP8 errors + "W", # PEP8 warnings + "F", # PyFlakes + "I", # isort + "C", # mccabe + "T201", # flake8-print +] + +[tool.ruff.lint.isort] +known-first-party = ["jazzmin"] +known-local-folder = ["tests"] + +[tool.ruff.lint.mccabe] max-complexity = 10 -ignore = ["C901", "W504", "W503", "E231", "E203"] -exclude = [".git", ".tox", "_resource", "migrations", "build", "dist", "docs"] -max-line-length = 120 -show-source = true -statistics = false [tool.coverage.run] omit = ["*/__init__.py", ".tox", ".mypy_cache", ".reports", ".git"] @@ -96,47 +104,46 @@ show_missing = true legacy_tox_ini = """ [tox] envlist = - django3-{python3.6,python3.7,python3.8,python3.9}, - django4-{python3.8,python3.9,python3.10}, + django4-{python3.8,python3.9,python3.10,python3.11,python3.12}, + django5-{python3.10,python3.11,python3.12}, # run one of the tests again but with coverage - coveralls-django4-python3.10, + coveralls-django4-python3.12, skipsdist = True [gh-actions] python = - 3.6: python3.6 - 3.7: python3.7 3.8: python3.8 3.9: python3.9 3.10: python3.10 + 3.11: python3.11 + 3.12: python3.12 [testenv] setenv = PYTHONPATH = {toxinidir} commands = - {envpython} -m flake8 jazzmin --max-line-length 120 + {envpython} -m ruff format --check jazzmin + {envpython} -m ruff check jazzmin {envpython} -m pytest deps = django-jazzmin # Installing self to allow automatic getting of `version` which needs `django-jazzmin` to be install to get information about the package. - flake9 pytest pytest-django pytest-cov coveralls mypy - black + ruff beautifulsoup4 factory-boy - python3.6: importlib-metadata<5.0 # Needed for compatibility reasons - https://stackoverflow.com/a/73932581 - python3.7: importlib-metadata<5.0 # Needed for compatibility reasons - https://stackoverflow.com/a/73932581 - django3: Django<4 django4: Django<5 + django5: Django<6 -[testenv:coveralls-django4-python3.10] +[testenv:coveralls-django4-python3.12] passenv = COVERALLS_REPO_TOKEN commands = mypy jazzmin --ignore-missing-imports - black --check jazzmin tests --target-version py310 --line-length 120 + ruff format --check jazzmin tests + ruff check jazzmin tests pytest coveralls """ diff --git a/tests/test_admin_views.py b/tests/test_admin_views.py index a8ca0258..7a7b9e12 100644 --- a/tests/test_admin_views.py +++ b/tests/test_admin_views.py @@ -1,6 +1,8 @@ import re + import django import pytest + from jazzmin.compat import reverse from .test_app.library.books.models import Book @@ -40,7 +42,7 @@ def test_logout(admin_client): """ url = reverse("admin:logout") - response = admin_client.get(url) + response = admin_client.post(url) templates_used = [t.name for t in response.templates] assert response.status_code == 200 @@ -138,11 +140,10 @@ def test_password_change(admin_client): "django/forms/widgets/input.html", "django/forms/widgets/attrs.html", "jazzmin/includes/ui_builder_panel.html", + "django/forms/errors/list/default.html", + "django/forms/errors/list/ul.html", } - if django.VERSION[0] == 4: - expected_templates_used.update({"django/forms/errors/list/default.html", "django/forms/errors/list/ul.html"}) - assert response.status_code == 200 assert set(templates_used) == expected_templates_used @@ -219,18 +220,12 @@ def test_detail(admin_client): "django/forms/widgets/time.html": 2, "jazzmin/includes/horizontal_tabs.html": 1, "jazzmin/includes/ui_builder_panel.html": 1, + "django/forms/div.html": 1, + "django/forms/errors/list/default.html": 2, + "admin/widgets/date.html": 3, + "django/forms/errors/list/ul.html": 56, } - if django.VERSION[0] == 4: - expected_render_counts.update( - { - "django/forms/div.html": 1, - "django/forms/errors/list/default.html": 2, - "admin/widgets/date.html": 3, - "django/forms/errors/list/ul.html": 56, - } - ) - # The number of times each template was rendered assert render_counts == expected_render_counts @@ -258,18 +253,12 @@ def test_detail(admin_client): "django/forms/widgets/time.html", "jazzmin/includes/horizontal_tabs.html", "jazzmin/includes/ui_builder_panel.html", + "django/forms/div.html", + "django/forms/errors/list/default.html", + "admin/widgets/date.html", + "django/forms/errors/list/ul.html", } - if django.VERSION[0] == 4: - expected_templates_used.update( - { - "django/forms/div.html", - "django/forms/errors/list/default.html", - "admin/widgets/date.html", - "django/forms/errors/list/ul.html", - } - ) - # The templates that were used assert set(templates_used) == expected_templates_used @@ -309,17 +298,11 @@ def test_list(admin_client): "django/forms/widgets/select_option.html": 4, "django/forms/widgets/text.html": 5, "jazzmin/includes/ui_builder_panel.html": 1, + "django/forms/div.html": 1, + "django/forms/errors/list/default.html": 6, + "django/forms/errors/list/ul.html": 6, } - if django.VERSION[0] == 4: - expected_render_counts.update( - { - "django/forms/div.html": 1, - "django/forms/errors/list/default.html": 6, - "django/forms/errors/list/ul.html": 6, - } - ) - # The number of times each template was rendered assert render_counts == expected_render_counts @@ -341,17 +324,11 @@ def test_list(admin_client): "django/forms/widgets/select_option.html", "django/forms/widgets/text.html", "jazzmin/includes/ui_builder_panel.html", + "django/forms/div.html", + "django/forms/errors/list/default.html", + "django/forms/errors/list/ul.html", } - if django.VERSION[0] == 4: - expected_templates.update( - { - "django/forms/div.html", - "django/forms/errors/list/default.html", - "django/forms/errors/list/ul.html", - } - ) - # The templates that were used assert set(templates_used) == expected_templates diff --git a/tests/test_app/library/books/admin.py b/tests/test_app/library/books/admin.py index e38b9a4d..d3135b54 100644 --- a/tests/test_app/library/books/admin.py +++ b/tests/test_app/library/books/admin.py @@ -4,6 +4,7 @@ from django.contrib.auth.models import User from django.utils.html import format_html from django.utils.timesince import timesince + from jazzmin.utils import attr from ..loans.admin import BookLoanInline diff --git a/tests/test_app/library/books/management/commands/reset.py b/tests/test_app/library/books/management/commands/reset.py index 14dbb91c..4118ad3c 100644 --- a/tests/test_app/library/books/management/commands/reset.py +++ b/tests/test_app/library/books/management/commands/reset.py @@ -1,18 +1,18 @@ from random import choice -from django.contrib.auth.models import User, Group +from django.contrib.auth.models import Group, User from django.core.management import BaseCommand -from ...models import Book, Author, Genre from ....factories import ( - BookLoanFactory, - UserFactory, AuthorFactory, BookFactory, + BookLoanFactory, GroupFactory, LibraryFactory, + UserFactory, ) -from ....loans.models import Library, BookLoan +from ....loans.models import BookLoan, Library +from ...models import Author, Book, Genre class Command(BaseCommand): diff --git a/tests/test_app/library/loans/views.py b/tests/test_app/library/loans/views.py index 794d0699..0fe9b04e 100644 --- a/tests/test_app/library/loans/views.py +++ b/tests/test_app/library/loans/views.py @@ -1,5 +1,5 @@ -from django.views.generic import TemplateView from django.contrib.admin.sites import site +from django.views.generic import TemplateView class CustomView(TemplateView): diff --git a/tests/test_app/library/settings.py b/tests/test_app/library/settings.py index c170c189..6f4897fb 100644 --- a/tests/test_app/library/settings.py +++ b/tests/test_app/library/settings.py @@ -185,7 +185,8 @@ }, # Custom icons for side menu apps/models See the link below # https://fontawesome.com/icons?d=gallery&m=free&v=5.0.0,5.0.1,5.0.10,5.0.11,5.0.12,5.0.13,5.0.2,5.0.3,5.0.4,5.0.5,5.0.6,5.0.7,5.0.8,5.0.9,5.1.0, - # 5.1.1,5.2.0,5.3.0,5.3.1,5.4.0,5.4.1,5.4.2,5.13.0,5.12.0,5.11.2,5.11.1,5.10.0,5.9.0,5.8.2,5.8.1,5.7.2,5.7.1,5.7.0,5.6.3,5.5.0,5.4.2 + # 5.1.1,5.2.0,5.3.0,5.3.1,5.4.0,5.4.1,5.4.2,5.13.0,5.12.0, + # 5.11.2,5.11.1,5.10.0,5.9.0,5.8.2,5.8.1,5.7.2,5.7.1,5.7.0,5.6.3,5.5.0,5.4.2 # for the full list of 5.13.0 free icon classes "icons": { "auth": "fas fa-users-cog", diff --git a/tests/test_customisation.py b/tests/test_customisation.py index add01f98..657c4e04 100644 --- a/tests/test_customisation.py +++ b/tests/test_customisation.py @@ -1,6 +1,7 @@ import pytest from bs4 import BeautifulSoup from django.urls import reverse + from jazzmin.settings import CHANGEFORM_TEMPLATES from jazzmin.templatetags.jazzmin import get_sections @@ -37,7 +38,7 @@ def test_update_login_logo(client, custom_jazzmin_settings): @pytest.mark.django_db -@pytest.mark.parametrize("config_value,template", [(k, v) for k, v in CHANGEFORM_TEMPLATES.items()]) +@pytest.mark.parametrize("config_value,template", list(CHANGEFORM_TEMPLATES.items())) def test_changeform_templates(config_value, template, admin_client, custom_jazzmin_settings): """ All changeform config values use the correct templates diff --git a/tests/test_templatetags.py b/tests/test_templatetags.py index 46947f28..03da7a7b 100644 --- a/tests/test_templatetags.py +++ b/tests/test_templatetags.py @@ -3,6 +3,7 @@ import pytest from django.contrib.admin.models import CHANGE, LogEntry + from jazzmin.templatetags import jazzmin diff --git a/tests/test_utils.py b/tests/test_utils.py index 2861a973..cc237de0 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,19 +1,20 @@ -from unittest.mock import patch, MagicMock, Mock +from unittest.mock import MagicMock, Mock, patch import pytest from django.db.models.functions import Upper from django.urls import reverse from jazzmin.utils import ( - order_with_respect_to, get_admin_url, + get_app_admin_urls, get_custom_url, get_model_meta, - get_app_admin_urls, get_view_permissions, + order_with_respect_to, ) -from .test_app.library.factories import BookFactory, UserFactory + from .test_app.library.books.models import Book +from .test_app.library.factories import BookFactory, UserFactory def test_order_with_respect_to():