django-tools


Namedjango-tools JSON
Version 0.54.0 PyPI version JSON
download
home_pagehttps://github.com/jedie/django-tools/
Summarymiscellaneous tools for Django based projects
upload_time2022-09-15 08:02:05
maintainer
docs_urlNone
authorJens Diemer
requires_python>=3.7,<4.0.0
licenseGPL-3.0-or-later
keywords django helpers tools utilities
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI
coveralls test coverage No coveralls.
            ![django-tools](https://raw.githubusercontent.com/jedie/django-tools/master/logo/logo.svg "django-tools")

Miscellaneous tools for django.

Look also at the siblings project: [django-cms-tools](https://github.com/jedie/django-cms-tools) (Tools/helpers around Django-CMS).

| ![PyPi](https://img.shields.io/pypi/v/django-tools?label=django-tools%20%40%20PyPi "PyPi")                                             | [https://pypi.python.org/pypi/django-tools/](https://pypi.python.org/pypi/django-tools/) |
| ![Build Status on github](https://github.com/jedie/django-tools/workflows/test/badge.svg?branch=main "Build Status on github")         | [github.com/jedie/django-tools/actions](https://github.com/jedie/django-tools/actions)   |
| ![Coverage Status on codecov.io](https://codecov.io/gh/jedie/django-tools/branch/main/graph/badge.svg "Coverage Status on codecov.io") | [codecov.io/gh/jedie/django-tools](https://codecov.io/gh/jedie/django-tools)             |
| ![Coverage Status on coveralls.io](https://coveralls.io/repos/jedie/django-tools/badge.svg "Coverage Status on coveralls.io")          | [coveralls.io/r/jedie/django-tools](https://coveralls.io/r/jedie/django-tools)           |

![Python Versions](https://img.shields.io/pypi/pyversions/django-tools "Python Versions")
![License](https://img.shields.io/pypi/l/django-tools "License")

(Logo contributed by [@reallinfo](https://github.com/reallinfo) see [#16](https://github.com/jedie/django-tools/pull/16))

## try-out

e.g.:
```
~$ git clone https://github.com/jedie/django-tools.git
~$ cd django-tools/
~/django-tools$ make install
~/django-tools$ make
help                       List all commands
install-poetry             install or update poetry
install                    install django-tools via poetry
update                     update the sources and installation
lint                       Run code formatters and linter
fix-code-style             Fix code formatting
tox-listenvs               List all tox test environments
tox                        Run pytest via tox with all environments
tox-py37                   Run pytest via tox with *python v3.7*
tox-py38                   Run pytest via tox with *python v3.8*
tox-py39                   Run pytest via tox with *python v3.9*
pytest                     Run pytest
update-test-snapshot-files Update all snapshot files (by remove and recreate all snapshot files)
publish                    Release new version to PyPi
start-dev-server           Start Django dev. server with the test project
playwright-install         Install test browser for Playwright tests
playwright-inspector       Run Playwright inspector
playwright-tests           Run only the Playwright tests
safety                     Run https://github.com/pyupio/safety
```

## existing stuff

### Serve User Media File

Serve `settings.MEDIA_ROOT` files only for allowed users.

See separate README here: [django_tools/serve_media_app](https://github.com/jedie/django-tools/tree/main/django_tools/serve_media_app)

### Mode Version Protect

Protect a model against overwriting a newer entry with an older one, by adding a auto increment version number.

See separate README here: [django_tools/model_version_protect](https://github.com/jedie/django-tools/tree/main/django_tools/model_version_protect)

### OverwriteFileSystemStorage

A django filesystem storage that will overwrite existing files and can create backups, if content changed.
usage:
```
class ExampleModel(models.Model):
    foo_file = models.FileField(
        storage=OverwriteFileSystemStorage(create_backups=True)
    )
    bar_image = models.ImageField(
        storage=OverwriteFileSystemStorage(create_backups=False)
    )
```

Backup made by appending a suffix and sequential number, e.g.:


* source....: foo.bar
* backup 1..: foo.bar.bak
* backup 2..: foo.bar.bak0
* backup 3..: foo.bar.bak1

Backup files are only made if file content changed. But at least one time!

### Django Logging utils

Put this into your settings, e.g.:
```
from django_tools.unittest_utils.logging_utils import CutPathnameLogRecordFactory, FilterAndLogWarnings

# Filter warnings and pipe them to logging system
# Warnings of external packages are displayed only once and only the file path.
warnings.showwarning = FilterAndLogWarnings()

# Adds 'cut_path' attribute on log record. So '%(cut_path)s' can be used in log formatter.
logging.setLogRecordFactory(CutPathnameLogRecordFactory(max_length=50))

LOGGING = {
    # ...
    'formatters': {
        'verbose': {
            'format': '%(levelname)8s %(cut_path)s:%(lineno)-3s %(message)s'
        },
    },
    # ...
}
```

(Activate warnings by, e.g.: `export PYTHONWARNINGS=all`)

#### ThrottledAdminEmailHandler

[ThrottledAdminEmailHandler](https://github.com/jedie/django-tools/blob/master/django_tools/log_utils/throttle_admin_email_handler.py) works similar as the origin [django.utils.log.AdminEmailHandler](https://docs.djangoproject.com/en/1.11/topics/logging/#django.utils.log.AdminEmailHandler)
but is will throttle the number of mails that can be send in a time range.
usage e.g.:
```
LOGGING = {
    # ...
    "handlers": {
        "mail_admins": {
            "level": "ERROR",
            "class": "django_tools.log_utils.throttle_admin_email_handler.ThrottledAdminEmailHandler",
            "formatter": "email",
            "min_delay_sec": 20, # << -- skip mails in this time range
        },
        # ...
    },
    # ...
}
```

### django_tools.template.loader.DebugCacheLoader

Insert template name as html comments, e.g.:
```
<!-- START 'foo/bar.html' -->
...
<!-- END 'foo/bar.html' -->
```

To use this, you must add **django_tools.template.loader.DebugCacheLoader** as template loader.

e.g.: Activate it only in DEBUG mode:
```
if DEBUG:
    TEMPLATES[0]["OPTIONS"]["loaders"] = [
        (
            "django_tools.template.loader.DebugCacheLoader", (
                'django.template.loaders.filesystem.Loader',
                'django.template.loaders.app_directories.Loader',
            )
        )
    ]
```

### send text+html mails

A helper class to send text+html mails used the django template library.

You need two template files, e.g.:


* [mail_test.txt](https://github.com/jedie/django-tools/blob/master/django_tools_test_project/django_tools_test_app/templates/mail_test.txt)
* [mail_test.html](https://github.com/jedie/django-tools/blob/master/django_tools_test_project/django_tools_test_app/templates/mail_test.html)

You have to specify the template file like this: `template_base="mail_test.{ext}"`

Send via Celery task:
```
# settings.py
SEND_MAIL_CELERY_TASK_NAME="mail:send_task"

from django_tools.mail.send_mail import SendMailCelery
SendMailCelery(
    template_base="mail_test.{ext}",
    mail_context={"foo": "first", "bar": "second"},
    subject="Only a test",
    recipient_list="foo@bar.tld"
).send()
```

Send without Celery:
```
from django_tools.mail.send_mail import SendMail
SendMail(
    template_base="mail_test.{ext}",
    mail_context={"foo": "first", "bar": "second"},
    subject="Only a test",
    recipient_list="foo@bar.tld"
).send()
```

See also the existing unittests:


* [django_tools_tests/test_email.py](https://github.com/jedie/django-tools/blob/master/django_tools_tests/test_email.py)

### Delay tools

Sometimes you want to simulate when processing takes a little longer.
There exists `django_tools.debug.delay.SessionDelay` and `django_tools.debug.delay.CacheDelay` for this.
The usage will create logging entries and user messages, if user is authenticated.

More info in seperate [django_tools/debug/README.creole](https://github.com/jedie/django-tools/blob/master/django_tools/debug/README.creole) file.

### Filemanager library

Library for building django application like filemanager, gallery etc.

more info, read [./filemanager/README.creole](https://github.com/jedie/django-tools/blob/master/django_tools/filemanager/README.creole)

### per-site cache middleware

Similar to [django UpdateCacheMiddleware and FetchFromCacheMiddleware](https://docs.djangoproject.com/en/1.4/topics/cache/#the-per-site-cache),
but has some enhancements: ['per site cache' in ./cache/README.creole](https://github.com/jedie/django-tools/blob/master/django_tools/cache/README.creole#per-site-cache-middleware)

### smooth cache backends

Same as django cache backends, but adds `cache.smooth_update()` to clears the cache smoothly depend on the current system load.
more info in: ['smooth cache backends' in ./cache/README.creole](https://github.com/jedie/django-tools/blob/master/django_tools/cache/README.creole#smooth-cache-backends)

### local sync cache

Keep a local dict in a multi-threaded environment up-to-date. Usefull for cache dicts.
More info, read DocString in [./local_sync_cache/local_sync_cache.py](https://github.com/jedie/django-tools/blob/master/django_tools/local_sync_cache/local_sync_cache.py).

### threadlocals middleware

For getting request object anywhere, use [./middlewares/ThreadLocal.py](https://github.com/jedie/django-tools/blob/master/django_tools/middlewares/ThreadLocal.py)

### Dynamic SITE_ID middleware

Note: Currently not maintained! TODO: Fix unittests for all python/django version

Set settings.SITE_ID dynamically with a middleware base on the current request domain name.
Domain name alias can be specify as a simple string or as a regular expression.

more info, read [./dynamic_site/README.creole](https://github.com/jedie/django-tools/blob/master/django_tools/dynamic_site/README.creole).

### StackInfoStorage

Message storage like LegacyFallbackStorage, except, every message would have a stack info, witch is helpful, for debugging.
Stack info would only be added, if settings DEBUG or MESSAGE_DEBUG is on.
To use it, put this into your settings:
```
MESSAGE_STORAGE = "django_tools.utils.messages.StackInfoStorage"
```

More info, read DocString in [./utils/messages.py](https://github.com/jedie/django-tools/blob/master/django_tools/utils/messages.py).

### limit to usergroups

Limit something with only one field, by selecting:


* anonymous users
* staff users
* superusers
* ..all existing user groups..

More info, read DocString in [./limit_to_usergroups.py](https://github.com/jedie/django-tools/blob/master/django_tools/limit_to_usergroups.py)

### permission helpers

See [django_tools.permissions](https://github.com/jedie/django-tools/blob/master/django_tools/permissions.py)
and unittests: [django_tools_tests.test_permissions](https://github.com/jedie/django-tools/blob/master/django_tools_tests/test_permissions.py)

### form/model fields


* [Directory field](https://github.com/jedie/django-tools/blob/master/django_tools/fields/directory.py) - check if exist and if in a defined base path
* [language code field with validator](https://github.com/jedie/django-tools/blob/master/django_tools/fields/language_code.py)
* [Media Path field](https://github.com/jedie/django-tools/blob/master/django_tools/fields/media_path.py) browse existign path to select and validate input
* [sign seperated form/model field](https://github.com/jedie/django-tools/blob/master/django_tools/fields/sign_separated.py) e.g. comma seperated field
* [static path field](https://github.com/jedie/django-tools/blob/master/django_tools/fields/static_path.py)
* [url field](https://github.com/jedie/django-tools/blob/master/django_tools/fields/url.py) A flexible version of the original django form URLField

## unittests helpers

### Selenium Test Cases

There are Firefox and Chromium test cases, with and without django StaticLiveServerTestCase!

Chromium + StaticLiveServer example:
```
from django_tools.selenium.chromedriver import chromium_available
from django_tools.selenium.django import SeleniumChromiumStaticLiveServerTestCase

@unittest.skipUnless(chromium_available(), "Skip because Chromium is not available!")
class ExampleChromiumTests(SeleniumChromiumStaticLiveServerTestCase):
    def test_admin_login_page(self):
        self.driver.get(self.live_server_url + "/admin/login/")
        self.assert_equal_page_title("Log in | Django site admin")
        self.assert_in_page_source('<form action="/admin/login/" method="post" id="login-form">')
        self.assert_no_javascript_alert()
```

Firefox + StaticLiveServer example:
```
from django_tools.selenium.django import SeleniumFirefoxStaticLiveServerTestCase
from django_tools.selenium.geckodriver import firefox_available

@unittest.skipUnless(firefox_available(), "Skip because Firefox is not available!")
class ExampleFirefoxTests(SeleniumFirefoxStaticLiveServerTestCase):
    def test_admin_login_page(self):
        self.driver.get(self.live_server_url + "/admin/login/")
        self.assert_equal_page_title("Log in | Django site admin")
        self.assert_in_page_source('<form action="/admin/login/" method="post" id="login-form">')
        self.assert_no_javascript_alert()
```

Test cases without StaticLiveServer:
```
from django_tools.selenium.chromedriver import SeleniumChromiumTestCase
from django_tools.selenium.geckodriver import SeleniumFirefoxTestCase
```

See also existing unitests here:


* [/django_tools/django_tools_tests/test_unittest_selenium.py](https://github.com/jedie/django-tools/blob/master/django_tools/django_tools_tests/test_unittest_selenium.py)

#### Setup Web Drivers

Selenium test cases needs the browser and the web driver.

`SeleniumChromiumTestCase` and `SeleniumFirefoxTestCase` will automaticly install the web driver via [webdriver-manager](https://github.com/SergeyPirogov/webdriver_manager)

There is a small CLI (called `django_tools_selenium`) to check / install the web drivers, e.g.:
```
~/django-tools$ poetry run django_tools_selenium install
~/django-tools$ poetry run django_tools_selenium info
```

### Mockup utils

Create dummy PIL/django-filer images with Text, see:


* [/django_tools/unittest_utils/mockup.py](https://github.com/jedie/django-tools/blob/master/django_tools/unittest_utils/mockup.py)

usage/tests:


* [/django_tools_tests/test_mockup.py](https://github.com/jedie/django-tools/blob/master/django_tools_tests/test_mockup.py)

### Model instance unittest code generator

Generate unittest code skeletons from existing model instance. You can use this feature as django manage command or as admin action.

Usage as management command, e.g.:
```
$ ./manage.py generate_model_test_code auth.
...
#
# pk:1 from auth.User <class 'django.contrib.auth.models.User'>
#
user = User.objects.create(
    password='pbkdf2_sha256$36000$ybRfVQDOPQ9F$jwmgc5UsqRQSXxJs/NrZeTLguieUSSZfaSZbMmC+L5w=', # CharField, String (up to 128)
    last_login=datetime.datetime(2018, 4, 24, 8, 27, 49, 578107, tzinfo=<UTC>), # DateTimeField, Date (with time)
    is_superuser=True, # BooleanField, Boolean (Either True or False)
    username='test', # CharField, String (up to 150)
    first_name='', # CharField, String (up to 30)
    last_name='', # CharField, String (up to 30)
    email='', # CharField, Email address
    is_staff=True, # BooleanField, Boolean (Either True or False)
    is_active=True, # BooleanField, Boolean (Either True or False)
    date_joined=datetime.datetime(2018, 3, 6, 17, 15, 50, 93136, tzinfo=<UTC>), # DateTimeField, Date (with time)
)
...
```

### create users

[/unittest_utils/user.py](https://github.com/jedie/django-tools/blob/master/django_tools/unittest_utils/user.py):


* `django_tools.unittest_utils.user.create_user()` - create users, get_super_user
* `django_tools.unittest_utils.user.get_super_user()` - get the first existing superuser

### Isolated Filesystem decorator / context manager

[django_tools.unittest_utils.isolated_filesystem.isolated_filesystem](https://github.com/jedie/django-tools/blob/master/django_tools/unittest_utils/isolated_filesystem.py) acts as either a decorator or a context manager.
Useful to for tests that will create files/directories in current work dir, it does this:


* create a new temp directory
* change the current working directory to the temp directory
* after exit:
* Delete an entire temp directory tree

usage e.g.:
```
from django_tools.unittest_utils.isolated_filesystem import isolated_filesystem

with isolated_filesystem(prefix="temp_dir_prefix"):
    open("foo.txt", "w").write("bar")
```

### BaseUnittestCase

**django_tools.unittest_utils.unittest_base.BaseUnittestCase** contains some low-level assert methods:


* assertEqual_dedent()

Note: assert methods will be migrated to: `django_tools.unittest_utils.assertments` in the future!

_django_tools.unittest_utils.tempdir_ contains **TempDir**, a Context Manager Class:
```
with TempDir(prefix="foo_") as tempfolder:
    # create a file:
    open(os.path.join(tempfolder, "bar"), "w").close()

# the created temp folder was deleted with shutil.rmtree()
```

usage/tests:


* [/django_tools_tests/test_unittest_utils.py](https://github.com/jedie/django-tools/blob/master/django_tools_tests/test_unittest_utils.py)

### DjangoCommandMixin

Helper to run shell commands. e.g.: "./manage.py cms check" in unittests.

usage/tests:


* [/django_tools_tests/test_unittest_django_command.py](https://github.com/jedie/django-tools/blob/master/django_tools_tests/test_unittest_django_command.py)

### DOM compare in unittests

The Problem:
You can’t easy check if e.g. some form input fields are in the response,
because the form rendering use a dict for storing all html attributes.
So, the ordering of form field attributes are not sorted and varied.

The Solution:
You need to parse the response content into a DOM tree and compare nodes.

We add the great work of Gregor Müllegger at his GSoC 2011 form-rendering branch.
You will have the following assert methods inherit from: django_tools.unittest_utils.unittest_base.BaseTestCase


* self.assertHTMLEqual() – for compare two HTML DOM trees
* self.assertDOM() – for check if nodes in response or not.
* self.assertContains() – Check if ond node occurs 'count’ times in response

More info and examples in [./django_tools_tests/test_dom_asserts.py](https://github.com/jedie/django-tools/blob/master/django_tools/django_tools_tests/test_dom_asserts.py)

### @set_string_if_invalid() decorator

Helper to check if there are missing template tags by set temporary `'string_if_invalid'`, see: [https://docs.djangoproject.com/en/1.8/ref/templates/api/#invalid-template-variables](https://docs.djangoproject.com/en/1.8/ref/templates/api/#invalid-template-variables)

Usage, e.g.:
```
from django.test import SimpleTestCase
from django_tools.unittest_utils.template import TEMPLATE_INVALID_PREFIX, set_string_if_invalid

@set_string_if_invalid()
class TestMyTemplate(SimpleTestCase):
    def test_valid_tag(self):
        response = self.client.get('/foo/bar/')
        self.assertNotIn(TEMPLATE_INVALID_PREFIX, response.content)
```

You can also decorate the test method ;)

### unittest_utils/signals.py


* `SignalsContextManager` connect/disconnet signal callbacks via with statement

### unittest_utils/assertments.py

The file contains some common assert functions:


* `assert_startswith` - Check if test starts with prefix.
* `assert_endswith` - Check if text ends with suffix.
* `assert_locmem_mail_backend` - Check if current email backend is the In-memory backend.
* {{{assert_language_code() - Check if given language_code is in settings.LANGUAGES
* `assert_installed_apps()` - Check entries in settings.INSTALLED_APPS
* `assert_is_dir` - Check if given path is a directory
* `assert_is_file` - Check if given path is a file
* `assert_path_not_exists` - Check if given path doesn't exists

### Speedup tests

Speedup test run start by disable migrations, e.g.:
```
from django_tools.unittest_utils.disable_migrations import DisableMigrations
MIGRATION_MODULES = DisableMigrations()
```

### small tools

#### debug_csrf_failure()

Display the normal debug page and not the minimal csrf debug page.
More info in DocString here: [django_tools/views/csrf.py](https://github.com/jedie/django-tools/blob/master/django_tools/views/csrf.py)

#### import lib helper

additional helper to the existing `importlib`
more info in the sourcecode: [./utils/importlib.py](https://github.com/jedie/django-tools/blob/master/django_tools/utils/importlib.py)

#### http utils

Pimped HttpRequest to get some more information about a request.
More info in DocString here: [django_tools/utils/http.py](https://github.com/jedie/django-tools/blob/master/django_tools/utils/http.py)

#### @display_admin_error

Developer helper to display silent errors in ModelAdmin.list_display callables.
See: **display_admin_error** in [decorators.py](https://github.com/jedie/django-tools/blob/master/django_tools/decorators.py)

### upgrade virtualenv

A simple commandline script that calls `pip install —-upgrade XY` for every package thats installed in a virtualenv.
Simply copy/symlink it into the root directory of your virtualenv and start it.

**Note:**[Seems that this solution can't observe editables right.](https://github.com/pypa/pip/issues/319)

To use it, without installing django-tools:
```
~/$ cd goto/your_env
.../your_env/$ wget https://github.com/jedie/django-tools/raw/master/django_tools/upgrade_virtualenv.py
.../your_env/$ chmod +x upgrade_virtualenv.py
.../your_env/$ ./upgrade_virtualenv.py
```

This script will be obsolete, if [pip has a own upgrade command](https://github.com/pypa/pip/issues/59).

### django_tools.utils.url.GetDict

Similar to origin django.http.QueryDict but:


* urlencode() doesn't add "=" to empty values: "?empty" instead of "?empty="
* always mutable
* output will be sorted (easier for tests ;)

More info, see tests: [django_tools_tests/test_utils_url.py](https://github.com/jedie/django-tools/blob/master/django_tools_tests/test_utils_url.py)

#### SignedCookieStorage

Store information in signed Cookies, use **django.core.signing**.
So the cookie data can't be manipulated from the client.
Sources/examples:


* [/django_tools/utils/client_storage.py](https://github.com/jedie/django-tools/blob/master/django_tools/utils/client_storage.py)
* [/django_tools_tests/test_signed_cookie.py](https://github.com/jedie/django-tools/blob/master/django_tools_tests/test_signed_cookie.py)

### Print SQL Queries

Print the used SQL queries via context manager.

usage e.g.:
```
from django_tools.unittest_utils.print_sql import PrintQueries

# e.g. use in unittests:
class MyTests(TestCase):
    def test_foobar(self):
        with PrintQueries("Create object"):
            FooBar.objects.create("name"=foo)

# e.g. use in views:
def my_view(request):
    with PrintQueries("Create object"):
        FooBar.objects.create("name"=foo)
```

the output is like:
```
_______________________________________________________________________________
 *** Create object ***
1 - INSERT INTO "foobar" ("name")
    VALUES (foo)
-------------------------------------------------------------------------------
```

### SetRequestDebugMiddleware

middleware to add debug bool attribute to request object.
More info: [./debug/README.creole](https://github.com/jedie/django-tools/blob/master/django_tools/debug/README.creole)

### TracebackLogMiddleware

Put traceback in log by call [logging.exception()](https://docs.python.org/3/library/logging.html#logging.Logger.exception) on `process_exception()`
Activate with:
```
MIDDLEWARE_CLASSES = (
    ...
    'django_tools.middlewares.TracebackLogMiddleware.TracebackLogMiddleware',
    ...
)
```

### FnMatchIps() - Unix shell-style wildcards in INTERNAL_IPS / ALLOWED_HOSTS

settings.py e.g.:
```
from django_tools.settings_utils import FnMatchIps

INTERNAL_IPS = FnMatchIps(["127.0.0.1", "::1", "192.168.*.*", "10.0.*.*"])
ALLOWED_HOSTS = FnMatchIps(["127.0.0.1", "::1", "192.168.*.*", "10.0.*.*"])
```

### StdoutStderrBuffer()

redirect stdout + stderr to a string buffer. e.g.:
```
from django_tools.unittest_utils.stdout_redirect import StdoutStderrBuffer

with StdoutStderrBuffer() as buffer:
    print("foo")
output = buffer.get_output() # contains "foo\n"
```

### Management commands

#### permission_info

List all permissions for one django user.
(Needs `'django_tools'` in INSTALLED_APPS)

e.g.:
```
$ ./manage.py permission_info
No username given!

All existing users are:
foo, bar, john, doe

$ ./manage.py permission_info foo
All permissions for user 'test_editor':
	is_active    : yes
	is_staff     : yes
	is_superuser : no
[*] admin.add_logentry
[*] admin.change_logentry
[*] admin.delete_logentry
[ ] auth.add_group
[ ] auth.add_permission
[ ] auth.add_user
...
```

#### logging_info

Shows a list of all loggers and marks which ones are configured in settings.LOGGING:
```
$ ./manage.py logging_info
```

#### nice_diffsettings

Similar to django 'diffsettings', but used pretty-printed representation:
```
$ ./manage.py nice_diffsettings
```

#### database_info

Just display some information about the used database and connections:
```
$ ./manage.py database_info
```

#### list_models

Just list all existing models in app_label.ModelName format. Useful to use this in 'dumpdata' etc:
```
$ ./manage.py list_models
```

### ..all others…

There exist many miscellaneous stuff. Look in the source, luke!

## Backwards-incompatible changes

Old changes archived in git history here:


* [>=v0.47](https://github.com/jedie/django-tools/tree/v0.49.0#backwards-incompatible-changes)
* [>=v0.35](https://github.com/jedie/django-tools/tree/v0.35.0#backwards-incompatible-changes)

### v0.51

All Selenium helper are deprecated, please migrate to [Playwright](https://playwright.dev/python/) ;)

### v0.50

Removed old selenium helper function, [deprecated since v0.43](https://github.com/jedie/django-tools/tree/v0.43.0#v043)

Make all Selenium web driver instances persistent for the complete test run session.
This speedup tests and fixed some bugs in Selenium.

This result in the same browser/webdriver settings for all test classes!

## Django compatibility

| django-tools     | django version | python          |
| ---------------- | -------------- | --------------- |
| >= v0.52.0       | 3.2, 4.0, 4.1  | 3.8, 3.9, 3.10  |
| >= v0.50.0       | 2.2, 3.2, 4.0  | 3.8, 3.9, 3.10  |
| >= v0.49.0       | 2.2, 3.1, 3.2  | 3.7, 3.8, 3.9   |
| >= v0.47.0       | 2.2, 3.0, 3.1  | >= 3.6, pypy3   |
| >= v0.39         | 1.11, 2.0      | 3.5, 3.6, pypy3 |
| >= v0.38.1       | 1.8, 1.11      | 3.5, 3.6, pypy3 |
| >= v0.38.0       | 1.8, 1.11      | 3.5, 3.6        |
| >= v0.37.0       | 1.8, 1.11      | 3.4, 3.5        |
| >= v0.33.0       | 1.8, 1.11      | 2.7, 3.4, 3.5   |
| v0.30.1-v0.32.14 | 1.8, 1.9, 1.10 | 2.7, 3.4, 3.5   |
| v0.30            | 1.8, 1.9       | 2.7, 3.4        |
| v0.29            | 1.6 - 1.8      | 2.7, 3.4        |
| v0.26            | <=1.6          |                 |
| v0.25            | <=1.4          |                 |

(See also combinations for [tox in pyproject.toml](https://github.com/jedie/django-tools/blob/master/pyproject.toml))

## history


* *dev* - [compare v0.54.0...main](https://github.com/jedie/django-tools/compare/v0.54.0...main)
  * TBC
* [v0.54.0 - 15.09.2022](https://github.com/jedie/django-tools/compare/v0.53.0...v0.54.0)
  * Run [safety](https://github.com/pyupio/safety) in CI pipeline.
  * NEW: `SyslogHandler` for easy logging to `syslog`
  * Replace creole README with markdown
* [v0.53.0 - 18.08.2022](https://github.com/jedie/django-tools/compare/v0.52.0...v0.53.0)
  * NEW: `MassContextManagerBase`, `DenyStdWrite` + Updated: `StdoutStderrBuffer`
  * Enhance `run_testserver` command
  * Small test project setup updates
* [v0.52.0 - 17.08.2022](https://github.com/jedie/django-tools/compare/v0.51.0...v0.52.0)
  * Move `run_testserver` management command from `django_tools_test_app` to `django_tools` and polish it.
  * Restrict `AlwaysLoggedInAsSuperUserMiddleware` to the admin.
  * Test with Django 3.2, 4.0 and 4.1
* [v0.51.0 - 26.07.2022](https://github.com/jedie/django-tools/compare/v0.50.0...v0.51.0)
  * NEW: `check_editor_config()` to check editor config and line length in projects
  * DEPRECATE all Selenium helper, please migrate to Playwright ;)
  * NEW: [Playwright](https://playwright.dev/python/) base Unittest class and login helper
* [v0.50.0 - 29.05.2022](https://github.com/jedie/django-tools/compare/v0.49.0...v0.50.0)
  * Rename git "master" branch to "main"
  * Use [webdriver-manager](https://github.com/SergeyPirogov/webdriver_manager) to setup selenium web drivers
  * CHANGE: Now all web driver instances are cached and reused for the complete test run!
  * Use [darker](https://github.com/akaihola/darker) for code formatting
  * Update test matrix: Add Python 3.10 and Django v4.0 (remove Python 3.7 and Django 3.1)
* v0.49.0 - 22.11.2021 - [compare v0.48.3...v0.49.0](https://github.com/jedie/django-tools/compare/v0.48.3...v0.49.0)
  * NEW: `VersionProtectBaseModel` - Protect a model against overwriting a newer entry with an older one, by adding a auto increment version number.
  * NEW: `AlwaysLoggedInAsSuperUserMiddleware` useful for local dev server run to auto login into Django Admin
  * Enhance Selenium test helpers and [fix #21: Set chrome accept_languages in headless mode](https://github.com/jedie/django-tools/issues/21)
  * Update project: Tests, code-style etc.
* v0.48.3 - 20.12.2020 - [compare v0.47.2...v0.48.3](https://github.com/jedie/django-tools/compare/v0.47.2...v0.48.3)
  * NEW: `ImageDummy().in_memory_image_file()` useful for e.g.: POST a image upload via Django's test client
* v0.48.2 - 06.12.2020 - [compare v0.47.1...v0.48.2](https://github.com/jedie/django-tools/compare/v0.47.1...v0.48.2)
  * Bugfix "Serve User Media File": Create tokens via migrations and handle if tokens not exists
* v0.48.1 - 06.12.2020 - [compare v0.47.0...v0.48.1](https://github.com/jedie/django-tools/compare/v0.47.0...v0.48.1)
  * Add migration file to "Serve User Media File"
* v0.48.0 - 06.12.2020 - [compare v0.47.0...v0.48.0](https://github.com/jedie/django-tools/compare/v0.47.0...v0.48.0)
  * NEW: "Serve User Media File" reuseable app
  * NEW: django_tools.unittest_utils.signals.SignalsContextManager
  * Update `installed_apps_utils.get_filtered_apps()` and support app config entries in `settings.INSTALLED_APPS`
* v0.47.0 - 26.11.2020 - [compare v0.46.1...v0.47.0](https://github.com/jedie/django-tools/compare/v0.46.1...v0.47.0)
  * updates for newer django versions
  * NEW: assert_warnings(), assert_no_warnings() and assert_in_logs()
  * remove broken LoggingBuffer()
  * update project setup, fix tests and pipelines
* v0.46.1 - 19.02.2020 - [compare v0.46.0...v0.46.1](https://github.com/jedie/django-tools/compare/v0.46.0...v0.46.1)
  * less restricted dependency specification
  * NEW: `"django_tools.middlewares.LogHeaders.LogRequestHeadersMiddleware"`
  * SeleniumChromiumTestCase: set "accept_languages" and disable "headless" mode, see also: [https://github.com/jedie/django-tools/issues/21](https://github.com/jedie/django-tools/issues/21)
* v0.46.0 - 13.02.2020 - [compare v0.45.3...v0.46.0](https://github.com/jedie/django-tools/compare/v0.45.3...v0.46.0)
  * `dynamic_site` was removed. Please use e.g.: [django-hosts](https://github.com/jazzband/django-hosts)
  * modernize project setup and use `poetry`
  * remove `lxml` decency by using [bleach](https://github.com/mozilla/bleach) for `html_utils.html2text`
  * update code and code style
* v0.45.3 - 25.08.2019 - [compare v0.45.2...v0.45.3](https://github.com/jedie/django-tools/compare/v0.45.2...v0.45.3)
  * Add `excepted_exit_code` to `django_tools.unittest_utils.django_command.DjangoCommandMixin` to it's possible to test errors in manage commands
* v0.45.2 - 26.06.2019 - [compare v0.45.1...v0.45.2](https://github.com/jedie/django-tools/compare/v0.45.1...v0.45.2)
  * NEW: `django_tools.log_utils.throttle_admin_email_handler.ThrottledAdminEmailHandler`
* v0.45.1 - 03.04.2019 - [compare v0.45.0...v0.45.1](https://github.com/jedie/django-tools/compare/v0.45.0...v0.45.1)
  * Bugfix ValueError in `django_tools.unittest_utils.email.print_mailbox`
* v0.45.0 - 01.04.2019 - [compare v0.44.2...v0.45.0](https://github.com/jedie/django-tools/compare/v0.44.2...v0.45.0)
  * NEW: `OverwriteFileSystemStorage` with backup functionality
  * NEW: `print_exc_plus()` - traceback with a listing of all the local variables
  * NEW: `assert_pformat_equal` with `pprintpp` and `icdiff`
  * NEW: `assert_filenames_and_content`
* v0.44.2 - 02.01.2019 - [compare v0.44.1...v0.44.2](https://github.com/jedie/django-tools/compare/v0.44.1...v0.44.2)
  * Handle errors like: [https://github.com/andymccurdy/redis-py/issues/995](https://github.com/andymccurdy/redis-py/issues/995)
* v0.44.1 - 02.01.2019 - [compare v0.44.0...v0.44.1](https://github.com/jedie/django-tools/compare/v0.44.0...v0.44.1)
  * `LoggingBuffer`: Don't deactivate existing log handler, just append the buffer handler.
* v0.44.0 - 13.12.2018 - [compare v0.43.2...v0.44.0](https://github.com/jedie/django-tools/compare/v0.43.2...v0.44.0)
  * NEW: `django_file = ImageDummy().create_django_file_info_image(text="")` e.g.: for attach to `models.ImageField()`
  * Make `mockup.ImageDummy()` usable even if django-filer is not installed.
  * `mockup.ImageDummy()` default image format changed from png to jpeg
  * Cleanup: remove old, since v0.35 deprecated mockup image API
* v0.43.2 - 11.12.2018 - [compare v0.43.1...v0.43.2](https://github.com/jedie/django-tools/compare/v0.43.1...v0.43.2)
  * Bugfix Selenium refactor: Use the class with the same functionality if old usage places are used.
* v0.43.1 - 11.12.2018 - [compare v0.43.0...v0.43.1](https://github.com/jedie/django-tools/compare/v0.43.0...v0.43.1)
  * Bugfix: Selenium test cases: clear `window.localStorage` after test run
* v0.43.0 - 11.12.2018 - [compare v0.42.4...v0.43.0](https://github.com/jedie/django-tools/compare/v0.42.4...v0.43.0)
  * Refactor selenium helpers
  * Split selenium test cases: with and without Django StaticLiveServerTestCase
  * NEW: Selenium helper to access `window.localStorage`
* v0.42.4 - 12.10.2018 - [compare v0.42.3...v0.42.4](https://github.com/jedie/django-tools/compare/v0.42.3...v0.42.4)
  * Add `request` object to `TestUserMixin.login()` (needed for e.g.: django-axes auth backend)
* v0.42.3 - 10.10.2018 - [compare v0.42.2...v0.42.3](https://github.com/jedie/django-tools/compare/v0.42.2...v0.42.3)
  * NEW: * `assertments.assert_is_dir`, `assertments.assert_is_file`, `assertments.assert_path_not_exists`
* v0.42.2 - 18.09.2018 - [compare v0.42.1...v0.42.2](https://github.com/jedie/django-tools/compare/v0.42.1...v0.42.2)
  * NEW: `assert_installed_apps()` - Check entries in settings.INSTALLED_APPS
* v0.42.1 - 17.09.2018 - [compare v0.42.0...v0.42.1](https://github.com/jedie/django-tools/compare/v0.42.0...v0.42.1)
  * NEW: `django_tools.unittest_utils.assertments.assert_language_code` - Check if given language_code is in settings.LANGUAGES
* v0.42.0 - 07.09.2018 - [compare v0.41.0...v0.42.0](https://github.com/jedie/django-tools/compare/v0.41.0...v0.42.0)
  * remove all celery stuff
  * NEW: `$ ./manage.py logging_info` Shows a list of all loggers and marks which ones are configured in settings.LOGGING
  * manage commands `list_models` and `nice_diffsettings` are moved from seperate apps
* v0.41.0 - 28.08.2018 - [compare v0.40.6...v0.41.0](https://github.com/jedie/django-tools/compare/v0.40.6...v0.41.0)
  * NEW: `unittest_utils/assertments.py` with some common assert functions
  * Remove `@task_always_eager()` decorator
* v0.40.6 - 28.08.2018 - [compare v0.40.5...v0.40.6](https://github.com/jedie/django-tools/compare/v0.40.5...v0.40.6)
  * Bugfix `@task_always_eager()` decorator
* v0.40.5 - 27.08.2018 - [compare v0.40.4...v0.40.5](https://github.com/jedie/django-tools/compare/v0.40.4...v0.40.5)
  * Bugfix `DjangoCommandMixin.call_manage_py()`: Use the given `manage.py` filename
* v0.40.4 - 21.08.2018 - [compare v0.40.3...v0.40.4](https://github.com/jedie/django-tools/compare/v0.40.3...v0.40.4)
  * NEW: `django_tools.debug.delay` to simulate longer processing time by set a delay via GET parameter (see above)
* v0.40.3 - 18.07.2018 - [compare v0.40.2...v0.40.3](https://github.com/jedie/django-tools/compare/v0.40.2...v0.40.3)
  * Enhance selenium test cases:
    * NEW: `assert_visible_by_id()`
    * NEW: `assert_clickable_by_id()`
    * NEW: `assert_clickable_by_xpath()`
    * add `desired_capabilities` to firefox and chrome test cases
    * enable logging in chrome test case
    * NEW: `assert_in_browser_log()` in chrome test case
* v0.40.2 - 04.07.2018 - [compare v0.40.1...v0.40.2](https://github.com/jedie/django-tools/compare/v0.40.1...v0.40.2)
  * Bugfix selenium Test Case if driver is None
  * Bugfix django compatibility
* v0.40.1 - 28.06.2018 - [compare v0.40.0...v0.40.1](https://github.com/jedie/django-tools/compare/v0.40.0...v0.40.1)
  * Bugfix selenium test case if executable can't be found.
* v0.40.0 - 15.06.2018 - [compare v0.39.6...v0.40.0](https://github.com/jedie/django-tools/compare/v0.39.6...v0.40.0)
  * NEW: selenium chrome and firefox test cases in `django_tools.unittest_utils.selenium_utils`
  * Fix test project and add `run_test_project_dev_server.sh` for easy test
  * Fixing tests
* v0.39.6 - 04.05.2018 - [compare v0.39.5...v0.39.6](https://github.com/jedie/django-tools/compare/v0.39.5...v0.39.6)
  * Enhance model instance unittest code generator
* v0.39.5 - 24.04.2018 - [compare v0.39.4...v0.39.5](https://github.com/jedie/django-tools/compare/v0.39.4...v0.39.5)
  * NEW: Model instance unittest code generator (see above)
* v0.39.4 - 06.04.2018 - [compare v0.39.3...v0.39.4](https://github.com/jedie/django-tools/compare/v0.39.3...v0.39.4)
  * NEW: `django_tools.unittest_utils.logging_utils.FilterAndLogWarnings` and `django_tools.unittest_utils.logging_utils.CutPathnameLogRecordFactory`
* v0.39.3 - 22.03.2018 - [compare v0.39.2...v0.39.3](https://github.com/jedie/django-tools/compare/v0.39.2...v0.39.3)
  * `django_tools.permissions.get_filtered_permissions` has new keyword argument: `exclude_actions`
* v0.39.2 - 22.03.2018 - [compare v0.39.1...v0.39.2](https://github.com/jedie/django-tools/compare/v0.39.1...v0.39.2)
  * NEW: `django_tools.parler_utils.parler_fixtures.ParlerDummyGenerator`
  * NEW: `django_tools.fixture_tools.languages.iter_languages`
* v0.39.1 - 19.03.2018 - [compare v0.39.0...v0.39.1](https://github.com/jedie/django-tools/compare/v0.39.0...v0.39.1)
  * NEW: `django_tools.unittest_utils.email.print_mailbox()`
  * minor updates
* v0.39.0 - 02.03.2018 - [compare v0.38.9...v0.39.0](https://github.com/jedie/django-tools/compare/v0.38.9...v0.39.0)
  * NEW: Isolated Filesystem decorator / context manager
  * Backwards-incompatible change: file renamed `django_tools/unittest_utils/{celery.py => celery_utils.py`}
  * Skip run test with Django 1.8 and run tests with Django 1.11 and 2.0
* v0.38.9 - 05.02.2018 - [compare v0.38.8...v0.38.9](https://github.com/jedie/django-tools/compare/v0.38.8...v0.38.9)
  * lowering log level on missing permissions from "error" to "debug"
* v0.38.8 - 05.02.2018 - [compare v0.38.7...v0.38.8](https://github.com/jedie/django-tools/compare/v0.38.7...v0.38.8)
  * send mail: use from celery import shared_task instead of djcelery_transactions
* v0.38.7 - 15.01.2018 - [compare v0.38.6...v0.38.7](https://github.com/jedie/django-tools/compare/v0.38.6...v0.38.7)
  * Add missing arguments (like "attachments", "cc" etc.) to `django_tools.mail.send_mail.SendMailCelery`
* v0.38.6 - 10.01.2018 - [compare v0.38.4...v0.38.5](https://github.com/jedie/django-tools/compare/v0.38.4...v0.38.5)
  * NEW: `./manage.py clear_cache`
  * Display POST data in browser debug (`django_tools.unittest_utils.BrowserDebug.debug_response`)
* v0.38.5 - 02.01.2018 - [compare v0.38.4...v0.38.5](https://github.com/jedie/django-tools/compare/v0.38.4...v0.38.5)
  * NEW: Helper to assert django message framework output in unittests:
    * `BaseUnittestCase.get_messages()`: return a list of all messages
    * `BaseTestCase.assertMessages()`: compare messages
    * `BaseTestCase.assertResponse()`: has new keyword argument `messages`
  * NEW: `BaseUnittestCase.assert_exception_startswith()`
* v0.38.4 - 28.12.2017 - [compare v0.38.3...v0.38.4](https://github.com/jedie/django-tools/compare/v0.38.3...v0.38.4)
  * Bugfix attach user group on existing user in: `django_tools.unittest_utils.user.get_or_create_user`
* v0.38.3 - 28.12.2017 - [compare v0.38.2...v0.38.3](https://github.com/jedie/django-tools/compare/v0.38.2...v0.38.3)
  * Bugfix: `unittest_utils.user.get_or_create_group` also removes obsolete permissions, too.
* v0.38.2 - 27.12.2017 - [compare v0.38.1...v0.38.2](https://github.com/jedie/django-tools/compare/v0.38.1...v0.38.2)
  * NEW: `./manage.py update_permissions`
* v0.38.1 - 21.12.2017 - [compare v0.38.0...v0.38.1](https://github.com/jedie/django-tools/compare/v0.38.0...v0.38.1)
  * refactor travis/tox/pytest/coverage stuff
  * Tests can be run via `python3 setup.py tox` and/or `python3 setup.py test`
  * Test also with pypy3 on Travis CI.
* v0.38.0 - 19.12.2017 - [compare v0.37.0...v0.38.0](https://github.com/jedie/django-tools/compare/v0.37.0...v0.38.0)
  * NEW: `django_tools.unittest_utils.user.get_or_create_group`
  * NEW: `django_tools.unittest_utils.user.get_or_create_user`
  * NEW: `django_tools.unittest_utils.user.get_or_create_user_and_group`
  * NEW: `BaseUnittestCase.get_admin_change_url()` and `BaseUnittestCase.get_admin_add_url()`
  * NEW: `BaseUnittestCase.assert_startswith()` and `BaseUnittestCase.assert_endswith()`
* v0.37.0 - 11.12.2017 - [compare v0.36.0...v0.37.0](https://github.com/jedie/django-tools/compare/v0.36.0...v0.37.0)
  * Skip official support for python v2 (remove from text matrix)
  * NEW: `./manage.py permission_info`: Display a list of all permissions for one django user
  * NEW: `django_tools.permissions.get_filtered_permissions()` and `django_tools.permissions.pprint_filtered_permissions()`
  * `django_tools.settings_utils.InternalIps` was renamed to `FnMatchIps` and can be also used for **ALLOWED_HOSTS**
  * Bugfix/Enhance permission helpers
* v0.36.0 - 20.11.2017 - [compare v0.35.0...v0.36.0](https://github.com/jedie/django-tools/compare/v0.35.0...v0.36.0)
  * NEW: `./manage.py database_info`
  * Bugfix: **ModelPermissionMixin**
  * Dynamic Sites is no longer maintained and tests are deactivated. It's currently not compatible with all django versions.
* v0.35.0 - 26.09.2017 - [compare v0.34.0...v0.35.0](https://github.com/jedie/django-tools/compare/v0.34.0...v0.35.0)
  * CHANGE: The dummy image generation function in `django_tools.unittest_utils.mockup` has a new API.
* v0.34.0 - 18.09.2017 - [compare v0.33.0...v0.34.0](https://github.com/jedie/django-tools/compare/v0.33.0...v0.34.0)
  * CHANGE: The test usernames changed and spaces was replace with underscores e.g.: "staff test user" -> "staff_test_user"
  * Bugfix in mockup.create_pil_image: Created images has wrong sizes
* v0.33.0 - 11.07.2017 - [compare v0.32.14...v0.33.0](https://github.com/jedie/django-tools/compare/v0.32.14...v0.33.0)
  * Run tests only against Django v1.8 TLS and v1.11 TLS
  * For Django 1.11: Add support for new-style middleware - contributed by benkonrath
  * NEW: `django_tools.utils.request.create_fake_request()` for easier create a faked request object with `RequestFactory`
  * NEW: `django_tools.utils.html_utils.html2text()` - Strip HTML tags with lxml Cleaner + Django 'strip_tags'
* v0.32.14 - 14.06.2017 - [compare v0.32.13...v0.32.14](https://github.com/jedie/django-tools/compare/v0.32.13...v0.32.14)
  * Bugfix for Python 2: `mock` backport package is needed and added to `setup.install_requires`
* v0.32.13 - 24.05.2017 - [compare v0.32.12...v0.32.13](https://github.com/jedie/django-tools/compare/v0.32.12...v0.32.13)
  * remove some warnings
* v0.32.12 - 04.05.2017 - [compare v0.32.11...v0.32.12](https://github.com/jedie/django-tools/compare/v0.32.11...v0.32.12)
  * NEW: `self.assertIn_dedent()` in `django_tools.unittest_utils.unittest_base.BaseUnittestCase`
* v0.32.11 - 02.05.2017 - [compare v0.32.10...v0.32.11](https://github.com/jedie/django-tools/compare/v0.32.10...v0.32.11)
  * Fix PyPi package mistake (`.tar.gz` archive contains `.tox` ;)
* v0.32.10 - 02.05.2017 - [compare v0.32.9...v0.32.10](https://github.com/jedie/django-tools/compare/v0.32.9...v0.32.10)
  * NEW: `django_tools.mail` to send text+html mails (see above)
* v0.32.9 - 21.03.2017 - [compare v0.32.8...v0.32.9](https://github.com/jedie/django-tools/compare/v0.32.8...v0.32.9)
  * Bugfix `DebugCacheLoader` if TemplateDoesNotExist was raised
* v0.32.8 - 16.03.2017 - [compare v0.32.7...v0.32.8](https://github.com/jedie/django-tools/compare/v0.32.7...v0.32.8)
  * NEW: `django_tools.template.loader.DebugCacheLoader` to add template name as html comments
  * Change temp filename in BrowserDebug and use `django_tools_browserdebug_` prefix
  * Bugfix in `django_tools.middlewares.ThreadLocal.ThreadLocalMiddleware`
* v0.32.7 - 10.03.2017 - [compare v0.32.6...v0.32.7](https://github.com/jedie/django-tools/compare/v0.32.6...v0.32.7)
  * NEW: `django_tools.permissions` - helper for setup permissions
  * NEW: `/unittest_utils/user.py` - helper for creating users (needfull in unittests)
* v0.32.6 - 22.02.2017 - [compare v0.32.5...v0.32.6](https://github.com/jedie/django-tools/compare/v0.32.5...v0.32.6)
* `@task_always_eager()` decorator will set `CELERY_EAGER_PROPAGATES_EXCEPTIONS=True`, too.
* v0.32.5 - 10.02.2017 - [compare v0.32.4...v0.32.5](https://github.com/jedie/django-tools/compare/v0.32.4...v0.32.5)
  * NEW: Add `template_name` (optional) to `self.assertResponse()` (check with `assertTemplateUsed()`)
* v0.32.4 - 01.02.2017 - [compare v0.32.3...v0.32.4](https://github.com/jedie/django-tools/compare/v0.32.3...v0.32.4)
* Fix: Set "is_active" for created test users
* v0.32.3 - 25.01.2017 - [compare v0.32.2...v0.32.3](https://github.com/jedie/django-tools/compare/v0.32.2...v0.32.3)
  * Fix UnicodeDecodeError in BrowserDebug
  * NEW: `@set_string_if_invalid()` decorator
  * NEW: `@task_always_eager()` decorator
* v0.32.2 - 13.01.2017 - [compare v0.32.1...v0.32.2](https://github.com/jedie/django-tools/compare/v0.32.1...v0.32.2)
  * NEW: django_tools.utils.url.GetDict
* v0.32.1 - 29.12.2016 - [compare v0.32.0...v0.32.1](https://github.com/jedie/django-tools/compare/v0.32.0...v0.32.1)
  * NEW: TracebackLogMiddleware
* v0.32.0 - 19.12.2016 - [compare v0.31.0...v0.32.0](https://github.com/jedie/django-tools/compare/v0.31.0...v0.32.0)
  * NEW: Management commands: 'nice_diffsettings', 'list_models'
  * NEW: @display_admin_error to display silent errors in ModelAdmin.list_display callables.
  * NEW: django_tools.template.render.render_template_file
  * use [pytest-django](https://pypi.python.org/pypi/pytest-django)
  * remove outdated stuff: See 'Backwards-incompatible changes' above.
* v0.31.0 - 03.11.2016 - [compare v0.30.4...v0.31.0](https://github.com/jedie/django-tools/compare/v0.30.4...v0.31.0)
  * add Mockup utils to create dummy PIL/django-filer images with Text (see above)
  * move tests into `/django_tools_tests/`
* v0.30.4 - 27.10.2016 - [compare v0.30.2...v0.30.4](https://github.com/jedie/django-tools/compare/v0.30.2...v0.30.4)
  * add DjangoCommandMixin
* v0.30.2 - 05.10.2016 - [compare v0.30.1...v0.30.2](https://github.com/jedie/django-tools/compare/v0.30.1...v0.30.2)
  * Bugfix Python 2 compatibility
* v0.30.1 - 26.08.2016 - [compare v0.30.0...v0.30.1](https://github.com/jedie/django-tools/compare/v0.30.0...v0.30.1)
  * add: `django_tools.unittest_utils.disable_migrations.DisableMigrations` (see above)
  * run tests also with django v1.10 and Python 3.5
  * use tox
* v0.30.0 - 27.04.2016 - [compare v0.29.5...v0.30.0](https://github.com/jedie/django-tools/compare/v0.29.5...v0.30.0)
  * Django 1.9 and Python 3 support contributed by [naegelyd](https://github.com/jedie/django-tools/pull/9)
* v0.29.4 and v0.29.5 - 10.08.2015 - [compare v0.29.3...v0.29.5](https://github.com/jedie/django-tools/compare/v0.29.3...v0.29.5)
  * Some bugfixes for django 1.6 support
* v0.29.3 - 10.08.2015 - [compare v0.29.2...v0.29.3](https://github.com/jedie/django-tools/compare/v0.29.2...v0.29.3)
  * Clear ThreadLocal request atttribute after response is processed (contributed by Lucas Wiman)
* v0.29.2 - 19.06.2015 - [compare v0.29.1...v0.29.2](https://github.com/jedie/django-tools/compare/v0.29.1...v0.29.2)
  * Bugfix in unittest_utils.selenium_utils.selenium2fakes_response
  * assertResponse used assertContains from django
  * Add QueryLogMiddleware (TODO: add tests)
* v0.29.1 - 17.06.2015 - [compare v0.29.0...v0.29.1](https://github.com/jedie/django-tools/compare/v0.29.0...v0.29.1)
  * Bugfixes for Py2 and Py3
  * add StdoutStderrBuffer()
* v0.29.0 - 09.06.2015 - [compare v0.26.0...v0.29.0](https://github.com/jedie/django-tools/compare/v0.26.0...v0.29.0)
  * WIP: Refactor unittests (DocTests must be updated for Py3 and more unittests must be written to cover all)
  * catch more directory traversal attacks in BaseFilesystemBrowser (and merge code parts)
  * Bugfix for "django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet." if using **UpdateInfoBaseModel**
  * Bugfixes in **dynamic_site** for django 1.7
  * add: [django_tools.settings_utils.InternalIps](https://github.com/jedie/django-tools/blob/master/django_tools/settings_utils.py)
* v0.28.0 - 12.02.2015 - [compare v0.26.0...v0.28.0](https://github.com/jedie/django-tools/compare/v0.26.0...v0.28.0)
  * Work-a-round for import loops
  * (new Version number, because of PyPi stress)
* v0.26.0 - 11.02.2015 - [compare v0.25.1...v0.26.0](https://github.com/jedie/django-tools/compare/v0.25.1...v0.26.0)
  * Updates for Django 1.6 and Python 3
* v0.25.1 - 18.11.2013
  * Bugfix: Fall back to "UTF-8" if server send no encoding info
* v0.25.0 - 28.08.2012
  * Rename **cache.clear()** in SmoothCacheBackends to **cache.smooth_update()**, so that reset timestamp is independ from clear the cache.
* v0.24.10 - 24.08.2012
  * Add **SmoothCacheBackends**: [./cache/README.creole](https://github.com/jedie/django-tools/blob/master/django_tools/cache/README.creole)
* v0.24.9 - 24.08.2012
  * Bugfix in per-site cache middleware: set inital count values to None, if counting is disabled.
* v0.24.8 - 24.08.2012
  * Enhanced **per-site cache middleware**: [./cache/README.creole](https://github.com/jedie/django-tools/blob/master/django_tools/cache/README.creole)
  * Add **SetRequestDebugMiddleware**: [./debug/README.creole](https://github.com/jedie/django-tools/blob/master/django_tools/debug/README.creole)
* v0.24.7 - 21.08.2012
  * Add the **per-site cache middleware** (see above)
  * Add **import lib helper**: [./utils/importlib.py](https://github.com/jedie/django-tools/blob/master/django_tools/utils/importlib.py)
* v0.24.6 - 21.08.2012
  * Add the **filemanager library** (see above)
* v0.24.5 - 06.08.2012
  * Add **Print SQL Queries** context manager. (see above)
* v0.24.4 - 26.07.2012
  * remove date from version string, cause of side-effects e.g.: user clone the repo and has the filter not installed
* v0.24.3 - 25.07.2012
  * "Hardcode" the version string date attachment via [gitattribute filter script](https://github.com/jedie/python-code-snippets/tree/master/CodeSnippets/git) to fix [a reported issues](https://github.com/jedie/django-tools/issues/1) with [pip requirements file bug](https://github.com/pypa/pip/issues/145).
* v0.24.2 - 10.07.2012
  * Split [UpdateInfoBaseModel()](https://github.com/jedie/django-tools/blob/master/django_tools/models.py): So you can only set "createtime", "lastupdatetime" or "createby", "lastupdateby" or both types (This is backwards compatible)
* v0.24.1 - 12.06.2012
  * Bugfix: UsergroupsModelField() and add unittests for it
  * Add "normal users" in UsergroupsModelField()
  * New: Add create_user() and create_testusers() to BaseTestCase
  * Add a test project for the unittests. TODO: use this for all tests
* v0.24.0 - 04.06.2012
  * [Don't use auto_now_add and auto_now in UpdateInfoBaseModel](https://github.com/jedie/django-tools/commit/a3cf1f7b2e9dbe4964306f4793c74f1782f8b2ea)
  * Bugfix in [UsergroupsModelField](https://github.com/jedie/django-tools/blob/master/django_tools/limit_to_usergroups.py)
* v0.23.1
  * [Dynamic Site](https://github.com/jedie/django-tools/tree/main/django_tools/dynamic_site#dynamic-site-id) would be only initialised if settings.USE_DYNAMIC_SITE_MIDDLEWARE = True
* v0.23.0
  * Use cryptographic signing tools from django 1.4 in django_tools.utils.client_storage
* v0.22.0
  * Add [static_path.py](https://github.com/jedie/django-tools/blob/master/django_tools/fields/static_path.py) thats used settings.STATIC_ROOT.
  * The old [media_path.py](https://github.com/jedie/django-tools/blob/master/django_tools/fields/media_path.py) which used settings.MEDIA_ROOT is deprecated and will be removed in the future.
  * auto_add_check_unique_together() can use settings.DATABASES["default"]["ENGINE"], too.
* v0.21.1
  * Bugfixes in [Dynamic Site](https://github.com/jedie/django-tools/tree/main/django_tools/dynamic_site#dynamic-site-id).
* v0.21.0beta
  * New: site alias function
  * refractory 'DynamicSiteMiddleware' to a own app (**Backwards-incompatible change:** change your settings if you use the old DynamicSiteMiddleware.)
* v0.20.1
  * New: [debug_csrf_failure()](https://github.com/jedie/django-tools/blob/master/django_tools/views/csrf.py) to display the normal debug page and not the minimal csrf debug page.
* v0.20.0
  * Add experimental [DynamicSiteMiddleware](https://github.com/jedie/django-tools/blob/master/django_tools/middlewares/DynamicSite.py), please test it and give feedback.
* v0.19.6
  * Add some south introspection rules for LanguageCodeModelField and jQueryTagModelField
  * fallback if message for anonymous user can't created, because django.contrib.messages middleware not used.
  * Bugfix in django_tools.utils.messages.StackInfoStorage
* v0.19.5
  * Add [http://bugs.python.org/file22767/hp_fix.diff](http://bugs.python.org/file22767/hp_fix.diff) for [https://github.com/gregmuellegger/django/issues/1](https://github.com/gregmuellegger/django/issues/1)
* v0.19.4
  * Bugfix for PyPy in local_sync_cache get_cache_information(): sys.getsizeof() not implemented on PyPy
  * Bugfix in template.filters.chmod_symbol()
  * Nicer solution for template.filters.human_duration()
* v0.19.3
  * Add support for https in utils/http.py
* v0.19.2
  * Bugfix in utils/http.py timeout work-a-round
* v0.19.1
  * utils/http.py changes:
    * Use a better solution, see:
    * Add timeout and add a work-a-round for Python < 2.6
* v0.19.0
  * NEW: Add utils/http.py with helpers to get a webpage via http GET in unicode
  * Change README from textile to creole ;)
* v0.18.2
  * Bugfix: Add missing template in pypi package
* v0.18.0
  * NEW: Add DOM compare from Gregor Müllegger GSoC work into unittest utils.
* v0.17.1
  * Bugfix in “limit_to_usergroups”: Make choices “lazy”: Don’t access the database in _init_
* v0.17
  * Add the script “upgrade_virtualenv.py”
  * Add “limit_to_usergroups”
  * Add “local sync cache”
  * Add models.UpdateInfoBaseModel
  * Update decorators.render_to
  * render_to pass keyword arguments to render_to_response() (e.g.: mimetype=“text/plain”)
  * new argument “skip_fail” in get_filtered_apps(): If True: raise excaption if app is not importable
* v0.16.4
  * Bugfix: `get_db_prep_save() got an unexpected keyword argument 'connection’` when save a SignSeparatedModelField()
* v0.16.3
  * Update BrowserDebug: Use response.templates instead of response.template and make output nicer
* v0.16.2
  * Merge stack info code and display better stack info on browser debug page
* v0.16.1
  * Update django_tools.utils.messages.StackInfoStorage for django code changes.
* v0.16.0
  * NEW: path model field (check if direcotry exist)
* v0.15.0
  * NEW: Add a flexible URL field (own validator, model- and form-field)
* v0.14.1
  * Bugfix: make path in MediaPathModelField relativ (remove slashes)
* v0.14
  * NEW: django-tagging addon: Display existing tags under a tag field
* v0.13
  * Bugfix UnicodeEncodeError in Browser debug
* v0.12
  * NEW: django_tools.utils.messages.failsafe_message
* v0.11
  * NEW: Store data in a secure cookie, see: utils/client_storage.py
* v0.10.1
  * New: Display used templates in unittest BrowserDebug
  * Bugfix: catch if last usermessages exist
* v0.10.0
  * NEW: utils around django messages, see: /django_tools/utils/messages.py
* v0.9.1
  * Bugfix: database column was not created: don’t overwrite get_internal_type()
* v0.9
  * New: stuff in /django_tools/fields/
  * see also backwards-incompatible changes, above!
* v0.8.2
  * New: widgets.SelectMediaPath(): Select a sub directory in settings.MEDIA_ROOT
  * New: fields.SignSeparatedField()
* v0.8.1
  * Add “no_args” keyword argument to installed_apps_utils.get_filtered_apps()
* v0.8.0
  * Add model LanguageCode field and form LanguageCode field in Accept-Language header format (RFC 2616)
* v0.7.0
  * Add decorators.py
* v0.6.0
  * Add forms_utils.LimitManyToManyFields, crosspost: [http://www.djangosnippets.org/snippets/1691/](http://www.djangosnippets.org/snippets/1691/)
* v0.5.0
  * Add template/filters.py from PyLucid v0.8.x
* v0.4.0
  * Add experimental “warn_invalid_template_vars”
* v0.3.1
  * Bugfix: Exclude the instance if it was saved in the past.
* v0.3.0
  * Add utils.installed_apps_utils
* v0.2.0
  * Add models_utils, see: [http://www.jensdiemer.de/_command/118/blog/detail/67/](http://www.jensdiemer.de/_command/118/blog/detail/67/) (de)
* v0.1.0
  * first version cut out from PyLucid CMS – [http://www.pylucid.org](http://www.pylucid.org)

## links

| Homepage | [https://github.com/jedie/django-tools](https://github.com/jedie/django-tools)           |
| PyPi     | [https://pypi.python.org/pypi/django-tools/](https://pypi.python.org/pypi/django-tools/) |

## donation


* [paypal.me/JensDiemer](https://www.paypal.me/JensDiemer)
* [Flattr This!](https://flattr.com/submit/auto?uid=jedie&url=https%3A%2F%2Fgithub.com%2Fjedie%2Fdjango-tools%2F)
* Send [Bitcoins](https://www.bitcoin.org/) to [1823RZ5Md1Q2X5aSXRC5LRPcYdveCiVX6F](https://blockexplorer.com/address/1823RZ5Md1Q2X5aSXRC5LRPcYdveCiVX6F)


            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/jedie/django-tools/",
    "name": "django-tools",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.7,<4.0.0",
    "maintainer_email": "",
    "keywords": "django,helpers,tools,utilities",
    "author": "Jens Diemer",
    "author_email": "django-tools@jensdiemer.de",
    "download_url": "https://files.pythonhosted.org/packages/a5/43/8479c7e30a5bf6ddbc3c84aec45ec5a31b10e312e2c5a97524f9f287a7a9/django-tools-0.54.0.tar.gz",
    "platform": null,
    "description": "![django-tools](https://raw.githubusercontent.com/jedie/django-tools/master/logo/logo.svg \"django-tools\")\n\nMiscellaneous tools for django.\n\nLook also at the siblings project: [django-cms-tools](https://github.com/jedie/django-cms-tools) (Tools/helpers around Django-CMS).\n\n| ![PyPi](https://img.shields.io/pypi/v/django-tools?label=django-tools%20%40%20PyPi \"PyPi\")                                             | [https://pypi.python.org/pypi/django-tools/](https://pypi.python.org/pypi/django-tools/) |\n| ![Build Status on github](https://github.com/jedie/django-tools/workflows/test/badge.svg?branch=main \"Build Status on github\")         | [github.com/jedie/django-tools/actions](https://github.com/jedie/django-tools/actions)   |\n| ![Coverage Status on codecov.io](https://codecov.io/gh/jedie/django-tools/branch/main/graph/badge.svg \"Coverage Status on codecov.io\") | [codecov.io/gh/jedie/django-tools](https://codecov.io/gh/jedie/django-tools)             |\n| ![Coverage Status on coveralls.io](https://coveralls.io/repos/jedie/django-tools/badge.svg \"Coverage Status on coveralls.io\")          | [coveralls.io/r/jedie/django-tools](https://coveralls.io/r/jedie/django-tools)           |\n\n![Python Versions](https://img.shields.io/pypi/pyversions/django-tools \"Python Versions\")\n![License](https://img.shields.io/pypi/l/django-tools \"License\")\n\n(Logo contributed by [@reallinfo](https://github.com/reallinfo) see [#16](https://github.com/jedie/django-tools/pull/16))\n\n## try-out\n\ne.g.:\n```\n~$ git clone https://github.com/jedie/django-tools.git\n~$ cd django-tools/\n~/django-tools$ make install\n~/django-tools$ make\nhelp                       List all commands\ninstall-poetry             install or update poetry\ninstall                    install django-tools via poetry\nupdate                     update the sources and installation\nlint                       Run code formatters and linter\nfix-code-style             Fix code formatting\ntox-listenvs               List all tox test environments\ntox                        Run pytest via tox with all environments\ntox-py37                   Run pytest via tox with *python v3.7*\ntox-py38                   Run pytest via tox with *python v3.8*\ntox-py39                   Run pytest via tox with *python v3.9*\npytest                     Run pytest\nupdate-test-snapshot-files Update all snapshot files (by remove and recreate all snapshot files)\npublish                    Release new version to PyPi\nstart-dev-server           Start Django dev. server with the test project\nplaywright-install         Install test browser for Playwright tests\nplaywright-inspector       Run Playwright inspector\nplaywright-tests           Run only the Playwright tests\nsafety                     Run https://github.com/pyupio/safety\n```\n\n## existing stuff\n\n### Serve User Media File\n\nServe `settings.MEDIA_ROOT` files only for allowed users.\n\nSee separate README here: [django_tools/serve_media_app](https://github.com/jedie/django-tools/tree/main/django_tools/serve_media_app)\n\n### Mode Version Protect\n\nProtect a model against overwriting a newer entry with an older one, by adding a auto increment version number.\n\nSee separate README here: [django_tools/model_version_protect](https://github.com/jedie/django-tools/tree/main/django_tools/model_version_protect)\n\n### OverwriteFileSystemStorage\n\nA django filesystem storage that will overwrite existing files and can create backups, if content changed.\nusage:\n```\nclass ExampleModel(models.Model):\n    foo_file = models.FileField(\n        storage=OverwriteFileSystemStorage(create_backups=True)\n    )\n    bar_image = models.ImageField(\n        storage=OverwriteFileSystemStorage(create_backups=False)\n    )\n```\n\nBackup made by appending a suffix and sequential number, e.g.:\n\n\n* source....: foo.bar\n* backup 1..: foo.bar.bak\n* backup 2..: foo.bar.bak0\n* backup 3..: foo.bar.bak1\n\nBackup files are only made if file content changed. But at least one time!\n\n### Django Logging utils\n\nPut this into your settings, e.g.:\n```\nfrom django_tools.unittest_utils.logging_utils import CutPathnameLogRecordFactory, FilterAndLogWarnings\n\n# Filter warnings and pipe them to logging system\n# Warnings of external packages are displayed only once and only the file path.\nwarnings.showwarning = FilterAndLogWarnings()\n\n# Adds 'cut_path' attribute on log record. So '%(cut_path)s' can be used in log formatter.\nlogging.setLogRecordFactory(CutPathnameLogRecordFactory(max_length=50))\n\nLOGGING = {\n    # ...\n    'formatters': {\n        'verbose': {\n            'format': '%(levelname)8s %(cut_path)s:%(lineno)-3s %(message)s'\n        },\n    },\n    # ...\n}\n```\n\n(Activate warnings by, e.g.: `export PYTHONWARNINGS=all`)\n\n#### ThrottledAdminEmailHandler\n\n[ThrottledAdminEmailHandler](https://github.com/jedie/django-tools/blob/master/django_tools/log_utils/throttle_admin_email_handler.py) works similar as the origin [django.utils.log.AdminEmailHandler](https://docs.djangoproject.com/en/1.11/topics/logging/#django.utils.log.AdminEmailHandler)\nbut is will throttle the number of mails that can be send in a time range.\nusage e.g.:\n```\nLOGGING = {\n    # ...\n    \"handlers\": {\n        \"mail_admins\": {\n            \"level\": \"ERROR\",\n            \"class\": \"django_tools.log_utils.throttle_admin_email_handler.ThrottledAdminEmailHandler\",\n            \"formatter\": \"email\",\n            \"min_delay_sec\": 20, # << -- skip mails in this time range\n        },\n        # ...\n    },\n    # ...\n}\n```\n\n### django_tools.template.loader.DebugCacheLoader\n\nInsert template name as html comments, e.g.:\n```\n<!-- START 'foo/bar.html' -->\n...\n<!-- END 'foo/bar.html' -->\n```\n\nTo use this, you must add **django_tools.template.loader.DebugCacheLoader** as template loader.\n\ne.g.: Activate it only in DEBUG mode:\n```\nif DEBUG:\n    TEMPLATES[0][\"OPTIONS\"][\"loaders\"] = [\n        (\n            \"django_tools.template.loader.DebugCacheLoader\", (\n                'django.template.loaders.filesystem.Loader',\n                'django.template.loaders.app_directories.Loader',\n            )\n        )\n    ]\n```\n\n### send text+html mails\n\nA helper class to send text+html mails used the django template library.\n\nYou need two template files, e.g.:\n\n\n* [mail_test.txt](https://github.com/jedie/django-tools/blob/master/django_tools_test_project/django_tools_test_app/templates/mail_test.txt)\n* [mail_test.html](https://github.com/jedie/django-tools/blob/master/django_tools_test_project/django_tools_test_app/templates/mail_test.html)\n\nYou have to specify the template file like this: `template_base=\"mail_test.{ext}\"`\n\nSend via Celery task:\n```\n# settings.py\nSEND_MAIL_CELERY_TASK_NAME=\"mail:send_task\"\n\nfrom django_tools.mail.send_mail import SendMailCelery\nSendMailCelery(\n    template_base=\"mail_test.{ext}\",\n    mail_context={\"foo\": \"first\", \"bar\": \"second\"},\n    subject=\"Only a test\",\n    recipient_list=\"foo@bar.tld\"\n).send()\n```\n\nSend without Celery:\n```\nfrom django_tools.mail.send_mail import SendMail\nSendMail(\n    template_base=\"mail_test.{ext}\",\n    mail_context={\"foo\": \"first\", \"bar\": \"second\"},\n    subject=\"Only a test\",\n    recipient_list=\"foo@bar.tld\"\n).send()\n```\n\nSee also the existing unittests:\n\n\n* [django_tools_tests/test_email.py](https://github.com/jedie/django-tools/blob/master/django_tools_tests/test_email.py)\n\n### Delay tools\n\nSometimes you want to simulate when processing takes a little longer.\nThere exists `django_tools.debug.delay.SessionDelay` and `django_tools.debug.delay.CacheDelay` for this.\nThe usage will create logging entries and user messages, if user is authenticated.\n\nMore info in seperate [django_tools/debug/README.creole](https://github.com/jedie/django-tools/blob/master/django_tools/debug/README.creole) file.\n\n### Filemanager library\n\nLibrary for building django application like filemanager, gallery etc.\n\nmore info, read [./filemanager/README.creole](https://github.com/jedie/django-tools/blob/master/django_tools/filemanager/README.creole)\n\n### per-site cache middleware\n\nSimilar to [django UpdateCacheMiddleware and FetchFromCacheMiddleware](https://docs.djangoproject.com/en/1.4/topics/cache/#the-per-site-cache),\nbut has some enhancements: ['per site cache' in ./cache/README.creole](https://github.com/jedie/django-tools/blob/master/django_tools/cache/README.creole#per-site-cache-middleware)\n\n### smooth cache backends\n\nSame as django cache backends, but adds `cache.smooth_update()` to clears the cache smoothly depend on the current system load.\nmore info in: ['smooth cache backends' in ./cache/README.creole](https://github.com/jedie/django-tools/blob/master/django_tools/cache/README.creole#smooth-cache-backends)\n\n### local sync cache\n\nKeep a local dict in a multi-threaded environment up-to-date. Usefull for cache dicts.\nMore info, read DocString in [./local_sync_cache/local_sync_cache.py](https://github.com/jedie/django-tools/blob/master/django_tools/local_sync_cache/local_sync_cache.py).\n\n### threadlocals middleware\n\nFor getting request object anywhere, use [./middlewares/ThreadLocal.py](https://github.com/jedie/django-tools/blob/master/django_tools/middlewares/ThreadLocal.py)\n\n### Dynamic SITE_ID middleware\n\nNote: Currently not maintained! TODO: Fix unittests for all python/django version\n\nSet settings.SITE_ID dynamically with a middleware base on the current request domain name.\nDomain name alias can be specify as a simple string or as a regular expression.\n\nmore info, read [./dynamic_site/README.creole](https://github.com/jedie/django-tools/blob/master/django_tools/dynamic_site/README.creole).\n\n### StackInfoStorage\n\nMessage storage like LegacyFallbackStorage, except, every message would have a stack info, witch is helpful, for debugging.\nStack info would only be added, if settings DEBUG or MESSAGE_DEBUG is on.\nTo use it, put this into your settings:\n```\nMESSAGE_STORAGE = \"django_tools.utils.messages.StackInfoStorage\"\n```\n\nMore info, read DocString in [./utils/messages.py](https://github.com/jedie/django-tools/blob/master/django_tools/utils/messages.py).\n\n### limit to usergroups\n\nLimit something with only one field, by selecting:\n\n\n* anonymous users\n* staff users\n* superusers\n* ..all existing user groups..\n\nMore info, read DocString in [./limit_to_usergroups.py](https://github.com/jedie/django-tools/blob/master/django_tools/limit_to_usergroups.py)\n\n### permission helpers\n\nSee [django_tools.permissions](https://github.com/jedie/django-tools/blob/master/django_tools/permissions.py)\nand unittests: [django_tools_tests.test_permissions](https://github.com/jedie/django-tools/blob/master/django_tools_tests/test_permissions.py)\n\n### form/model fields\n\n\n* [Directory field](https://github.com/jedie/django-tools/blob/master/django_tools/fields/directory.py) - check if exist and if in a defined base path\n* [language code field with validator](https://github.com/jedie/django-tools/blob/master/django_tools/fields/language_code.py)\n* [Media Path field](https://github.com/jedie/django-tools/blob/master/django_tools/fields/media_path.py) browse existign path to select and validate input\n* [sign seperated form/model field](https://github.com/jedie/django-tools/blob/master/django_tools/fields/sign_separated.py) e.g. comma seperated field\n* [static path field](https://github.com/jedie/django-tools/blob/master/django_tools/fields/static_path.py)\n* [url field](https://github.com/jedie/django-tools/blob/master/django_tools/fields/url.py) A flexible version of the original django form URLField\n\n## unittests helpers\n\n### Selenium Test Cases\n\nThere are Firefox and Chromium test cases, with and without django StaticLiveServerTestCase!\n\nChromium + StaticLiveServer example:\n```\nfrom django_tools.selenium.chromedriver import chromium_available\nfrom django_tools.selenium.django import SeleniumChromiumStaticLiveServerTestCase\n\n@unittest.skipUnless(chromium_available(), \"Skip because Chromium is not available!\")\nclass ExampleChromiumTests(SeleniumChromiumStaticLiveServerTestCase):\n    def test_admin_login_page(self):\n        self.driver.get(self.live_server_url + \"/admin/login/\")\n        self.assert_equal_page_title(\"Log in | Django site admin\")\n        self.assert_in_page_source('<form action=\"/admin/login/\" method=\"post\" id=\"login-form\">')\n        self.assert_no_javascript_alert()\n```\n\nFirefox + StaticLiveServer example:\n```\nfrom django_tools.selenium.django import SeleniumFirefoxStaticLiveServerTestCase\nfrom django_tools.selenium.geckodriver import firefox_available\n\n@unittest.skipUnless(firefox_available(), \"Skip because Firefox is not available!\")\nclass ExampleFirefoxTests(SeleniumFirefoxStaticLiveServerTestCase):\n    def test_admin_login_page(self):\n        self.driver.get(self.live_server_url + \"/admin/login/\")\n        self.assert_equal_page_title(\"Log in | Django site admin\")\n        self.assert_in_page_source('<form action=\"/admin/login/\" method=\"post\" id=\"login-form\">')\n        self.assert_no_javascript_alert()\n```\n\nTest cases without StaticLiveServer:\n```\nfrom django_tools.selenium.chromedriver import SeleniumChromiumTestCase\nfrom django_tools.selenium.geckodriver import SeleniumFirefoxTestCase\n```\n\nSee also existing unitests here:\n\n\n* [/django_tools/django_tools_tests/test_unittest_selenium.py](https://github.com/jedie/django-tools/blob/master/django_tools/django_tools_tests/test_unittest_selenium.py)\n\n#### Setup Web Drivers\n\nSelenium test cases needs the browser and the web driver.\n\n`SeleniumChromiumTestCase` and `SeleniumFirefoxTestCase` will automaticly install the web driver via [webdriver-manager](https://github.com/SergeyPirogov/webdriver_manager)\n\nThere is a small CLI (called `django_tools_selenium`) to check / install the web drivers, e.g.:\n```\n~/django-tools$ poetry run django_tools_selenium install\n~/django-tools$ poetry run django_tools_selenium info\n```\n\n### Mockup utils\n\nCreate dummy PIL/django-filer images with Text, see:\n\n\n* [/django_tools/unittest_utils/mockup.py](https://github.com/jedie/django-tools/blob/master/django_tools/unittest_utils/mockup.py)\n\nusage/tests:\n\n\n* [/django_tools_tests/test_mockup.py](https://github.com/jedie/django-tools/blob/master/django_tools_tests/test_mockup.py)\n\n### Model instance unittest code generator\n\nGenerate unittest code skeletons from existing model instance. You can use this feature as django manage command or as admin action.\n\nUsage as management command, e.g.:\n```\n$ ./manage.py generate_model_test_code auth.\n...\n#\n# pk:1 from auth.User <class 'django.contrib.auth.models.User'>\n#\nuser = User.objects.create(\n    password='pbkdf2_sha256$36000$ybRfVQDOPQ9F$jwmgc5UsqRQSXxJs/NrZeTLguieUSSZfaSZbMmC+L5w=', # CharField, String (up to 128)\n    last_login=datetime.datetime(2018, 4, 24, 8, 27, 49, 578107, tzinfo=<UTC>), # DateTimeField, Date (with time)\n    is_superuser=True, # BooleanField, Boolean (Either True or False)\n    username='test', # CharField, String (up to 150)\n    first_name='', # CharField, String (up to 30)\n    last_name='', # CharField, String (up to 30)\n    email='', # CharField, Email address\n    is_staff=True, # BooleanField, Boolean (Either True or False)\n    is_active=True, # BooleanField, Boolean (Either True or False)\n    date_joined=datetime.datetime(2018, 3, 6, 17, 15, 50, 93136, tzinfo=<UTC>), # DateTimeField, Date (with time)\n)\n...\n```\n\n### create users\n\n[/unittest_utils/user.py](https://github.com/jedie/django-tools/blob/master/django_tools/unittest_utils/user.py):\n\n\n* `django_tools.unittest_utils.user.create_user()` - create users, get_super_user\n* `django_tools.unittest_utils.user.get_super_user()` - get the first existing superuser\n\n### Isolated Filesystem decorator / context manager\n\n[django_tools.unittest_utils.isolated_filesystem.isolated_filesystem](https://github.com/jedie/django-tools/blob/master/django_tools/unittest_utils/isolated_filesystem.py) acts as either a decorator or a context manager.\nUseful to for tests that will create files/directories in current work dir, it does this:\n\n\n* create a new temp directory\n* change the current working directory to the temp directory\n* after exit:\n* Delete an entire temp directory tree\n\nusage e.g.:\n```\nfrom django_tools.unittest_utils.isolated_filesystem import isolated_filesystem\n\nwith isolated_filesystem(prefix=\"temp_dir_prefix\"):\n    open(\"foo.txt\", \"w\").write(\"bar\")\n```\n\n### BaseUnittestCase\n\n**django_tools.unittest_utils.unittest_base.BaseUnittestCase** contains some low-level assert methods:\n\n\n* assertEqual_dedent()\n\nNote: assert methods will be migrated to: `django_tools.unittest_utils.assertments` in the future!\n\n_django_tools.unittest_utils.tempdir_ contains **TempDir**, a Context Manager Class:\n```\nwith TempDir(prefix=\"foo_\") as tempfolder:\n    # create a file:\n    open(os.path.join(tempfolder, \"bar\"), \"w\").close()\n\n# the created temp folder was deleted with shutil.rmtree()\n```\n\nusage/tests:\n\n\n* [/django_tools_tests/test_unittest_utils.py](https://github.com/jedie/django-tools/blob/master/django_tools_tests/test_unittest_utils.py)\n\n### DjangoCommandMixin\n\nHelper to run shell commands. e.g.: \"./manage.py cms check\" in unittests.\n\nusage/tests:\n\n\n* [/django_tools_tests/test_unittest_django_command.py](https://github.com/jedie/django-tools/blob/master/django_tools_tests/test_unittest_django_command.py)\n\n### DOM compare in unittests\n\nThe Problem:\nYou can\u2019t easy check if e.g. some form input fields are in the response,\nbecause the form rendering use a dict for storing all html attributes.\nSo, the ordering of form field attributes are not sorted and varied.\n\nThe Solution:\nYou need to parse the response content into a DOM tree and compare nodes.\n\nWe add the great work of Gregor M\u00fcllegger at his GSoC 2011 form-rendering branch.\nYou will have the following assert methods inherit from: django_tools.unittest_utils.unittest_base.BaseTestCase\n\n\n* self.assertHTMLEqual() \u2013 for compare two HTML DOM trees\n* self.assertDOM() \u2013 for check if nodes in response or not.\n* self.assertContains() \u2013 Check if ond node occurs 'count\u2019 times in response\n\nMore info and examples in [./django_tools_tests/test_dom_asserts.py](https://github.com/jedie/django-tools/blob/master/django_tools/django_tools_tests/test_dom_asserts.py)\n\n### @set_string_if_invalid() decorator\n\nHelper to check if there are missing template tags by set temporary `'string_if_invalid'`, see: [https://docs.djangoproject.com/en/1.8/ref/templates/api/#invalid-template-variables](https://docs.djangoproject.com/en/1.8/ref/templates/api/#invalid-template-variables)\n\nUsage, e.g.:\n```\nfrom django.test import SimpleTestCase\nfrom django_tools.unittest_utils.template import TEMPLATE_INVALID_PREFIX, set_string_if_invalid\n\n@set_string_if_invalid()\nclass TestMyTemplate(SimpleTestCase):\n    def test_valid_tag(self):\n        response = self.client.get('/foo/bar/')\n        self.assertNotIn(TEMPLATE_INVALID_PREFIX, response.content)\n```\n\nYou can also decorate the test method ;)\n\n### unittest_utils/signals.py\n\n\n* `SignalsContextManager` connect/disconnet signal callbacks via with statement\n\n### unittest_utils/assertments.py\n\nThe file contains some common assert functions:\n\n\n* `assert_startswith` - Check if test starts with prefix.\n* `assert_endswith` - Check if text ends with suffix.\n* `assert_locmem_mail_backend` - Check if current email backend is the In-memory backend.\n* {{{assert_language_code() - Check if given language_code is in settings.LANGUAGES\n* `assert_installed_apps()` - Check entries in settings.INSTALLED_APPS\n* `assert_is_dir` - Check if given path is a directory\n* `assert_is_file` - Check if given path is a file\n* `assert_path_not_exists` - Check if given path doesn't exists\n\n### Speedup tests\n\nSpeedup test run start by disable migrations, e.g.:\n```\nfrom django_tools.unittest_utils.disable_migrations import DisableMigrations\nMIGRATION_MODULES = DisableMigrations()\n```\n\n### small tools\n\n#### debug_csrf_failure()\n\nDisplay the normal debug page and not the minimal csrf debug page.\nMore info in DocString here: [django_tools/views/csrf.py](https://github.com/jedie/django-tools/blob/master/django_tools/views/csrf.py)\n\n#### import lib helper\n\nadditional helper to the existing `importlib`\nmore info in the sourcecode: [./utils/importlib.py](https://github.com/jedie/django-tools/blob/master/django_tools/utils/importlib.py)\n\n#### http utils\n\nPimped HttpRequest to get some more information about a request.\nMore info in DocString here: [django_tools/utils/http.py](https://github.com/jedie/django-tools/blob/master/django_tools/utils/http.py)\n\n#### @display_admin_error\n\nDeveloper helper to display silent errors in ModelAdmin.list_display callables.\nSee: **display_admin_error** in [decorators.py](https://github.com/jedie/django-tools/blob/master/django_tools/decorators.py)\n\n### upgrade virtualenv\n\nA simple commandline script that calls `pip install \u2014-upgrade XY` for every package thats installed in a virtualenv.\nSimply copy/symlink it into the root directory of your virtualenv and start it.\n\n**Note:**[Seems that this solution can't observe editables right.](https://github.com/pypa/pip/issues/319)\n\nTo use it, without installing django-tools:\n```\n~/$ cd goto/your_env\n.../your_env/$ wget https://github.com/jedie/django-tools/raw/master/django_tools/upgrade_virtualenv.py\n.../your_env/$ chmod +x upgrade_virtualenv.py\n.../your_env/$ ./upgrade_virtualenv.py\n```\n\nThis script will be obsolete, if [pip has a own upgrade command](https://github.com/pypa/pip/issues/59).\n\n### django_tools.utils.url.GetDict\n\nSimilar to origin django.http.QueryDict but:\n\n\n* urlencode() doesn't add \"=\" to empty values: \"?empty\" instead of \"?empty=\"\n* always mutable\n* output will be sorted (easier for tests ;)\n\nMore info, see tests: [django_tools_tests/test_utils_url.py](https://github.com/jedie/django-tools/blob/master/django_tools_tests/test_utils_url.py)\n\n#### SignedCookieStorage\n\nStore information in signed Cookies, use **django.core.signing**.\nSo the cookie data can't be manipulated from the client.\nSources/examples:\n\n\n* [/django_tools/utils/client_storage.py](https://github.com/jedie/django-tools/blob/master/django_tools/utils/client_storage.py)\n* [/django_tools_tests/test_signed_cookie.py](https://github.com/jedie/django-tools/blob/master/django_tools_tests/test_signed_cookie.py)\n\n### Print SQL Queries\n\nPrint the used SQL queries via context manager.\n\nusage e.g.:\n```\nfrom django_tools.unittest_utils.print_sql import PrintQueries\n\n# e.g. use in unittests:\nclass MyTests(TestCase):\n    def test_foobar(self):\n        with PrintQueries(\"Create object\"):\n            FooBar.objects.create(\"name\"=foo)\n\n# e.g. use in views:\ndef my_view(request):\n    with PrintQueries(\"Create object\"):\n        FooBar.objects.create(\"name\"=foo)\n```\n\nthe output is like:\n```\n_______________________________________________________________________________\n *** Create object ***\n1 - INSERT INTO \"foobar\" (\"name\")\n    VALUES (foo)\n-------------------------------------------------------------------------------\n```\n\n### SetRequestDebugMiddleware\n\nmiddleware to add debug bool attribute to request object.\nMore info: [./debug/README.creole](https://github.com/jedie/django-tools/blob/master/django_tools/debug/README.creole)\n\n### TracebackLogMiddleware\n\nPut traceback in log by call [logging.exception()](https://docs.python.org/3/library/logging.html#logging.Logger.exception) on `process_exception()`\nActivate with:\n```\nMIDDLEWARE_CLASSES = (\n    ...\n    'django_tools.middlewares.TracebackLogMiddleware.TracebackLogMiddleware',\n    ...\n)\n```\n\n### FnMatchIps() - Unix shell-style wildcards in INTERNAL_IPS / ALLOWED_HOSTS\n\nsettings.py e.g.:\n```\nfrom django_tools.settings_utils import FnMatchIps\n\nINTERNAL_IPS = FnMatchIps([\"127.0.0.1\", \"::1\", \"192.168.*.*\", \"10.0.*.*\"])\nALLOWED_HOSTS = FnMatchIps([\"127.0.0.1\", \"::1\", \"192.168.*.*\", \"10.0.*.*\"])\n```\n\n### StdoutStderrBuffer()\n\nredirect stdout + stderr to a string buffer. e.g.:\n```\nfrom django_tools.unittest_utils.stdout_redirect import StdoutStderrBuffer\n\nwith StdoutStderrBuffer() as buffer:\n    print(\"foo\")\noutput = buffer.get_output() # contains \"foo\\n\"\n```\n\n### Management commands\n\n#### permission_info\n\nList all permissions for one django user.\n(Needs `'django_tools'` in INSTALLED_APPS)\n\ne.g.:\n```\n$ ./manage.py permission_info\nNo username given!\n\nAll existing users are:\nfoo, bar, john, doe\n\n$ ./manage.py permission_info foo\nAll permissions for user 'test_editor':\n\tis_active    : yes\n\tis_staff     : yes\n\tis_superuser : no\n[*] admin.add_logentry\n[*] admin.change_logentry\n[*] admin.delete_logentry\n[ ] auth.add_group\n[ ] auth.add_permission\n[ ] auth.add_user\n...\n```\n\n#### logging_info\n\nShows a list of all loggers and marks which ones are configured in settings.LOGGING:\n```\n$ ./manage.py logging_info\n```\n\n#### nice_diffsettings\n\nSimilar to django 'diffsettings', but used pretty-printed representation:\n```\n$ ./manage.py nice_diffsettings\n```\n\n#### database_info\n\nJust display some information about the used database and connections:\n```\n$ ./manage.py database_info\n```\n\n#### list_models\n\nJust list all existing models in app_label.ModelName format. Useful to use this in 'dumpdata' etc:\n```\n$ ./manage.py list_models\n```\n\n### ..all others\u2026\n\nThere exist many miscellaneous stuff. Look in the source, luke!\n\n## Backwards-incompatible changes\n\nOld changes archived in git history here:\n\n\n* [>=v0.47](https://github.com/jedie/django-tools/tree/v0.49.0#backwards-incompatible-changes)\n* [>=v0.35](https://github.com/jedie/django-tools/tree/v0.35.0#backwards-incompatible-changes)\n\n### v0.51\n\nAll Selenium helper are deprecated, please migrate to [Playwright](https://playwright.dev/python/) ;)\n\n### v0.50\n\nRemoved old selenium helper function, [deprecated since v0.43](https://github.com/jedie/django-tools/tree/v0.43.0#v043)\n\nMake all Selenium web driver instances persistent for the complete test run session.\nThis speedup tests and fixed some bugs in Selenium.\n\nThis result in the same browser/webdriver settings for all test classes!\n\n## Django compatibility\n\n| django-tools     | django version | python          |\n| ---------------- | -------------- | --------------- |\n| >= v0.52.0       | 3.2, 4.0, 4.1  | 3.8, 3.9, 3.10  |\n| >= v0.50.0       | 2.2, 3.2, 4.0  | 3.8, 3.9, 3.10  |\n| >= v0.49.0       | 2.2, 3.1, 3.2  | 3.7, 3.8, 3.9   |\n| >= v0.47.0       | 2.2, 3.0, 3.1  | >= 3.6, pypy3   |\n| >= v0.39         | 1.11, 2.0      | 3.5, 3.6, pypy3 |\n| >= v0.38.1       | 1.8, 1.11      | 3.5, 3.6, pypy3 |\n| >= v0.38.0       | 1.8, 1.11      | 3.5, 3.6        |\n| >= v0.37.0       | 1.8, 1.11      | 3.4, 3.5        |\n| >= v0.33.0       | 1.8, 1.11      | 2.7, 3.4, 3.5   |\n| v0.30.1-v0.32.14 | 1.8, 1.9, 1.10 | 2.7, 3.4, 3.5   |\n| v0.30            | 1.8, 1.9       | 2.7, 3.4        |\n| v0.29            | 1.6 - 1.8      | 2.7, 3.4        |\n| v0.26            | <=1.6          |                 |\n| v0.25            | <=1.4          |                 |\n\n(See also combinations for [tox in pyproject.toml](https://github.com/jedie/django-tools/blob/master/pyproject.toml))\n\n## history\n\n\n* *dev* - [compare v0.54.0...main](https://github.com/jedie/django-tools/compare/v0.54.0...main)\n  * TBC\n* [v0.54.0 - 15.09.2022](https://github.com/jedie/django-tools/compare/v0.53.0...v0.54.0)\n  * Run [safety](https://github.com/pyupio/safety) in CI pipeline.\n  * NEW: `SyslogHandler` for easy logging to `syslog`\n  * Replace creole README with markdown\n* [v0.53.0 - 18.08.2022](https://github.com/jedie/django-tools/compare/v0.52.0...v0.53.0)\n  * NEW: `MassContextManagerBase`, `DenyStdWrite` + Updated: `StdoutStderrBuffer`\n  * Enhance `run_testserver` command\n  * Small test project setup updates\n* [v0.52.0 - 17.08.2022](https://github.com/jedie/django-tools/compare/v0.51.0...v0.52.0)\n  * Move `run_testserver` management command from `django_tools_test_app` to `django_tools` and polish it.\n  * Restrict `AlwaysLoggedInAsSuperUserMiddleware` to the admin.\n  * Test with Django 3.2, 4.0 and 4.1\n* [v0.51.0 - 26.07.2022](https://github.com/jedie/django-tools/compare/v0.50.0...v0.51.0)\n  * NEW: `check_editor_config()` to check editor config and line length in projects\n  * DEPRECATE all Selenium helper, please migrate to Playwright ;)\n  * NEW: [Playwright](https://playwright.dev/python/) base Unittest class and login helper\n* [v0.50.0 - 29.05.2022](https://github.com/jedie/django-tools/compare/v0.49.0...v0.50.0)\n  * Rename git \"master\" branch to \"main\"\n  * Use [webdriver-manager](https://github.com/SergeyPirogov/webdriver_manager) to setup selenium web drivers\n  * CHANGE: Now all web driver instances are cached and reused for the complete test run!\n  * Use [darker](https://github.com/akaihola/darker) for code formatting\n  * Update test matrix: Add Python 3.10 and Django v4.0 (remove Python 3.7 and Django 3.1)\n* v0.49.0 - 22.11.2021 - [compare v0.48.3...v0.49.0](https://github.com/jedie/django-tools/compare/v0.48.3...v0.49.0)\n  * NEW: `VersionProtectBaseModel` - Protect a model against overwriting a newer entry with an older one, by adding a auto increment version number.\n  * NEW: `AlwaysLoggedInAsSuperUserMiddleware` useful for local dev server run to auto login into Django Admin\n  * Enhance Selenium test helpers and [fix #21: Set chrome accept_languages in headless mode](https://github.com/jedie/django-tools/issues/21)\n  * Update project: Tests, code-style etc.\n* v0.48.3 - 20.12.2020 - [compare v0.47.2...v0.48.3](https://github.com/jedie/django-tools/compare/v0.47.2...v0.48.3)\n  * NEW: `ImageDummy().in_memory_image_file()` useful for e.g.: POST a image upload via Django's test client\n* v0.48.2 - 06.12.2020 - [compare v0.47.1...v0.48.2](https://github.com/jedie/django-tools/compare/v0.47.1...v0.48.2)\n  * Bugfix \"Serve User Media File\": Create tokens via migrations and handle if tokens not exists\n* v0.48.1 - 06.12.2020 - [compare v0.47.0...v0.48.1](https://github.com/jedie/django-tools/compare/v0.47.0...v0.48.1)\n  * Add migration file to \"Serve User Media File\"\n* v0.48.0 - 06.12.2020 - [compare v0.47.0...v0.48.0](https://github.com/jedie/django-tools/compare/v0.47.0...v0.48.0)\n  * NEW: \"Serve User Media File\" reuseable app\n  * NEW: django_tools.unittest_utils.signals.SignalsContextManager\n  * Update `installed_apps_utils.get_filtered_apps()` and support app config entries in `settings.INSTALLED_APPS`\n* v0.47.0 - 26.11.2020 - [compare v0.46.1...v0.47.0](https://github.com/jedie/django-tools/compare/v0.46.1...v0.47.0)\n  * updates for newer django versions\n  * NEW: assert_warnings(), assert_no_warnings() and assert_in_logs()\n  * remove broken LoggingBuffer()\n  * update project setup, fix tests and pipelines\n* v0.46.1 - 19.02.2020 - [compare v0.46.0...v0.46.1](https://github.com/jedie/django-tools/compare/v0.46.0...v0.46.1)\n  * less restricted dependency specification\n  * NEW: `\"django_tools.middlewares.LogHeaders.LogRequestHeadersMiddleware\"`\n  * SeleniumChromiumTestCase: set \"accept_languages\" and disable \"headless\" mode, see also: [https://github.com/jedie/django-tools/issues/21](https://github.com/jedie/django-tools/issues/21)\n* v0.46.0 - 13.02.2020 - [compare v0.45.3...v0.46.0](https://github.com/jedie/django-tools/compare/v0.45.3...v0.46.0)\n  * `dynamic_site` was removed. Please use e.g.: [django-hosts](https://github.com/jazzband/django-hosts)\n  * modernize project setup and use `poetry`\n  * remove `lxml` decency by using [bleach](https://github.com/mozilla/bleach) for `html_utils.html2text`\n  * update code and code style\n* v0.45.3 - 25.08.2019 - [compare v0.45.2...v0.45.3](https://github.com/jedie/django-tools/compare/v0.45.2...v0.45.3)\n  * Add `excepted_exit_code` to `django_tools.unittest_utils.django_command.DjangoCommandMixin` to it's possible to test errors in manage commands\n* v0.45.2 - 26.06.2019 - [compare v0.45.1...v0.45.2](https://github.com/jedie/django-tools/compare/v0.45.1...v0.45.2)\n  * NEW: `django_tools.log_utils.throttle_admin_email_handler.ThrottledAdminEmailHandler`\n* v0.45.1 - 03.04.2019 - [compare v0.45.0...v0.45.1](https://github.com/jedie/django-tools/compare/v0.45.0...v0.45.1)\n  * Bugfix ValueError in `django_tools.unittest_utils.email.print_mailbox`\n* v0.45.0 - 01.04.2019 - [compare v0.44.2...v0.45.0](https://github.com/jedie/django-tools/compare/v0.44.2...v0.45.0)\n  * NEW: `OverwriteFileSystemStorage` with backup functionality\n  * NEW: `print_exc_plus()` - traceback with a listing of all the local variables\n  * NEW: `assert_pformat_equal` with `pprintpp` and `icdiff`\n  * NEW: `assert_filenames_and_content`\n* v0.44.2 - 02.01.2019 - [compare v0.44.1...v0.44.2](https://github.com/jedie/django-tools/compare/v0.44.1...v0.44.2)\n  * Handle errors like: [https://github.com/andymccurdy/redis-py/issues/995](https://github.com/andymccurdy/redis-py/issues/995)\n* v0.44.1 - 02.01.2019 - [compare v0.44.0...v0.44.1](https://github.com/jedie/django-tools/compare/v0.44.0...v0.44.1)\n  * `LoggingBuffer`: Don't deactivate existing log handler, just append the buffer handler.\n* v0.44.0 - 13.12.2018 - [compare v0.43.2...v0.44.0](https://github.com/jedie/django-tools/compare/v0.43.2...v0.44.0)\n  * NEW: `django_file = ImageDummy().create_django_file_info_image(text=\"\")` e.g.: for attach to `models.ImageField()`\n  * Make `mockup.ImageDummy()` usable even if django-filer is not installed.\n  * `mockup.ImageDummy()` default image format changed from png to jpeg\n  * Cleanup: remove old, since v0.35 deprecated mockup image API\n* v0.43.2 - 11.12.2018 - [compare v0.43.1...v0.43.2](https://github.com/jedie/django-tools/compare/v0.43.1...v0.43.2)\n  * Bugfix Selenium refactor: Use the class with the same functionality if old usage places are used.\n* v0.43.1 - 11.12.2018 - [compare v0.43.0...v0.43.1](https://github.com/jedie/django-tools/compare/v0.43.0...v0.43.1)\n  * Bugfix: Selenium test cases: clear `window.localStorage` after test run\n* v0.43.0 - 11.12.2018 - [compare v0.42.4...v0.43.0](https://github.com/jedie/django-tools/compare/v0.42.4...v0.43.0)\n  * Refactor selenium helpers\n  * Split selenium test cases: with and without Django StaticLiveServerTestCase\n  * NEW: Selenium helper to access `window.localStorage`\n* v0.42.4 - 12.10.2018 - [compare v0.42.3...v0.42.4](https://github.com/jedie/django-tools/compare/v0.42.3...v0.42.4)\n  * Add `request` object to `TestUserMixin.login()` (needed for e.g.: django-axes auth backend)\n* v0.42.3 - 10.10.2018 - [compare v0.42.2...v0.42.3](https://github.com/jedie/django-tools/compare/v0.42.2...v0.42.3)\n  * NEW: * `assertments.assert_is_dir`, `assertments.assert_is_file`, `assertments.assert_path_not_exists`\n* v0.42.2 - 18.09.2018 - [compare v0.42.1...v0.42.2](https://github.com/jedie/django-tools/compare/v0.42.1...v0.42.2)\n  * NEW: `assert_installed_apps()` - Check entries in settings.INSTALLED_APPS\n* v0.42.1 - 17.09.2018 - [compare v0.42.0...v0.42.1](https://github.com/jedie/django-tools/compare/v0.42.0...v0.42.1)\n  * NEW: `django_tools.unittest_utils.assertments.assert_language_code` - Check if given language_code is in settings.LANGUAGES\n* v0.42.0 - 07.09.2018 - [compare v0.41.0...v0.42.0](https://github.com/jedie/django-tools/compare/v0.41.0...v0.42.0)\n  * remove all celery stuff\n  * NEW: `$ ./manage.py logging_info` Shows a list of all loggers and marks which ones are configured in settings.LOGGING\n  * manage commands `list_models` and `nice_diffsettings` are moved from seperate apps\n* v0.41.0 - 28.08.2018 - [compare v0.40.6...v0.41.0](https://github.com/jedie/django-tools/compare/v0.40.6...v0.41.0)\n  * NEW: `unittest_utils/assertments.py` with some common assert functions\n  * Remove `@task_always_eager()` decorator\n* v0.40.6 - 28.08.2018 - [compare v0.40.5...v0.40.6](https://github.com/jedie/django-tools/compare/v0.40.5...v0.40.6)\n  * Bugfix `@task_always_eager()` decorator\n* v0.40.5 - 27.08.2018 - [compare v0.40.4...v0.40.5](https://github.com/jedie/django-tools/compare/v0.40.4...v0.40.5)\n  * Bugfix `DjangoCommandMixin.call_manage_py()`: Use the given `manage.py` filename\n* v0.40.4 - 21.08.2018 - [compare v0.40.3...v0.40.4](https://github.com/jedie/django-tools/compare/v0.40.3...v0.40.4)\n  * NEW: `django_tools.debug.delay` to simulate longer processing time by set a delay via GET parameter (see above)\n* v0.40.3 - 18.07.2018 - [compare v0.40.2...v0.40.3](https://github.com/jedie/django-tools/compare/v0.40.2...v0.40.3)\n  * Enhance selenium test cases:\n    * NEW: `assert_visible_by_id()`\n    * NEW: `assert_clickable_by_id()`\n    * NEW: `assert_clickable_by_xpath()`\n    * add `desired_capabilities` to firefox and chrome test cases\n    * enable logging in chrome test case\n    * NEW: `assert_in_browser_log()` in chrome test case\n* v0.40.2 - 04.07.2018 - [compare v0.40.1...v0.40.2](https://github.com/jedie/django-tools/compare/v0.40.1...v0.40.2)\n  * Bugfix selenium Test Case if driver is None\n  * Bugfix django compatibility\n* v0.40.1 - 28.06.2018 - [compare v0.40.0...v0.40.1](https://github.com/jedie/django-tools/compare/v0.40.0...v0.40.1)\n  * Bugfix selenium test case if executable can't be found.\n* v0.40.0 - 15.06.2018 - [compare v0.39.6...v0.40.0](https://github.com/jedie/django-tools/compare/v0.39.6...v0.40.0)\n  * NEW: selenium chrome and firefox test cases in `django_tools.unittest_utils.selenium_utils`\n  * Fix test project and add `run_test_project_dev_server.sh` for easy test\n  * Fixing tests\n* v0.39.6 - 04.05.2018 - [compare v0.39.5...v0.39.6](https://github.com/jedie/django-tools/compare/v0.39.5...v0.39.6)\n  * Enhance model instance unittest code generator\n* v0.39.5 - 24.04.2018 - [compare v0.39.4...v0.39.5](https://github.com/jedie/django-tools/compare/v0.39.4...v0.39.5)\n  * NEW: Model instance unittest code generator (see above)\n* v0.39.4 - 06.04.2018 - [compare v0.39.3...v0.39.4](https://github.com/jedie/django-tools/compare/v0.39.3...v0.39.4)\n  * NEW: `django_tools.unittest_utils.logging_utils.FilterAndLogWarnings` and `django_tools.unittest_utils.logging_utils.CutPathnameLogRecordFactory`\n* v0.39.3 - 22.03.2018 - [compare v0.39.2...v0.39.3](https://github.com/jedie/django-tools/compare/v0.39.2...v0.39.3)\n  * `django_tools.permissions.get_filtered_permissions` has new keyword argument: `exclude_actions`\n* v0.39.2 - 22.03.2018 - [compare v0.39.1...v0.39.2](https://github.com/jedie/django-tools/compare/v0.39.1...v0.39.2)\n  * NEW: `django_tools.parler_utils.parler_fixtures.ParlerDummyGenerator`\n  * NEW: `django_tools.fixture_tools.languages.iter_languages`\n* v0.39.1 - 19.03.2018 - [compare v0.39.0...v0.39.1](https://github.com/jedie/django-tools/compare/v0.39.0...v0.39.1)\n  * NEW: `django_tools.unittest_utils.email.print_mailbox()`\n  * minor updates\n* v0.39.0 - 02.03.2018 - [compare v0.38.9...v0.39.0](https://github.com/jedie/django-tools/compare/v0.38.9...v0.39.0)\n  * NEW: Isolated Filesystem decorator / context manager\n  * Backwards-incompatible change: file renamed `django_tools/unittest_utils/{celery.py => celery_utils.py`}\n  * Skip run test with Django 1.8 and run tests with Django 1.11 and 2.0\n* v0.38.9 - 05.02.2018 - [compare v0.38.8...v0.38.9](https://github.com/jedie/django-tools/compare/v0.38.8...v0.38.9)\n  * lowering log level on missing permissions from \"error\" to \"debug\"\n* v0.38.8 - 05.02.2018 - [compare v0.38.7...v0.38.8](https://github.com/jedie/django-tools/compare/v0.38.7...v0.38.8)\n  * send mail: use from celery import shared_task instead of djcelery_transactions\n* v0.38.7 - 15.01.2018 - [compare v0.38.6...v0.38.7](https://github.com/jedie/django-tools/compare/v0.38.6...v0.38.7)\n  * Add missing arguments (like \"attachments\", \"cc\" etc.) to `django_tools.mail.send_mail.SendMailCelery`\n* v0.38.6 - 10.01.2018 - [compare v0.38.4...v0.38.5](https://github.com/jedie/django-tools/compare/v0.38.4...v0.38.5)\n  * NEW: `./manage.py clear_cache`\n  * Display POST data in browser debug (`django_tools.unittest_utils.BrowserDebug.debug_response`)\n* v0.38.5 - 02.01.2018 - [compare v0.38.4...v0.38.5](https://github.com/jedie/django-tools/compare/v0.38.4...v0.38.5)\n  * NEW: Helper to assert django message framework output in unittests:\n    * `BaseUnittestCase.get_messages()`: return a list of all messages\n    * `BaseTestCase.assertMessages()`: compare messages\n    * `BaseTestCase.assertResponse()`: has new keyword argument `messages`\n  * NEW: `BaseUnittestCase.assert_exception_startswith()`\n* v0.38.4 - 28.12.2017 - [compare v0.38.3...v0.38.4](https://github.com/jedie/django-tools/compare/v0.38.3...v0.38.4)\n  * Bugfix attach user group on existing user in: `django_tools.unittest_utils.user.get_or_create_user`\n* v0.38.3 - 28.12.2017 - [compare v0.38.2...v0.38.3](https://github.com/jedie/django-tools/compare/v0.38.2...v0.38.3)\n  * Bugfix: `unittest_utils.user.get_or_create_group` also removes obsolete permissions, too.\n* v0.38.2 - 27.12.2017 - [compare v0.38.1...v0.38.2](https://github.com/jedie/django-tools/compare/v0.38.1...v0.38.2)\n  * NEW: `./manage.py update_permissions`\n* v0.38.1 - 21.12.2017 - [compare v0.38.0...v0.38.1](https://github.com/jedie/django-tools/compare/v0.38.0...v0.38.1)\n  * refactor travis/tox/pytest/coverage stuff\n  * Tests can be run via `python3 setup.py tox` and/or `python3 setup.py test`\n  * Test also with pypy3 on Travis CI.\n* v0.38.0 - 19.12.2017 - [compare v0.37.0...v0.38.0](https://github.com/jedie/django-tools/compare/v0.37.0...v0.38.0)\n  * NEW: `django_tools.unittest_utils.user.get_or_create_group`\n  * NEW: `django_tools.unittest_utils.user.get_or_create_user`\n  * NEW: `django_tools.unittest_utils.user.get_or_create_user_and_group`\n  * NEW: `BaseUnittestCase.get_admin_change_url()` and `BaseUnittestCase.get_admin_add_url()`\n  * NEW: `BaseUnittestCase.assert_startswith()` and `BaseUnittestCase.assert_endswith()`\n* v0.37.0 - 11.12.2017 - [compare v0.36.0...v0.37.0](https://github.com/jedie/django-tools/compare/v0.36.0...v0.37.0)\n  * Skip official support for python v2 (remove from text matrix)\n  * NEW: `./manage.py permission_info`: Display a list of all permissions for one django user\n  * NEW: `django_tools.permissions.get_filtered_permissions()` and `django_tools.permissions.pprint_filtered_permissions()`\n  * `django_tools.settings_utils.InternalIps` was renamed to `FnMatchIps` and can be also used for **ALLOWED_HOSTS**\n  * Bugfix/Enhance permission helpers\n* v0.36.0 - 20.11.2017 - [compare v0.35.0...v0.36.0](https://github.com/jedie/django-tools/compare/v0.35.0...v0.36.0)\n  * NEW: `./manage.py database_info`\n  * Bugfix: **ModelPermissionMixin**\n  * Dynamic Sites is no longer maintained and tests are deactivated. It's currently not compatible with all django versions.\n* v0.35.0 - 26.09.2017 - [compare v0.34.0...v0.35.0](https://github.com/jedie/django-tools/compare/v0.34.0...v0.35.0)\n  * CHANGE: The dummy image generation function in `django_tools.unittest_utils.mockup` has a new API.\n* v0.34.0 - 18.09.2017 - [compare v0.33.0...v0.34.0](https://github.com/jedie/django-tools/compare/v0.33.0...v0.34.0)\n  * CHANGE: The test usernames changed and spaces was replace with underscores e.g.: \"staff test user\" -> \"staff_test_user\"\n  * Bugfix in mockup.create_pil_image: Created images has wrong sizes\n* v0.33.0 - 11.07.2017 - [compare v0.32.14...v0.33.0](https://github.com/jedie/django-tools/compare/v0.32.14...v0.33.0)\n  * Run tests only against Django v1.8 TLS and v1.11 TLS\n  * For Django 1.11: Add support for new-style middleware - contributed by benkonrath\n  * NEW: `django_tools.utils.request.create_fake_request()` for easier create a faked request object with `RequestFactory`\n  * NEW: `django_tools.utils.html_utils.html2text()` - Strip HTML tags with lxml Cleaner + Django 'strip_tags'\n* v0.32.14 - 14.06.2017 - [compare v0.32.13...v0.32.14](https://github.com/jedie/django-tools/compare/v0.32.13...v0.32.14)\n  * Bugfix for Python 2: `mock` backport package is needed and added to `setup.install_requires`\n* v0.32.13 - 24.05.2017 - [compare v0.32.12...v0.32.13](https://github.com/jedie/django-tools/compare/v0.32.12...v0.32.13)\n  * remove some warnings\n* v0.32.12 - 04.05.2017 - [compare v0.32.11...v0.32.12](https://github.com/jedie/django-tools/compare/v0.32.11...v0.32.12)\n  * NEW: `self.assertIn_dedent()` in `django_tools.unittest_utils.unittest_base.BaseUnittestCase`\n* v0.32.11 - 02.05.2017 - [compare v0.32.10...v0.32.11](https://github.com/jedie/django-tools/compare/v0.32.10...v0.32.11)\n  * Fix PyPi package mistake (`.tar.gz` archive contains `.tox` ;)\n* v0.32.10 - 02.05.2017 - [compare v0.32.9...v0.32.10](https://github.com/jedie/django-tools/compare/v0.32.9...v0.32.10)\n  * NEW: `django_tools.mail` to send text+html mails (see above)\n* v0.32.9 - 21.03.2017 - [compare v0.32.8...v0.32.9](https://github.com/jedie/django-tools/compare/v0.32.8...v0.32.9)\n  * Bugfix `DebugCacheLoader` if TemplateDoesNotExist was raised\n* v0.32.8 - 16.03.2017 - [compare v0.32.7...v0.32.8](https://github.com/jedie/django-tools/compare/v0.32.7...v0.32.8)\n  * NEW: `django_tools.template.loader.DebugCacheLoader` to add template name as html comments\n  * Change temp filename in BrowserDebug and use `django_tools_browserdebug_` prefix\n  * Bugfix in `django_tools.middlewares.ThreadLocal.ThreadLocalMiddleware`\n* v0.32.7 - 10.03.2017 - [compare v0.32.6...v0.32.7](https://github.com/jedie/django-tools/compare/v0.32.6...v0.32.7)\n  * NEW: `django_tools.permissions` - helper for setup permissions\n  * NEW: `/unittest_utils/user.py` - helper for creating users (needfull in unittests)\n* v0.32.6 - 22.02.2017 - [compare v0.32.5...v0.32.6](https://github.com/jedie/django-tools/compare/v0.32.5...v0.32.6)\n* `@task_always_eager()` decorator will set `CELERY_EAGER_PROPAGATES_EXCEPTIONS=True`, too.\n* v0.32.5 - 10.02.2017 - [compare v0.32.4...v0.32.5](https://github.com/jedie/django-tools/compare/v0.32.4...v0.32.5)\n  * NEW: Add `template_name` (optional) to `self.assertResponse()` (check with `assertTemplateUsed()`)\n* v0.32.4 - 01.02.2017 - [compare v0.32.3...v0.32.4](https://github.com/jedie/django-tools/compare/v0.32.3...v0.32.4)\n* Fix: Set \"is_active\" for created test users\n* v0.32.3 - 25.01.2017 - [compare v0.32.2...v0.32.3](https://github.com/jedie/django-tools/compare/v0.32.2...v0.32.3)\n  * Fix UnicodeDecodeError in BrowserDebug\n  * NEW: `@set_string_if_invalid()` decorator\n  * NEW: `@task_always_eager()` decorator\n* v0.32.2 - 13.01.2017 - [compare v0.32.1...v0.32.2](https://github.com/jedie/django-tools/compare/v0.32.1...v0.32.2)\n  * NEW: django_tools.utils.url.GetDict\n* v0.32.1 - 29.12.2016 - [compare v0.32.0...v0.32.1](https://github.com/jedie/django-tools/compare/v0.32.0...v0.32.1)\n  * NEW: TracebackLogMiddleware\n* v0.32.0 - 19.12.2016 - [compare v0.31.0...v0.32.0](https://github.com/jedie/django-tools/compare/v0.31.0...v0.32.0)\n  * NEW: Management commands: 'nice_diffsettings', 'list_models'\n  * NEW: @display_admin_error to display silent errors in ModelAdmin.list_display callables.\n  * NEW: django_tools.template.render.render_template_file\n  * use [pytest-django](https://pypi.python.org/pypi/pytest-django)\n  * remove outdated stuff: See 'Backwards-incompatible changes' above.\n* v0.31.0 - 03.11.2016 - [compare v0.30.4...v0.31.0](https://github.com/jedie/django-tools/compare/v0.30.4...v0.31.0)\n  * add Mockup utils to create dummy PIL/django-filer images with Text (see above)\n  * move tests into `/django_tools_tests/`\n* v0.30.4 - 27.10.2016 - [compare v0.30.2...v0.30.4](https://github.com/jedie/django-tools/compare/v0.30.2...v0.30.4)\n  * add DjangoCommandMixin\n* v0.30.2 - 05.10.2016 - [compare v0.30.1...v0.30.2](https://github.com/jedie/django-tools/compare/v0.30.1...v0.30.2)\n  * Bugfix Python 2 compatibility\n* v0.30.1 - 26.08.2016 - [compare v0.30.0...v0.30.1](https://github.com/jedie/django-tools/compare/v0.30.0...v0.30.1)\n  * add: `django_tools.unittest_utils.disable_migrations.DisableMigrations` (see above)\n  * run tests also with django v1.10 and Python 3.5\n  * use tox\n* v0.30.0 - 27.04.2016 - [compare v0.29.5...v0.30.0](https://github.com/jedie/django-tools/compare/v0.29.5...v0.30.0)\n  * Django 1.9 and Python 3 support contributed by [naegelyd](https://github.com/jedie/django-tools/pull/9)\n* v0.29.4 and v0.29.5 - 10.08.2015 - [compare v0.29.3...v0.29.5](https://github.com/jedie/django-tools/compare/v0.29.3...v0.29.5)\n  * Some bugfixes for django 1.6 support\n* v0.29.3 - 10.08.2015 - [compare v0.29.2...v0.29.3](https://github.com/jedie/django-tools/compare/v0.29.2...v0.29.3)\n  * Clear ThreadLocal request atttribute after response is processed (contributed by Lucas Wiman)\n* v0.29.2 - 19.06.2015 - [compare v0.29.1...v0.29.2](https://github.com/jedie/django-tools/compare/v0.29.1...v0.29.2)\n  * Bugfix in unittest_utils.selenium_utils.selenium2fakes_response\n  * assertResponse used assertContains from django\n  * Add QueryLogMiddleware (TODO: add tests)\n* v0.29.1 - 17.06.2015 - [compare v0.29.0...v0.29.1](https://github.com/jedie/django-tools/compare/v0.29.0...v0.29.1)\n  * Bugfixes for Py2 and Py3\n  * add StdoutStderrBuffer()\n* v0.29.0 - 09.06.2015 - [compare v0.26.0...v0.29.0](https://github.com/jedie/django-tools/compare/v0.26.0...v0.29.0)\n  * WIP: Refactor unittests (DocTests must be updated for Py3 and more unittests must be written to cover all)\n  * catch more directory traversal attacks in BaseFilesystemBrowser (and merge code parts)\n  * Bugfix for \"django.core.exceptions.AppRegistryNotReady: Models aren't loaded yet.\" if using **UpdateInfoBaseModel**\n  * Bugfixes in **dynamic_site** for django 1.7\n  * add: [django_tools.settings_utils.InternalIps](https://github.com/jedie/django-tools/blob/master/django_tools/settings_utils.py)\n* v0.28.0 - 12.02.2015 - [compare v0.26.0...v0.28.0](https://github.com/jedie/django-tools/compare/v0.26.0...v0.28.0)\n  * Work-a-round for import loops\n  * (new Version number, because of PyPi stress)\n* v0.26.0 - 11.02.2015 - [compare v0.25.1...v0.26.0](https://github.com/jedie/django-tools/compare/v0.25.1...v0.26.0)\n  * Updates for Django 1.6 and Python 3\n* v0.25.1 - 18.11.2013\n  * Bugfix: Fall back to \"UTF-8\" if server send no encoding info\n* v0.25.0 - 28.08.2012\n  * Rename **cache.clear()** in SmoothCacheBackends to **cache.smooth_update()**, so that reset timestamp is independ from clear the cache.\n* v0.24.10 - 24.08.2012\n  * Add **SmoothCacheBackends**: [./cache/README.creole](https://github.com/jedie/django-tools/blob/master/django_tools/cache/README.creole)\n* v0.24.9 - 24.08.2012\n  * Bugfix in per-site cache middleware: set inital count values to None, if counting is disabled.\n* v0.24.8 - 24.08.2012\n  * Enhanced **per-site cache middleware**: [./cache/README.creole](https://github.com/jedie/django-tools/blob/master/django_tools/cache/README.creole)\n  * Add **SetRequestDebugMiddleware**: [./debug/README.creole](https://github.com/jedie/django-tools/blob/master/django_tools/debug/README.creole)\n* v0.24.7 - 21.08.2012\n  * Add the **per-site cache middleware** (see above)\n  * Add **import lib helper**: [./utils/importlib.py](https://github.com/jedie/django-tools/blob/master/django_tools/utils/importlib.py)\n* v0.24.6 - 21.08.2012\n  * Add the **filemanager library** (see above)\n* v0.24.5 - 06.08.2012\n  * Add **Print SQL Queries** context manager. (see above)\n* v0.24.4 - 26.07.2012\n  * remove date from version string, cause of side-effects e.g.: user clone the repo and has the filter not installed\n* v0.24.3 - 25.07.2012\n  * \"Hardcode\" the version string date attachment via [gitattribute filter script](https://github.com/jedie/python-code-snippets/tree/master/CodeSnippets/git) to fix [a reported issues](https://github.com/jedie/django-tools/issues/1) with [pip requirements file bug](https://github.com/pypa/pip/issues/145).\n* v0.24.2 - 10.07.2012\n  * Split [UpdateInfoBaseModel()](https://github.com/jedie/django-tools/blob/master/django_tools/models.py): So you can only set \"createtime\", \"lastupdatetime\" or \"createby\", \"lastupdateby\" or both types (This is backwards compatible)\n* v0.24.1 - 12.06.2012\n  * Bugfix: UsergroupsModelField() and add unittests for it\n  * Add \"normal users\" in UsergroupsModelField()\n  * New: Add create_user() and create_testusers() to BaseTestCase\n  * Add a test project for the unittests. TODO: use this for all tests\n* v0.24.0 - 04.06.2012\n  * [Don't use auto_now_add and auto_now in UpdateInfoBaseModel](https://github.com/jedie/django-tools/commit/a3cf1f7b2e9dbe4964306f4793c74f1782f8b2ea)\n  * Bugfix in [UsergroupsModelField](https://github.com/jedie/django-tools/blob/master/django_tools/limit_to_usergroups.py)\n* v0.23.1\n  * [Dynamic Site](https://github.com/jedie/django-tools/tree/main/django_tools/dynamic_site#dynamic-site-id) would be only initialised if settings.USE_DYNAMIC_SITE_MIDDLEWARE = True\n* v0.23.0\n  * Use cryptographic signing tools from django 1.4 in django_tools.utils.client_storage\n* v0.22.0\n  * Add [static_path.py](https://github.com/jedie/django-tools/blob/master/django_tools/fields/static_path.py) thats used settings.STATIC_ROOT.\n  * The old [media_path.py](https://github.com/jedie/django-tools/blob/master/django_tools/fields/media_path.py) which used settings.MEDIA_ROOT is deprecated and will be removed in the future.\n  * auto_add_check_unique_together() can use settings.DATABASES[\"default\"][\"ENGINE\"], too.\n* v0.21.1\n  * Bugfixes in [Dynamic Site](https://github.com/jedie/django-tools/tree/main/django_tools/dynamic_site#dynamic-site-id).\n* v0.21.0beta\n  * New: site alias function\n  * refractory 'DynamicSiteMiddleware' to a own app (**Backwards-incompatible change:** change your settings if you use the old DynamicSiteMiddleware.)\n* v0.20.1\n  * New: [debug_csrf_failure()](https://github.com/jedie/django-tools/blob/master/django_tools/views/csrf.py) to display the normal debug page and not the minimal csrf debug page.\n* v0.20.0\n  * Add experimental [DynamicSiteMiddleware](https://github.com/jedie/django-tools/blob/master/django_tools/middlewares/DynamicSite.py), please test it and give feedback.\n* v0.19.6\n  * Add some south introspection rules for LanguageCodeModelField and jQueryTagModelField\n  * fallback if message for anonymous user can't created, because django.contrib.messages middleware not used.\n  * Bugfix in django_tools.utils.messages.StackInfoStorage\n* v0.19.5\n  * Add [http://bugs.python.org/file22767/hp_fix.diff](http://bugs.python.org/file22767/hp_fix.diff) for [https://github.com/gregmuellegger/django/issues/1](https://github.com/gregmuellegger/django/issues/1)\n* v0.19.4\n  * Bugfix for PyPy in local_sync_cache get_cache_information(): sys.getsizeof() not implemented on PyPy\n  * Bugfix in template.filters.chmod_symbol()\n  * Nicer solution for template.filters.human_duration()\n* v0.19.3\n  * Add support for https in utils/http.py\n* v0.19.2\n  * Bugfix in utils/http.py timeout work-a-round\n* v0.19.1\n  * utils/http.py changes:\n    * Use a better solution, see:\n    * Add timeout and add a work-a-round for Python < 2.6\n* v0.19.0\n  * NEW: Add utils/http.py with helpers to get a webpage via http GET in unicode\n  * Change README from textile to creole ;)\n* v0.18.2\n  * Bugfix: Add missing template in pypi package\n* v0.18.0\n  * NEW: Add DOM compare from Gregor M\u00fcllegger GSoC work into unittest utils.\n* v0.17.1\n  * Bugfix in \u201climit_to_usergroups\u201d: Make choices \u201clazy\u201d: Don\u2019t access the database in _init_\n* v0.17\n  * Add the script \u201cupgrade_virtualenv.py\u201d\n  * Add \u201climit_to_usergroups\u201d\n  * Add \u201clocal sync cache\u201d\n  * Add models.UpdateInfoBaseModel\n  * Update decorators.render_to\n  * render_to pass keyword arguments to render_to_response() (e.g.: mimetype=\u201ctext/plain\u201d)\n  * new argument \u201cskip_fail\u201d in get_filtered_apps(): If True: raise excaption if app is not importable\n* v0.16.4\n  * Bugfix: `get_db_prep_save() got an unexpected keyword argument 'connection\u2019` when save a SignSeparatedModelField()\n* v0.16.3\n  * Update BrowserDebug: Use response.templates instead of response.template and make output nicer\n* v0.16.2\n  * Merge stack info code and display better stack info on browser debug page\n* v0.16.1\n  * Update django_tools.utils.messages.StackInfoStorage for django code changes.\n* v0.16.0\n  * NEW: path model field (check if direcotry exist)\n* v0.15.0\n  * NEW: Add a flexible URL field (own validator, model- and form-field)\n* v0.14.1\n  * Bugfix: make path in MediaPathModelField relativ (remove slashes)\n* v0.14\n  * NEW: django-tagging addon: Display existing tags under a tag field\n* v0.13\n  * Bugfix UnicodeEncodeError in Browser debug\n* v0.12\n  * NEW: django_tools.utils.messages.failsafe_message\n* v0.11\n  * NEW: Store data in a secure cookie, see: utils/client_storage.py\n* v0.10.1\n  * New: Display used templates in unittest BrowserDebug\n  * Bugfix: catch if last usermessages exist\n* v0.10.0\n  * NEW: utils around django messages, see: /django_tools/utils/messages.py\n* v0.9.1\n  * Bugfix: database column was not created: don\u2019t overwrite get_internal_type()\n* v0.9\n  * New: stuff in /django_tools/fields/\n  * see also backwards-incompatible changes, above!\n* v0.8.2\n  * New: widgets.SelectMediaPath(): Select a sub directory in settings.MEDIA_ROOT\n  * New: fields.SignSeparatedField()\n* v0.8.1\n  * Add \u201cno_args\u201d keyword argument to installed_apps_utils.get_filtered_apps()\n* v0.8.0\n  * Add model LanguageCode field and form LanguageCode field in Accept-Language header format (RFC 2616)\n* v0.7.0\n  * Add decorators.py\n* v0.6.0\n  * Add forms_utils.LimitManyToManyFields, crosspost: [http://www.djangosnippets.org/snippets/1691/](http://www.djangosnippets.org/snippets/1691/)\n* v0.5.0\n  * Add template/filters.py from PyLucid v0.8.x\n* v0.4.0\n  * Add experimental \u201cwarn_invalid_template_vars\u201d\n* v0.3.1\n  * Bugfix: Exclude the instance if it was saved in the past.\n* v0.3.0\n  * Add utils.installed_apps_utils\n* v0.2.0\n  * Add models_utils, see: [http://www.jensdiemer.de/_command/118/blog/detail/67/](http://www.jensdiemer.de/_command/118/blog/detail/67/) (de)\n* v0.1.0\n  * first version cut out from PyLucid CMS \u2013 [http://www.pylucid.org](http://www.pylucid.org)\n\n## links\n\n| Homepage | [https://github.com/jedie/django-tools](https://github.com/jedie/django-tools)           |\n| PyPi     | [https://pypi.python.org/pypi/django-tools/](https://pypi.python.org/pypi/django-tools/) |\n\n## donation\n\n\n* [paypal.me/JensDiemer](https://www.paypal.me/JensDiemer)\n* [Flattr This!](https://flattr.com/submit/auto?uid=jedie&url=https%3A%2F%2Fgithub.com%2Fjedie%2Fdjango-tools%2F)\n* Send [Bitcoins](https://www.bitcoin.org/) to [1823RZ5Md1Q2X5aSXRC5LRPcYdveCiVX6F](https://blockexplorer.com/address/1823RZ5Md1Q2X5aSXRC5LRPcYdveCiVX6F)\n\n",
    "bugtrack_url": null,
    "license": "GPL-3.0-or-later",
    "summary": "miscellaneous tools for Django based projects",
    "version": "0.54.0",
    "split_keywords": [
        "django",
        "helpers",
        "tools",
        "utilities"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "md5": "2042e78bad77514314a4c6235af1f478",
                "sha256": "a7b7bfa5b9c5a81966454d17dffb2403cee25a806c858ee0486a08798227598f"
            },
            "downloads": -1,
            "filename": "django_tools-0.54.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "2042e78bad77514314a4c6235af1f478",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.7,<4.0.0",
            "size": 228665,
            "upload_time": "2022-09-15T08:02:03",
            "upload_time_iso_8601": "2022-09-15T08:02:03.164236Z",
            "url": "https://files.pythonhosted.org/packages/70/4b/8c4cb4498883f8923418cd9170c29c4b5e46ea99f31ad2c2213877630110/django_tools-0.54.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "md5": "6ceee38364024d8344c6078b829190ee",
                "sha256": "5040a91282be9d1c9d379b0c65da50bcb3691bff03cee54fd4123ace238c3a43"
            },
            "downloads": -1,
            "filename": "django-tools-0.54.0.tar.gz",
            "has_sig": false,
            "md5_digest": "6ceee38364024d8344c6078b829190ee",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.7,<4.0.0",
            "size": 181966,
            "upload_time": "2022-09-15T08:02:05",
            "upload_time_iso_8601": "2022-09-15T08:02:05.451270Z",
            "url": "https://files.pythonhosted.org/packages/a5/43/8479c7e30a5bf6ddbc3c84aec45ec5a31b10e312e2c5a97524f9f287a7a9/django-tools-0.54.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2022-09-15 08:02:05",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "github_user": "jedie",
    "github_project": "django-tools",
    "travis_ci": true,
    "coveralls": false,
    "github_actions": true,
    "lcname": "django-tools"
}
        
Elapsed time: 0.46135s