[Python-modules-commits] [python-django-extensions] 01/08: Import python-django-extensions_1.5.7.orig.tar.gz
Brian May
bam at moszumanska.debian.org
Sat Oct 24 06:50:19 UTC 2015
This is an automated email from the git hooks/post-receive script.
bam pushed a commit to branch master
in repository python-django-extensions.
commit ab5069fe8d44daa6b3fc004252476a00ed6c3f63
Author: Brian May <brian at linuxpenguins.xyz>
Date: Sat Oct 24 14:45:21 2015 +1100
Import python-django-extensions_1.5.7.orig.tar.gz
---
.editorconfig | 18 ++
.gitignore | 5 +-
.travis.yml | 87 +++----
CHANGELOG.md | 107 +++++++++
MANIFEST.in | 1 -
README.rst | 11 +-
conftest.py | 72 ++++++
django_extensions/__init__.py | 2 +-
django_extensions/admin/__init__.py | 18 +-
django_extensions/admin/filter.py | 59 +++++
django_extensions/admin/widgets.py | 7 +-
django_extensions/compat.py | 95 ++++++++
.../app_template/migrations/__init__.py.tmpl} | 0
django_extensions/db/fields/__init__.py | 266 ++++++++++++++++-----
django_extensions/db/fields/encrypted.py | 7 +-
django_extensions/db/fields/json.py | 6 +-
django_extensions/db/models.py | 21 +-
django_extensions/future_1_5.py | 16 --
django_extensions/jobs/daily/cache_cleanup.py | 4 +-
.../management/commands => logging}/__init__.py | 0
django_extensions/logging/filters.py | 35 +++
django_extensions/management/base.py | 1 -
django_extensions/management/color.py | 8 +
.../management/commands/admin_generator.py | 32 ++-
django_extensions/management/commands/clean_pyc.py | 9 +-
.../management/commands/compile_pyc.py | 9 +-
.../management/commands/create_app.py | 18 +-
.../management/commands/create_command.py | 6 +-
.../management/commands/create_jobs.py | 4 +-
.../management/commands/create_template_tags.py | 8 +-
.../management/commands/describe_form.py | 3 +-
.../management/commands/drop_test_database.py | 11 +-
.../management/commands/dumpscript.py | 17 +-
.../management/commands/export_emails.py | 12 +-
.../management/commands/find_template.py | 6 +-
.../management/commands/generate_secret_key.py | 3 +-
.../management/commands/graph_models.py | 9 +-
.../management/commands/mail_debug.py | 13 +-
django_extensions/management/commands/notes.py | 9 +-
django_extensions/management/commands/passwd.py | 11 +-
.../management/commands/pipchecker.py | 50 ++--
.../management/commands/print_settings.py | 6 +-
.../management/commands/print_user_for_session.py | 10 +-
django_extensions/management/commands/reset_db.py | 9 +-
django_extensions/management/commands/runjob.py | 4 +-
django_extensions/management/commands/runjobs.py | 8 +-
.../management/commands/runprofileserver.py | 10 +-
django_extensions/management/commands/runscript.py | 13 +-
.../management/commands/runserver_plus.py | 104 +++++++-
.../management/commands/set_default_site.py | 2 +-
.../management/commands/set_fake_emails.py | 13 +-
.../management/commands/set_fake_passwords.py | 7 +-
.../management/commands/shell_plus.py | 165 ++++++++++---
.../management/commands/show_templatetags.py | 5 +-
django_extensions/management/commands/show_urls.py | 122 ++++++----
django_extensions/management/commands/sqlcreate.py | 7 +-
django_extensions/management/commands/sqldiff.py | 34 ++-
django_extensions/management/commands/sqldsn.py | 143 +++++++++++
django_extensions/management/commands/sync_s3.py | 14 +-
django_extensions/management/commands/syncdata.py | 12 +-
.../management/commands/unreferenced_files.py | 3 +-
.../management/commands/update_permissions.py | 3 +-
.../management/commands/validate_templates.py | 19 +-
.../management/email_notifications.py | 4 +-
django_extensions/management/modelviz.py | 48 ++--
django_extensions/management/shells.py | 47 ++--
django_extensions/management/technical_response.py | 1 -
django_extensions/management/utils.py | 33 +--
django_extensions/mongodb/fields/encrypted.py | 4 +-
django_extensions/mongodb/fields/json.py | 18 +-
django_extensions/mongodb/models.py | 10 +-
django_extensions/settings.py | 1 +
django_extensions/templatetags/highlighting.py | 18 +-
django_extensions/templatetags/syntax_color.py | 17 +-
django_extensions/templatetags/truncate_letters.py | 1 -
django_extensions/templatetags/widont.py | 2 +
django_extensions/tests/__init__.py | 26 --
django_extensions/tests/shortuuid_field.py | 39 ---
django_extensions/tests/utils.py | 76 ------
django_extensions/utils/dia2django.py | 11 +-
django_extensions/utils/text.py | 1 -
django_extensions/utils/validatingtemplatetags.py | 3 +-
docs/AUTHORS | 1 +
docs/admin_extensions.rst | 19 ++
docs/command_extensions.rst | 5 +
docs/conf.py | 2 +-
docs/field_extensions.rst | 18 ++
docs/index.rst | 3 +-
docs/model_extensions.rst | 7 +-
docs/runscript.rst | 2 +-
docs/runserver_plus.rst | 38 ++-
docs/shell_plus.rst | 25 ++
docs/sqldsn.rst | 52 ++++
run_tests.py | 141 -----------
setup.cfg | 14 ++
setup.py | 37 ++-
.../tests/testapp => tests}/__init__.py | 0
.../tests => tests}/management/__init__.py | 0
.../management/commands}/__init__.py | 0
.../management/commands/error_raising_command.py | 2 -
.../fields.py => tests/test_autoslug_fields.py | 73 +++---
.../tests => tests}/test_clean_pyc.py | 21 +-
.../tests => tests}/test_compile_pyc.py | 35 ++-
.../tests => tests}/test_dumpscript.py | 30 +--
.../test_encrypted_fields.py | 88 +++----
.../json_field.py => tests/test_json_field.py | 13 +-
.../test_management_command.py | 34 ++-
{django_extensions/tests => tests}/test_models.py | 3 +-
tests/test_randomchar_field.py | 92 +++++++
tests/test_runscript.py | 33 +++
tests/test_shortuuid_field.py | 29 +++
.../tests => tests}/test_templatetags.py | 1 -
tests/test_utils.py | 26 ++
.../uuid_field.py => tests/test_uuid_field.py | 20 +-
.../tests/management => tests/testapp}/__init__.py | 0
tests/testapp/apps.py | 8 +
.../tests => tests}/testapp/models.py | 81 ++++++-
.../testapp/scripts}/__init__.py | 0
tests/testapp/scripts/sample_script.py | 2 +
{django_extensions/tests => tests}/testapp/urls.py | 0
tox.ini | 103 +++-----
121 files changed, 2160 insertions(+), 1047 deletions(-)
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..5c99f65
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,18 @@
+# http://editorconfig.org
+# Source: pydanny cookiecutter-django repo
+
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.{py,rst,ini}]
+indent_style = space
+indent_size = 4
+
+[*.yml]
+indent_style = space
+indent_size = 2
diff --git a/.gitignore b/.gitignore
index ca4ca99..8d268cd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,8 +6,11 @@ build
dist
docs/_build
docs/_static
-venv
+venv*
*.egg-info
.tox
*.bak
.DS_Store
+.eggs/
+.coverage
+
diff --git a/.travis.yml b/.travis.yml
index b36720f..8a3dc3b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,52 +1,53 @@
language: python
-python:
- - 2.6
- - 2.7
- - 3.3
- - 3.4
- - pypy
- - pypy3
+sudo: false
env:
- - DJANGO=Django==1.4
- - DJANGO=Django==1.5
- - DJANGO=Django==1.6
- - DJANGO=Django==1.7
- - DJANGO=Django==1.8a1
- - DJANGO="git+git://github.com/django/django.git@master#egg=django"
+ - TOX_ENV=py27-flake8
+ - TOX_ENV=py34-flake8
+ - TOX_ENV=py27-dj14
+ - TOX_ENV=py27-dj15
+ - TOX_ENV=py27-dj16
+ - TOX_ENV=py32-dj15
+ - TOX_ENV=py32-dj16
+ - TOX_ENV=py33-dj15
+ - TOX_ENV=py33-dj16
+ - TOX_ENV=py34-dj15
+ - TOX_ENV=py34-dj16
+ - TOX_ENV=py27-dj17
+ - TOX_ENV=py27-dj18
+ - TOX_ENV=py27-djmaster
+ - TOX_ENV=py32-dj17
+ - TOX_ENV=py32-dj18
+ - TOX_ENV=py32-djmaster
+ - TOX_ENV=py33-dj17
+ - TOX_ENV=py33-dj18
+ - TOX_ENV=py33-djmaster
+ - TOX_ENV=py34-dj17
+ - TOX_ENV=py34-dj18
+ - TOX_ENV=py34-djmaster
+ - TOX_ENV=pypy-dj17
+ - TOX_ENV=pypy-dj18
+ - TOX_ENV=pypy-djmaster
+ - TOX_ENV=pypy3-dj17
+ - TOX_ENV=pypy3-dj18
+ - TOX_ENV=pypy3-djmaster
+
+matrix:
+ fast_finish: true
+ allow_failures:
+ - env: TOX_ENV=py34-djmaster
+ - env: TOX_ENV=py33-djmaster
+ - env: TOX_ENV=py32-djmaster
+ - env: TOX_ENV=py27-djmaster
+ - env: TOX_ENV=pypy-djmaster
+ - env: TOX_ENV=pypy3-djmaster
install:
- - pip install -q $DJANGO
- - pip install flake8
- - pip install python-dateutil
- - if [[ $TRAVIS_PYTHON_VERSION < '3.0' ]]; then pip install --pre -q python-keyczar; fi
- - pip install -q PyYAML shortuuid
- - pip install -e .
+ - pip install tox coveralls
script:
- - flake8 --ignore=E265,E501,W391 .
- - python setup.py test
+ - tox -e $TOX_ENV
-matrix:
- exclude:
- - python: 2.6
- env: DJANGO=Django==1.7
- - python: 2.6
- env: DJANGO=Django==1.8a1
- - python: 2.6
- env: DJANGO="git+git://github.com/django/django.git@master#egg=django"
- - python: 3.3
- env: DJANGO=Django==1.4
- - python: 3.4
- env: DJANGO=Django==1.4
- - python: pypy
- env: DJANGO=Django==1.4
- - python: pypy
- env: DJANGO=Django==1.5
- - python: pypy3
- env: DJANGO=Django==1.4
- - python: pypy3
- env: DJANGO=Django==1.5
- allow_failures:
- - env: DJANGO="git+git://github.com/django/django.git@master#egg=django"
+after_success:
+ - coveralls
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cd2122e..5303e1c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,113 @@
Changelog
=========
+1.5.7
+-----
+
+Changes:
+ - Fix: CreationDateTimeField, migration error
+ - Fix: ModificationDateTimeField, migration error
+ - Fix: shell_plus, options is not always in db config dictionary
+ - Fix: admin filters, contrib.admin.util fallback code
+ - Fix: graph_models, currectly support parsing lists for cli options
+ - Improvement: sqldsn, support postfix
+ - Improvement: utils, remove get_project_root function
+
+
+1.5.6
+-----
+
+Changes:
+ - New: RandomCharField, prepopulates random character string
+ - New: (Not)NullFieldListFilter, filters for admin
+ - New: runserver_plus, integrate with django-pdb
+ - New: runserver_plus, add check_migrations from Django
+ - Improvement: show_urls, nested namespace support
+ - Improvement: show_urls, allow to specify alternative urlconf
+ - Improvement: show_urls, support i18n_patterns
+ - Improvement: show_urls, use --language to filter on a particular language
+ - Improvement: admin_generator, added docstrings to module
+ - Improvement: shell_plus, allow cli arguments to be passed to ipython
+ - Improvement: shell_plus, fixed PYTHONPATH bug when using django-admin shell_plus --notebook
+ - Improvement: shell_plus, set application_name on PostgreSQL databases
+ - Improvement: shell_plus, load user pypython config file
+ - Improvement: CreationDateTimeField, use auto_now_add instead of default ModificationDateTimeField
+ - Improvement: ModificationDateTimeField, use auto_now instead of pre_save method
+ - Improvement: ForeignKeyAutocompleteAdmin, added ability to filter autocomplete query
+ - Fix: shell_plus, support for pypython>=0.27
+ - Fix: shell_plus, load apps and models directly through the apps interface when available
+ - Fix: shell_plus, use ipython start_ipython instead of embed
+ - Fix: shell_plus, fix swalling ImportErrors with IPython 3 and higher
+ - Fix: dumpscript, fix missing imports in dumped script
+ - Fix: admin_generator, fix issues with Django 1.9
+ - Fix: template tags, move exception for import failure to inside of the template tags
+ - Fix: reset_db, fix for Django 1.9
+ - Fix: runserver_plus, fix for Django 1.9
+
+
+1.5.5
+-----
+
+Changes:
+ - Fix: sqldiff, previous Django 1.8 fix was slightly broken
+
+
+1.5.4
+-----
+
+Changes:
+ - Improvement: syncdata, add skip-remove option
+ - Improvement: logging, report how often mail was ratelimited
+ - Fix: admin, Django 1.8 compatibility module_name is now called model_name
+ - Fix: notes, Python 3.x fix force output of filter into list
+ - Fix: sqldiff, fix for Django 1.8
+
+
+1.5.3
+-----
+
+Changes:
+ - New: ratelimiter, a simple ratelimiter filter for Python logging
+ - Fix: various improvements for Django 1.8
+ - Fix: sync_s3, use os.walk instead of os.path.walk (py3 fix)
+ - Improvement: pipchecker, use name instead of url_name to fix casing mismatches
+ - Improvement: pipchecker, use https
+ - Improvement: pipchecker, fix issues with new(er) pip versions
+ - Docs: fixed a few typos
+ - Docs: added documentation about NOTEBOOK_ARGUMENTS settings
+
+
+1.5.2
+-----
+
+Changes:
+ - New: sqldsn, prints Data Source Name for defined database(s)
+ - Fix: graph_models, Django 1.8 support
+ - Fix: highlighting tag, fix usage of is_safe
+ - Fix: runscript, fix for runscript with AppConfig apps
+ - Fix: sqldiff, KeyError when index is missing in database
+ - Fix: sqldiff, multi column indexes was also counted as a single colomn index
+ - Improvements: JSONField, Added try/catch for importing json/simplejson for Django 1.7
+
+
+1.5.1
+-----
+
+Changes:
+ - New: runserver_plus, add support for --extra-files parameter
+ - Fix: Django 1.7 defined MIDDLEWARE_CLASSES for tests
+ - Fix: shell_plus, problem when auto-loading modules with empty '__module__' property
+ - Improvement: shell_plus, IPython 3.x support for notebooks
+ - Improvement: tests, move to py.test and lots of other improvements
+ - Improvement: create_app, add migrations folder
+ - Improvement: tox.ini, refactored to be more DRY
+ - Improvement: runserver_plus, also reload on changes to translation files
+ - Improvement: runserver_plus, add reloader_interval support
+ - Improvement: create_template_tags, removed unusued command line option
+ - Docs: print_user_for_session, add note about SESSION_ENGINE
+ - Docs: runserver_plus, added section about IO calls and CPU usage
+
+
1.5.0
-----
diff --git a/MANIFEST.in b/MANIFEST.in
index f9b717a..c9207cb 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -4,5 +4,4 @@ recursive-include django_extensions/static *
recursive-include django_extensions/locale *
recursive-include docs *
include LICENSE
-include run_tests.py
include tox.ini
diff --git a/README.rst b/README.rst
index 06d839d..c92ae51 100644
--- a/README.rst
+++ b/README.rst
@@ -17,6 +17,9 @@
:target: https://pypi.python.org/pypi/django-extensions/
:alt: Number of PyPI downloads
+.. image:: https://coveralls.io/repos/django-extensions/django-extensions/badge.png?branch=master
+ :target: https://coveralls.io/r/django-extensions/django-extensions?branch=master
+ :alt: Coverage
Django Extensions is a collection of custom extensions for the Django Framework.
@@ -80,11 +83,11 @@ Check templates for rendering errors::
$ python manage.py validate_templates
-Run the enchanced django shell::
+Run the enhanced django shell::
$ python manage.py shell_plus
-Run the enchanced django runserver, (requires Werkzeug install)::
+Run the enhanced django runserver, (requires Werkzeug install)::
$ python manage.py runserver_plus
@@ -124,7 +127,6 @@ go towards the Django and Python foundations.
We have setup a couple of ways you can donate towards Django Extensions using the buttons below:
- Bountysource
- - Bitcoins at address: 13V3AaapCRz2i2HnJiFoqVECe1t5H26yCg
- Gratipay (formerly Gittip)
- Flattr
- PayPal
@@ -134,9 +136,6 @@ We have setup a couple of ways you can donate towards Django Extensions using th
:target: https://www.bountysource.com/teams/django-extensions/bounties?utm_source=django-extensions&utm_medium=shield&utm_campaign=bounties_posted
:alt: BountySource
-.. image:: https://img.shields.io/bitcoin/donate.png
- :target: bitcoin:13V3AaapCRz2i2HnJiFoqVECe1t5H26yCg?label=DjangoExtensions
-
.. image:: https://img.shields.io/flattr/donate.png
:target: https://flattr.com/submit/auto?user_id=Trbs&url=https%3A%2F%2Fgithub.com%2Fdjango-extensions%2Fdjango-extensions
:alt: Flattr this
diff --git a/conftest.py b/conftest.py
new file mode 100644
index 0000000..3091bdd
--- /dev/null
+++ b/conftest.py
@@ -0,0 +1,72 @@
+from django.conf import settings
+
+
+def pytest_configure():
+ import sys
+
+ try:
+ import django # NOQA
+ except ImportError:
+ print("Error: missing test dependency:")
+ print(" django library is needed to run test suite")
+ print(" you can install it with 'pip install django'")
+ print(" or use tox to automatically handle test dependencies")
+ sys.exit(1)
+
+ try:
+ import shortuuid # NOQA
+ except ImportError:
+ print("Error: missing test dependency:")
+ print(" shortuuid library is needed to run test suite")
+ print(" you can install it with 'pip install shortuuid'")
+ print(" or use tox to automatically handle test dependencies")
+ sys.exit(1)
+
+ try:
+ import dateutil # NOQA
+ except ImportError:
+ print("Error: missing test dependency:")
+ print(" dateutil library is needed to run test suite")
+ print(" you can install it with 'pip install python-dateutil'")
+ print(" or use tox to automatically handle test dependencies")
+ sys.exit(1)
+
+ try:
+ import six # NOQA
+ except ImportError:
+ print("Error: missing test dependency:")
+ print(" six library is needed to run test suite")
+ print(" you can install it with 'pip install six'")
+ print(" or use tox to automatically handle test dependencies")
+ sys.exit(1)
+
+ # Dynamically configure the Django settings with the minimum necessary to
+ # get Django running tests.
+ settings.configure(
+ INSTALLED_APPS=[
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.admin',
+ 'django.contrib.sessions',
+ 'tests.testapp',
+ 'django_extensions',
+ ],
+ MIDDLEWARE_CLASSES=(
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ ),
+ # Django replaces this, but it still wants it. *shrugs*
+ DATABASE_ENGINE='django.db.backends.sqlite3',
+ DATABASES={
+ 'default': {
+ 'ENGINE': 'django.db.backends.sqlite3',
+ 'NAME': ':memory:',
+ }
+ },
+ MEDIA_ROOT='/tmp/django_extensions_test_media/',
+ MEDIA_PATH='/media/',
+ ROOT_URLCONF='tests.urls',
+ DEBUG=True,
+ TEMPLATE_DEBUG=True,
+ )
diff --git a/django_extensions/__init__.py b/django_extensions/__init__.py
index 65ff1ac..03c8095 100644
--- a/django_extensions/__init__.py
+++ b/django_extensions/__init__.py
@@ -1,5 +1,5 @@
-VERSION = (1, 5, 0)
+VERSION = (1, 5, 7)
# Dynamically calculate the version based on VERSION tuple
if len(VERSION) > 2 and VERSION[2] is not None:
diff --git a/django_extensions/admin/__init__.py b/django_extensions/admin/__init__.py
index 46e10f4..019c745 100644
--- a/django_extensions/admin/__init__.py
+++ b/django_extensions/admin/__init__.py
@@ -5,6 +5,7 @@ import six
import operator
from six.moves import reduce
+import django
from django.http import HttpResponse, HttpResponseNotFound
from django.conf import settings
from django.db import models
@@ -60,7 +61,12 @@ class ForeignKeyAutocompleteAdmin(ModelAdmin):
return self.admin_site.admin_view(view)(*args, **kwargs)
return update_wrapper(wrapper, view)
- info = self.model._meta.app_label, self.model._meta.module_name
+ # model._meta.module_name is deprecated in django version 1.7 and removed in django version 1.8.
+ # It is replaced by model._meta.model_name
+ if django.VERSION < (1, 7):
+ info = self.model._meta.app_label, self.model._meta.module_name
+ else:
+ info = self.model._meta.app_label, self.model._meta.model_name
urlpatterns = patterns('', url(r'foreignkey_autocomplete/$', wrap(self.foreignkey_autocomplete), name='%s_%s_autocomplete' % info))
urlpatterns += super(ForeignKeyAutocompleteAdmin, self).get_urls()
@@ -107,6 +113,10 @@ class ForeignKeyAutocompleteAdmin(ModelAdmin):
other_qs = other_qs.filter(reduce(operator.or_, or_queries))
queryset = queryset & other_qs
+ additional_filter = self.get_related_filter(model, request)
+ if additional_filter:
+ queryset = queryset.filter(additional_filter)
+
if self.autocomplete_limit:
queryset = queryset[:self.autocomplete_limit]
@@ -121,6 +131,12 @@ class ForeignKeyAutocompleteAdmin(ModelAdmin):
return HttpResponse(data)
return HttpResponseNotFound()
+ def get_related_filter(self, model, request):
+ """Given a model class and current request return an optional Q object
+ that should be applied as an additional filter for autocomplete query.
+ If no additional filtering is needed, this method should return
+ None."""
+
def get_help_text(self, field_name, model_name):
searchable_fields = self.related_search_fields.get(field_name, None)
if searchable_fields:
diff --git a/django_extensions/admin/filter.py b/django_extensions/admin/filter.py
new file mode 100644
index 0000000..571e235
--- /dev/null
+++ b/django_extensions/admin/filter.py
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.contrib.admin import FieldListFilter
+try:
+ from django.contrib.admin.utils import prepare_lookup_value
+except ImportError:
+ # django < 1.7
+ from django.contrib.admin.util import prepare_lookup_value
+from django.utils.translation import ugettext_lazy as _
+
+
+class NullFieldListFilter(FieldListFilter):
+ def __init__(self, field, request, params, model, model_admin, field_path):
+ self.lookup_kwarg = '{0}__isnull'.format(field_path)
+ super(NullFieldListFilter, self).__init__(field, request, params, model, model_admin, field_path)
+ lookup_choices = self.lookups(request, model_admin)
+ self.lookup_choices = () if lookup_choices is None else list(lookup_choices)
+
+ def expected_parameters(self):
+ return [self.lookup_kwarg]
+
+ def value(self):
+ return self.used_parameters.get(self.lookup_kwarg, None)
+
+ def lookups(self, request, model_admin):
+ return (
+ ('1', _('Yes')),
+ ('0', _('No')),
+ )
+
+ def choices(self, cl):
+ yield {
+ 'selected': self.value() is None,
+ 'query_string': cl.get_query_string({}, [self.lookup_kwarg]),
+ 'display': _('All'),
+ }
+ for lookup, title in self.lookup_choices:
+ yield {
+ 'selected': self.value() == prepare_lookup_value(self.lookup_kwarg, lookup),
+ 'query_string': cl.get_query_string({
+ self.lookup_kwarg: lookup,
+ }, []),
+ 'display': title,
+ }
+
+ def queryset(self, request, queryset):
+ if self.value() is not None:
+ kwargs = {self.lookup_kwarg: self.value()}
+ return queryset.filter(**kwargs)
+ return queryset
+
+
+class NotNullFieldListFilter(NullFieldListFilter):
+ def lookups(self, request, model_admin):
+ return (
+ ('0', _('Yes')),
+ ('1', _('No')),
+ )
diff --git a/django_extensions/admin/widgets.py b/django_extensions/admin/widgets.py
index e48e090..01cec62 100644
--- a/django_extensions/admin/widgets.py
+++ b/django_extensions/admin/widgets.py
@@ -1,12 +1,11 @@
-import six
import django
-
+import six
from django import forms
from django.contrib.admin.sites import site
+from django.contrib.admin.widgets import ForeignKeyRawIdWidget
+from django.template.loader import render_to_string
from django.utils.safestring import mark_safe
from django.utils.text import Truncator
-from django.template.loader import render_to_string
-from django.contrib.admin.widgets import ForeignKeyRawIdWidget
class ForeignKeySearchInput(ForeignKeyRawIdWidget):
diff --git a/django_extensions/compat.py b/django_extensions/compat.py
new file mode 100644
index 0000000..01017ce
--- /dev/null
+++ b/django_extensions/compat.py
@@ -0,0 +1,95 @@
+from __future__ import unicode_literals
+
+import sys
+
+import django
+from django.conf import settings
+
+# flake8: noqa
+
+#
+# Python compatibility
+#
+PY3 = sys.version_info[0] == 3
+OLD_PY2 = sys.version_info[:2] < (2, 7)
+
+if PY3: # pragma: no cover
+ from io import StringIO
+ import importlib
+
+elif OLD_PY2: # pragma: no cover
+ from cStringIO import StringIO
+ from django.utils import importlib
+
+else: # pragma: no cover
+ from cStringIO import StringIO
+ import importlib
+
+#
+# Django compatibility
+#
+try: # Django 1.5
+ from django.contrib.auth import get_user_model
+except ImportError: # pragma: no cover
+ assert django.VERSION < (1, 5)
+ from django.contrib.auth.models import User
+ User.USERNAME_FIELD = "username"
+ User.get_username = lambda self: self.username
+
+ def get_user_model():
+ return User
+
+
+def list_apps():
+ try:
+ # django >= 1.7, to support AppConfig
+ from django.apps import apps
+ return [app.name for app in apps.get_app_configs()]
+ except ImportError:
+ # old way
+ return settings.INSTALLED_APPS
+
+
+def get_apps():
+ try:
+ # django >= 1.7, to support AppConfig
+ from django.apps import apps
+ return [app.models_module for app in apps.get_app_configs() if app.models_module]
+ except ImportError:
+ from django.db import models
+ return models.get_apps()
+
+
+def get_app_models(app_labels=None):
+ if app_labels is None:
+ try:
+ # django >= 1.7, to support AppConfig
+ from django.apps import apps
+ return apps.get_models(include_auto_created=True)
+ except ImportError:
+ from django.db import models
+ return models.get_models(include_auto_created=True)
+
+ if not isinstance(app_labels, (list, tuple, set)):
+ app_labels = [app_labels]
+
+ app_models = []
+ try:
+ # django >= 1.7, to support AppConfig
+ from django.apps import apps
+
+ for app_label in app_labels:
+ app_config = apps.get_app_config(app_label)
+ app_models.extend(app_config.get_models(include_auto_created=True))
+ except ImportError:
+ from django.db import models
+
+ try:
+ app_list = [models.get_app(app_label) for app_label in app_labels]
+ except (models.ImproperlyConfigured, ImportError) as e:
+ raise CommandError("%s. Are you sure your INSTALLED_APPS setting is correct?" % e)
+
+ for app in app_list:
+ app_models.extend(models.get_models(app, include_auto_created=True))
+
+ return app_models
diff --git a/django_extensions/tests/management/__init__.py b/django_extensions/conf/app_template/migrations/__init__.py.tmpl
similarity index 100%
copy from django_extensions/tests/management/__init__.py
copy to django_extensions/conf/app_template/migrations/__init__.py.tmpl
diff --git a/django_extensions/db/fields/__init__.py b/django_extensions/db/fields/__init__.py
index d68c04e..d48774d 100644
--- a/django_extensions/db/fields/__init__.py
+++ b/django_extensions/db/fields/__init__.py
@@ -3,6 +3,7 @@ Django Extensions additional model fields
"""
import re
import six
+import string
import warnings
try:
@@ -18,8 +19,9 @@ except ImportError:
HAS_SHORT_UUID = False
from django.core.exceptions import ImproperlyConfigured
-from django.template.defaultfilters import slugify
from django.db.models import DateTimeField, CharField, SlugField
+from django.utils.crypto import get_random_string
+from django.template.defaultfilters import slugify
try:
from django.utils.timezone import now as datetime_now
@@ -34,7 +36,45 @@ except ImportError:
from django.utils.encoding import force_text as force_unicode # NOQA
-class AutoSlugField(SlugField):
+MAX_UNIQUE_QUERY_ATTEMPTS = 100
+
+
+class UniqueFieldMixin(object):
+
+ def check_is_bool(self, attrname):
+ if not isinstance(getattr(self, attrname), bool):
+ raise ValueError("'{}' argument must be True or False".format(attrname))
+
+ def get_queryset(self, model_cls, slug_field):
+ for field, model in model_cls._meta.get_fields_with_model():
+ if model and field == slug_field:
+ return model._default_manager.all()
+ return model_cls._default_manager.all()
+
+ def find_unique(self, model_instance, field, iterator, *args):
+ # exclude the current model instance from the queryset used in finding
+ # next valid hash
+ queryset = self.get_queryset(model_instance.__class__, field)
+ if model_instance.pk:
+ queryset = queryset.exclude(pk=model_instance.pk)
+
+ # form a kwarg dict used to impliment any unique_together contraints
+ kwargs = {}
+ for params in model_instance._meta.unique_together:
+ if self.attname in params:
+ for param in params:
+ kwargs[param] = getattr(model_instance, param, None)
+
+ new = six.next(iterator)
+ kwargs[self.attname] = new
+ while not new or queryset.filter(**kwargs):
+ new = six.next(iterator)
+ kwargs[self.attname] = new
+ setattr(model_instance, self.attname, new)
+ return new
+
+
+class AutoSlugField(UniqueFieldMixin, SlugField):
""" AutoSlugField
By default, sets editable=False, blank=True.
@@ -68,11 +108,9 @@ class AutoSlugField(SlugField):
self.slugify_function = kwargs.pop('slugify_function', slugify)
self.separator = kwargs.pop('separator', six.u('-'))
self.overwrite = kwargs.pop('overwrite', False)
- if not isinstance(self.overwrite, bool):
- raise ValueError("'overwrite' argument must be True or False")
+ self.check_is_bool('overwrite')
self.allow_duplicates = kwargs.pop('allow_duplicates', False)
- if not isinstance(self.allow_duplicates, bool):
- raise ValueError("'allow_duplicates' argument must be True or False")
+ self.check_is_bool('allow_duplicates')
super(AutoSlugField, self).__init__(*args, **kwargs)
def _slug_strip(self, value):
@@ -87,17 +125,25 @@ class AutoSlugField(SlugField):
value = re.sub('%s+' % re_sep, self.separator, value)
return re.sub(r'^%s+|%s+$' % (re_sep, re_sep), '', value)
- def get_queryset(self, model_cls, slug_field):
- for field, model in model_cls._meta.get_fields_with_model():
- if model and field == slug_field:
- return model._default_manager.all()
- return model_cls._default_manager.all()
-
def slugify_func(self, content):
if content:
return self.slugify_function(content)
return ''
+ def slug_generator(self, original_slug, start):
+ yield original_slug
+ for i in range(start, MAX_UNIQUE_QUERY_ATTEMPTS):
+ slug = original_slug
+ end = '%s%s' % (self.separator, i)
+ end_len = len(end)
+ if self.slug_len and len(slug) + end_len > self.slug_len:
+ slug = slug[:self.slug_len - end_len]
+ slug = self._slug_strip(slug)
+ slug = '%s%s' % (slug, end)
+ yield slug
+ raise RuntimeError('max slug attempts for %s exceeded (%s)' %
+ (original_slug, MAX_UNIQUE_QUERY_ATTEMPTS))
+
def create_slug(self, model_instance, add):
# get fields to populate from and slug field to set
if not isinstance(self._populate_from, (list, tuple)):
@@ -108,7 +154,7 @@ class AutoSlugField(SlugField):
# slugify the original field content and set next step to 2
slug_for_field = lambda field: self.slugify_func(getattr(model_instance, field))
slug = self.separator.join(map(slug_for_field, self._populate_from))
- next = 2
+ start = 2
else:
# get slug from the current model instance
slug = getattr(model_instance, self.attname)
@@ -118,46 +164,20 @@ class AutoSlugField(SlugField):
# strip slug depending on max_length attribute of the slug field
# and clean-up
- slug_len = slug_field.max_length
- if slug_len:
- slug = slug[:slug_len]
+ self.slug_len = slug_field.max_length
+ if self.slug_len:
+ slug = slug[:self.slug_len]
slug = self._slug_strip(slug)
original_slug = slug
if self.allow_duplicates:
return slug
- # exclude the current model instance from the queryset used in finding
- # the next valid slug
- queryset = self.get_queryset(model_instance.__class__, slug_field)
- if model_instance.pk:
- queryset = queryset.exclude(pk=model_instance.pk)
-
- # form a kwarg dict used to impliment any unique_together contraints
- kwargs = {}
- for params in model_instance._meta.unique_together:
- if self.attname in params:
- for param in params:
- kwargs[param] = getattr(model_instance, param, None)
- kwargs[self.attname] = slug
-
- # increases the number while searching for the next valid slug
- # depending on the given slug, clean-up
- while not slug or queryset.filter(**kwargs):
- slug = original_slug
- end = '%s%s' % (self.separator, next)
- end_len = len(end)
- if slug_len and len(slug) + end_len > slug_len:
- slug = slug[:slug_len - end_len]
- slug = self._slug_strip(slug)
- slug = '%s%s' % (slug, end)
- kwargs[self.attname] = slug
- next += 1
- return slug
+ return super(AutoSlugField, self).find_unique(
+ model_instance, slug_field, self.slug_generator(original_slug, start))
def pre_save(self, model_instance, add):
value = force_unicode(self.create_slug(model_instance, add))
- setattr(model_instance, self.attname, value)
return value
def get_internal_type(self):
@@ -190,16 +210,149 @@ class AutoSlugField(SlugField):
return name, path, args, kwargs
+class RandomCharField(UniqueFieldMixin, CharField):
+ """ RandomCharField
+
+ By default, sets editable=False, blank=True, unique=False.
+
+ Required arguments:
+
+ length
+ Specifies the length of the field
+
+ Optional arguments:
+
+ unique
+ If set to True, duplicate entries are not allowed (default: False)
+
+ lowercase
+ If set to True, lowercase the alpha characters (default: False)
+
+ uppercase
+ If set to True, uppercase the alpha characters (default: False)
+
+ include_alpha
+ If set to True, include alpha characters (default: True)
+
+ include_digits
+ If set to True, include digit characters (default: True)
+
+ include_punctuation
+ If set to True, include punctuation characters (default: False)
+ """
+ def __init__(self, *args, **kwargs):
+ kwargs.setdefault('blank', True)
+ kwargs.setdefault('editable', False)
+
+ self.length = kwargs.pop('length', None)
+ if self.length is None:
+ raise ValueError("missing 'length' argument")
+ kwargs['max_length'] = self.length
+
+ self.lowercase = kwargs.pop('lowercase', False)
+ self.check_is_bool('lowercase')
+ self.uppercase = kwargs.pop('uppercase', False)
+ self.check_is_bool('uppercase')
+ if self.uppercase and self.lowercase:
+ raise ValueError("the 'lowercase' and 'uppercase' arguments are mutually exclusive")
... 4722 lines suppressed ...
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-django-extensions.git
More information about the Python-modules-commits
mailing list