Skip to content

Commit 2d105af

Browse files
authored
Merge branch 'develop' into ci-improvements
2 parents 17bcb56 + b69e4ad commit 2d105af

File tree

7 files changed

+54
-20
lines changed

7 files changed

+54
-20
lines changed

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Dynamic-preferences allow you to register settings (a.k.a. preferences) in a dec
3131

3232
With dynamic-preferences, you can update settings on the fly, through django's admin or custom forms, without restarting your application.
3333

34-
The project is tested and work under Python 3.6, 3.7, 3.8 and 3.9 and with django 2.2, 3.0 and 3.1.
34+
The project is tested and work under Python 3.7, 3.8, 3.9, 3.10 and 3.11 and with django 3.2 and 4.1.
3535

3636
Features
3737
--------

dynamic_preferences/admin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def section_name(self, obj):
7676

7777
def save_model(self, request, obj, form, change):
7878
pref = form.instance
79-
manager = pref.registry.manager()
79+
manager = pref.registry.manager(instance=getattr(obj, "instance", None))
8080
manager.update_db_pref(pref.section, pref.name, form.cleaned_data["raw_value"])
8181

8282

dynamic_preferences/managers.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -104,18 +104,22 @@ def many_from_cache(self, preferences):
104104
if k in cached
105105
}
106106

107-
def to_cache(self, pref):
107+
def to_cache(self, *prefs):
108108
"""
109-
Update/create the cache value for the given preference model instance
109+
Update/create the cache value for the given preference model instances
110110
"""
111-
key = self.get_cache_key(pref.section, pref.name)
112-
value = pref.raw_value
113-
if value is None or value == "":
114-
# some cache backends refuse to cache None or empty values
115-
# resulting in more DB queries, so we cache an arbitrary value
116-
# to ensure the cache is hot (even with empty values)
117-
value = preferences_settings.CACHE_NONE_VALUE
118-
self.cache.set(key, value)
111+
update_dict = {}
112+
for pref in prefs:
113+
key = self.get_cache_key(pref.section, pref.name)
114+
value = pref.raw_value
115+
if value is None or value == "":
116+
# some cache backends refuse to cache None or empty values
117+
# resulting in more DB queries, so we cache an arbitrary value
118+
# to ensure the cache is hot (even with empty values)
119+
value = preferences_settings.CACHE_NONE_VALUE
120+
update_dict[key] = value
121+
122+
self.cache.set_many(update_dict)
119123

120124
def pref_obj(self, section, name):
121125
return self.registry.get(section=section, name=name)
@@ -220,6 +224,8 @@ def load_from_db(self, cache=False):
220224
"""Return a dictionary of preferences by section directly from DB"""
221225
a = {}
222226
db_prefs = {p.preference.identifier(): p for p in self.queryset}
227+
cache_prefs = []
228+
223229
for preference in self.registry.preferences():
224230
try:
225231
db_pref = db_prefs[preference.identifier()]
@@ -232,8 +238,11 @@ def load_from_db(self, cache=False):
232238
else:
233239
# cache if create_db_pref() hasn't already done so
234240
if cache:
235-
self.to_cache(db_pref)
241+
cache_prefs.append(db_pref)
236242

237243
a[preference.identifier()] = db_pref.value
238244

245+
if cache_prefs:
246+
self.to_cache(*cache_prefs)
247+
239248
return a

dynamic_preferences/serializers.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import decimal
33
import os
44

5-
from datetime import date, timedelta, datetime, time
5+
from datetime import date, timedelta, datetime, time, timezone
66

77
from django.conf import settings
88
from django.core.validators import EMPTY_VALUES
@@ -15,7 +15,6 @@
1515
from django.utils.duration import duration_string
1616
from django.utils.encoding import force_str
1717
from django.utils.timezone import (
18-
utc,
1918
is_aware,
2019
make_aware,
2120
make_naive,
@@ -252,10 +251,14 @@ def to_db(self, value, **kwargs):
252251
# Support single instances in this serializer to allow
253252
# create_deletion_handler to work for model multiple choice preferences
254253
value = [value.pk]
255-
else:
254+
elif hasattr(value, 'values_list'):
256255
value = list(value.values_list("pk", flat=True))
257-
258-
if self.sort:
256+
elif isinstance(value, list) and len(value) > 0 and isinstance(value[0], self.model):
257+
# Handle lists of model instances
258+
value = [i.pk for i in value]
259+
else:
260+
raise ValueError(f'Cannot handle value {value} of type {type(value)}')
261+
if value and self.sort:
259262
value = sorted(value)
260263

261264
return self.separator.join(map(str, value))
@@ -411,7 +414,7 @@ def enforce_timezone(cls, value):
411414
if (field_timezone is not None) and not is_aware(value):
412415
return make_aware(value, field_timezone)
413416
elif (field_timezone is None) and is_aware(value):
414-
return make_naive(value, utc)
417+
return make_naive(value, timezone.utc)
415418
return value
416419

417420
@classmethod

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
six
2-
wheel==0.24.0
2+
wheel==0.38.1
33
persisting_theory==1.0

tests/test_serializers.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,3 +335,24 @@ def test_model_multiple_single_serialization_with_non_int_pk(blog_entries):
335335
blog_entry = BlogEntryWithNonIntPk.objects.all().first()
336336

337337
assert s.serialize(blog_entry) == s.separator.join(map(str, [blog_entry.pk]))
338+
339+
340+
341+
def test_model_multiple_to_db_empty(blog_entries):
342+
result = serializers.ModelMultipleSerializer(BlogEntry).to_db([])
343+
assert result is None
344+
345+
346+
def test_model_multiple_to_db_multiple(blog_entries):
347+
entry1 = BlogEntry.objects.get(title="This is a test",)
348+
entry2 = BlogEntry.objects.get(title="This is only a test",)
349+
result = serializers.ModelMultipleSerializer(BlogEntry).to_db([
350+
entry1,
351+
entry2,
352+
])
353+
assert result == f'{entry1.pk},{entry2.pk}'
354+
355+
356+
def test_model_multiple_to_db_invalid(blog_entries):
357+
with pytest.raises(ValueError, match=r"Cannot handle value.* of type .*"):
358+
serializers.ModelMultipleSerializer(BlogEntry).to_db('invalid')

tox.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ deps =
2424

2525

2626
basepython =
27+
py311: python3.11
2728
py310: python3.10
2829
py39: python3.9
2930
py38: python3.8

0 commit comments

Comments
 (0)