Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into fix-constance-man…
Browse files Browse the repository at this point in the history
…agement-command-without-admin-installed
  • Loading branch information
syre committed Mar 30, 2023
2 parents 1c927ba + 5ab48e1 commit 26e5431
Show file tree
Hide file tree
Showing 29 changed files with 351 additions and 270 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: 3.8

Expand All @@ -26,7 +26,7 @@ jobs:
echo "::set-output name=dir::$(pip cache dir)"
- name: Cache
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: ${{ steps.pip-cache.outputs.dir }}
key: release-${{ hashFiles('**/setup.py') }}-${{ hashFiles('**/tox.ini') }}
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ jobs:
fail-fast: false
max-parallel: 5
matrix:
python-version: ['3.6', '3.7', '3.8', '3.9', '3.10', 'pypy-3.8']
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', 'pypy-3.8']

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

Expand All @@ -25,7 +25,7 @@ jobs:
echo "::set-output name=dir::$(pip cache dir)"
- name: Cache
uses: actions/cache@v2
uses: actions/cache@v3
with:
path: ${{ steps.pip-cache.outputs.dir }}
key:
Expand All @@ -36,13 +36,13 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install --upgrade tox tox-gh-actions
python -m pip install --upgrade 'tox<4' tox-gh-actions
- name: Tox tests
run: |
tox -v
- name: Upload coverage
uses: codecov/codecov-action@v1
uses: codecov/codecov-action@v3
with:
name: Python ${{ matrix.python-version }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ dist/
test.db
.tox
.coverage
coverage.xml
docs/_build
.idea
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ repos:
- id: python-check-blanket-noqa

- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.3.0
rev: v4.4.0
hooks:
- id: check-merge-conflict
- id: check-yaml
Expand Down
5 changes: 0 additions & 5 deletions constance/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import django
from django.utils.functional import LazyObject
from . import checks

__version__ = '2.9.1'

if django.VERSION < (3, 2): # pragma: no cover
default_app_config = 'constance.apps.ConstanceConfig'


class LazyConfig(LazyObject):
def _setup(self):
Expand Down
154 changes: 154 additions & 0 deletions constance/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,160 @@
config = LazyConfig()


NUMERIC_WIDGET = forms.TextInput(attrs={'size': 10})

INTEGER_LIKE = (fields.IntegerField, {'widget': NUMERIC_WIDGET})
STRING_LIKE = (fields.CharField, {
'widget': forms.Textarea(attrs={'rows': 3}),
'required': False,
})

FIELDS = {
bool: (fields.BooleanField, {'required': False}),
int: INTEGER_LIKE,
Decimal: (fields.DecimalField, {'widget': NUMERIC_WIDGET}),
str: STRING_LIKE,
datetime: (
fields.SplitDateTimeField, {'widget': widgets.AdminSplitDateTime}
),
timedelta: (
fields.DurationField, {'widget': widgets.AdminTextInputWidget}
),
date: (fields.DateField, {'widget': widgets.AdminDateWidget}),
time: (fields.TimeField, {'widget': widgets.AdminTimeWidget}),
float: (fields.FloatField, {'widget': NUMERIC_WIDGET}),
}


def parse_additional_fields(fields):
for key in fields:
field = list(fields[key])

if len(field) == 1:
field.append({})

field[0] = import_string(field[0])

if 'widget' in field[1]:
klass = import_string(field[1]['widget'])
field[1]['widget'] = klass(
**(field[1].get('widget_kwargs', {}) or {})
)

if 'widget_kwargs' in field[1]:
del field[1]['widget_kwargs']

fields[key] = field

return fields


FIELDS.update(parse_additional_fields(settings.ADDITIONAL_FIELDS))


def get_values():
"""
Get dictionary of values from the backend
:return:
"""

# First load a mapping between config name and default value
default_initial = ((name, options[0])
for name, options in settings.CONFIG.items())
# Then update the mapping with actually values from the backend
initial = dict(default_initial, **dict(config._backend.mget(settings.CONFIG)))

return initial


class ConstanceForm(forms.Form):
version = forms.CharField(widget=forms.HiddenInput)

def __init__(self, initial, request=None, *args, **kwargs):
super().__init__(*args, initial=initial, **kwargs)
version_hash = hashlib.sha256()

only_view = request and not request.user.has_perm('constance.change_config')
if only_view:
messages.warning(
request,
_("You don't have permission to change these values"),
)

for name, options in settings.CONFIG.items():
default = options[0]
if len(options) == 3:
config_type = options[2]
if config_type not in settings.ADDITIONAL_FIELDS and not isinstance(default, config_type):
raise ImproperlyConfigured(_("Default value type must be "
"equal to declared config "
"parameter type. Please fix "
"the default value of "
"'%(name)s'.")
% {'name': name})
else:
config_type = type(default)

if config_type not in FIELDS:
raise ImproperlyConfigured(_("Constance doesn't support "
"config values of the type "
"%(config_type)s. Please fix "
"the value of '%(name)s'.")
% {'config_type': config_type,
'name': name})
field_class, kwargs = FIELDS[config_type]
if only_view:
kwargs['disabled'] = True
self.fields[name] = field_class(label=name, **kwargs)

version_hash.update(smart_bytes(initial.get(name, '')))
self.initial['version'] = version_hash.hexdigest()

def save(self):
for file_field in self.files:
file = self.cleaned_data[file_field]
self.cleaned_data[file_field] = default_storage.save(file.name, file)

for name in settings.CONFIG:
current = getattr(config, name)
new = self.cleaned_data[name]

if isinstance(new, str):
new = normalize_newlines(new)

if conf.settings.USE_TZ and isinstance(current, datetime) and not timezone.is_aware(current):
current = timezone.make_aware(current)

if current != new:
setattr(config, name, new)

def clean_version(self):
value = self.cleaned_data['version']

if settings.IGNORE_ADMIN_VERSION_CHECK:
return value

if value != self.initial['version']:
raise forms.ValidationError(_('The settings have been modified '
'by someone else. Please reload the '
'form and resubmit your changes.'))
return value

def clean(self):
cleaned_data = super().clean()

if not settings.CONFIG_FIELDSETS:
return cleaned_data

missing_keys, extra_keys = get_inconsistent_fieldnames()
if missing_keys or extra_keys:
raise forms.ValidationError(_('CONSTANCE_CONFIG_FIELDSETS is missing '
'field(s) that exists in CONSTANCE_CONFIG.'))

return cleaned_data


>>>>>>> upstream/master
class ConstanceAdmin(admin.ModelAdmin):
change_list_template = 'admin/constance/change_list.html'
change_list_form = ConstanceForm
Expand Down
39 changes: 3 additions & 36 deletions constance/apps.py
Original file line number Diff line number Diff line change
@@ -1,45 +1,12 @@
from django.db.models import signals
from django.apps import apps, AppConfig
from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _


class ConstanceConfig(AppConfig):
name = 'constance'
verbose_name = _('Constance')
default_auto_field = 'django.db.models.AutoField'

def ready(self):
super().ready()
signals.post_migrate.connect(self.create_perm,
dispatch_uid='constance.create_perm')
from . import checks

def create_perm(self, using=None, *args, **kwargs):
"""
Creates a fake content type and permission
to be able to check for permissions
"""
from django.conf import settings

constance_dbs = getattr(settings, 'CONSTANCE_DBS', None)
if constance_dbs is not None and using not in constance_dbs:
return
if (
apps.is_installed('django.contrib.contenttypes') and
apps.is_installed('django.contrib.auth')
):
ContentType = apps.get_model('contenttypes.ContentType')
Permission = apps.get_model('auth.Permission')
content_type, created = ContentType.objects.using(using).get_or_create(
app_label='constance',
model='config',
)

Permission.objects.using(using).get_or_create(
content_type=content_type,
codename='change_config',
defaults={'name': 'Can change config'},
)
Permission.objects.using(using).get_or_create(
content_type=content_type,
codename='view_config',
defaults={'name': 'Can view config'},
)
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
)
from django.db.models.signals import post_save

from .. import Backend
from ... import settings, signals, config
from constance.backends import Backend
from constance import settings, signals, config


class DatabaseBackend(Backend):
def __init__(self):
from .models import Constance
from constance.models import Constance
self._model = Constance
self._prefix = settings.DATABASE_PREFIX
self._autofill_timeout = settings.DATABASE_CACHE_AUTOFILL_TIMEOUT
Expand Down
6 changes: 0 additions & 6 deletions constance/backends/database/apps.py

This file was deleted.

19 changes: 0 additions & 19 deletions constance/backends/database/migrations/0002_auto_20190129_2304.py

This file was deleted.

Loading

0 comments on commit 26e5431

Please sign in to comment.