django-dynamic-preferences
django-dynamic-preferences copied to clipboard
'NoneType' object has no attribute '__name__'
I have installed the library, define a preference , but when try to use it in my code, I got this.
File "/home/gonzalo/Playground/tektank-ws/tektank/tektank/apps/entities/models.py", line 27, in _validate_dob
oag = global_preferences['general__oldest_allowed_age']
File "/home/gonzalo/.virtualenvs/tektank-ws/lib/python3.6/site-packages/dynamic_preferences/managers.py", line 29, in __getitem__
return self.get(key)
File "/home/gonzalo/.virtualenvs/tektank-ws/lib/python3.6/site-packages/dynamic_preferences/managers.py", line 133, in get
return self.from_cache(section, name)
File "/home/gonzalo/.virtualenvs/tektank-ws/lib/python3.6/site-packages/dynamic_preferences/managers.py", line 63, in from_cache
self.get_cache_key(section, name), CachedValueNotFound)
File "/home/gonzalo/.virtualenvs/tektank-ws/lib/python3.6/site-packages/dynamic_preferences/managers.py", line 57, in get_cache_key
return 'dynamic_preferences_{0}_{1}_{2}'.format(self.model.__name__, section, name)
AttributeError: 'NoneType' object has no attribute '__name__'
This is my code:
from dynamic_preferences.registries import global_preferences_registry
global_preferences = global_preferences_registry.manager()
def _validate_dob(value):
oag = global_preferences['general__oldest_allowed_age']
yag = global_preferences['general__youngest_allowed_age']
if not oag <= value <= yag:
raise ValidationError(
_("Date must be between %(oag)s and %(yag)s"),
code='invalid_model_data',
params={'oag':oag.strftime("%x"), 'yag':yag.strftime("%x")},
)
I found this issue, but it does not give the solution with code. https://github.com/EliotBerriot/django-dynamic-preferences/issues/163
UPDATE:
If I force the parameter no_cache=True, i.e, I change for this lines in the function:
def _validate_dob(value):
oag = global_preferences.get('general__oldest_allowed_age', no_cache=True)
yag = global_preferences.get('general__youngest_allowed_age', no_cache=True)
I got this error instead
File "/home/gonzalo/Playground/tektank-ws/tektank/tektank/apps/entities/models.py", line 29, in _validate_dob
oag = global_preferences.get('general__oldest_allowed_age', no_cache=True)
File "/home/gonzalo/.virtualenvs/tektank-ws/lib/python3.6/site-packages/dynamic_preferences/managers.py", line 130, in get
return self.get_db_pref(section=section, name=name).value
File "/home/gonzalo/.virtualenvs/tektank-ws/lib/python3.6/site-packages/dynamic_preferences/managers.py", line 144, in get_db_pref
except self.model.DoesNotExist:
AttributeError: 'NoneType' object has no attribute 'DoesNotExist'
I have reviewd the code, and in registries.py, there is this code, and preference_model is None..
class PreferenceRegistry(persisting_theory.Registry):
...
preference_model = None
My idea is that I have to execute something to register preferences, but I do not find it in the documentation.
One more thing. If I execute the same code, in my django shell. It works ok.
In [1]: from dynamic_preferences.registries import global_preferences_registry
In [2]: global_preferences = global_preferences_registry.manager()
In [3]: global_preferences.get('general__oldest_allowed_age', no_cache=True)
Out[2]: 100
So I guess, there is a problem with the registration of the app, or something like that. But cant figure it out
@gonzaloamadio how are you running this script? Do you call django.conf.settings.configure
at some point? https://docs.djangoproject.com/en/2.1/topics/settings/#either-configure-or-django-settings-module-is-required
@EliotBerriot
It is a validator of a model. So I have the error while trying to save one instance of a model in the admin.
So the server is running, and settings should be already configured as I run the server with the command "python manage.py runserver 0.0.0.0:8888"
class MyModel(models.Model):
birth_date = models.DateField(
verbose_name=_("date of birth"),
null=True,
blank=True,
validators=[_validate_dob],
)
Just to expand a comment I made before. This is a snippet of the PreferenceRegistry, in registries.py
class PreferenceRegistry(persisting_theory.Registry):
// SOME OTHER CODE
preference_model = None
def manager(self, **kwargs):
return PreferencesManager(registry=self, model=self.preference_model, **kwargs)
class PreferencesManager(collections.Mapping):
def __init__(self, model, registry, **kwargs):
self.model = model <-- This is None
self.registry = registry
self.instance = kwargs.get('instance')
@property
def queryset(self):
qs = self.model.objects.all()
if self.instance:
qs = qs.filter(instance=self.instance)
return qs
I have put a pdb into the manager queryset function and init function. This is in my django shell_plus
In [1]: settings.configured
Out[1]: True
In [2]: from dynamic_preferences.registries import global_preferences_registry
...: global_preferences = global_preferences_registry.manager()
> /home/gonzalo/.virtualenvs/tektank-ws/lib/python3.6/site-packages/dynamic_preferences/managers.py(13)__init__()
-> self.model = model
(Pdb) model **<--- THIS LOOKS OK**
<class 'dynamic_preferences.models.GlobalPreferenceModel'>
In [3]: global_preferences.get('general__oldest_allowed_age', no_cache=True)
(Pdb) print(vars(self))
{'model': <class 'dynamic_preferences.models.GlobalPreferenceModel'>, 'registry': GlobalPreferenceRegistry([('general', OrderedDict([('title', <global_parameters.dynamic_preferences_registry.SiteTitle object at 0x7f13ab4d8780>), ('oldest_allowed_age', <global_parameters.dynamic_preferences_registry.OldestAllowedAge object at 0x7f13ab4d86a0>), ('youngest_allowed_age', <global_parameters.dynamic_preferences_registry.YoungestAllowedAge object at 0x7f13ab4d8828>)]))]), 'instance': None}
Out[3] ; 100 **<-- Indeed, it works ok**
But when I do the same from my code... self.model is None
In [5]: te = Tester.objects.first()
In [6]: old = timezone.now().date()-relativedelta(years=150)
In [7]: te.birth_date = old
In [8]: te.full_clean()
> /home/gonzalo/.virtualenvs/tektank-ws/lib/python3.6/site-packages/dynamic_preferences/managers.py(20)queryset()
-> qs = self.model.objects.all()
(Pdb) print(vars(self))
{'model': None, 'registry': GlobalPreferenceRegistry([('general', OrderedDict([('title', <global_parameters.dynamic_preferences_registry.SiteTitl
e object at 0x7f13ab4d8780>), ('oldest_allowed_age', <global_parameters.dynamic_preferences_registry.OldestAllowedAge object at 0x7f13ab4d86a0>),
('youngest_allowed_age', <global_parameters.dynamic_preferences_registry.YoungestAllowedAge object at 0x7f13ab4d8828>)]))]), 'instance': None}
Moroever, self.model and self.instance are None
SOLVED: But still don't know really why, I guess it has something to be with the initialisation process I would like to figure it out
I put the get of the manager inside the function. So instead of having this
from dynamic_preferences.registries import global_preferences_registry
global_preferences = global_preferences_registry.
def _validate_dob(value):
oag = global_preferences['general__oldest_allowed_age']
// more code
I have now this
from dynamic_preferences.registries import global_preferences_registry
def _validate_dob(value):
global_preferences = global_preferences_registry.manager()
oag = global_preferences['general__oldest_allowed_age']
// more code
Same problem here, putting global_preferences = global_preferences_registry.manager() inside a function fixes the problem but should be investigated more.
I run into the same issue.
I think the problem here is related to the order of entries in INSTALLED_APPS.
DynamicPreferencesConfig registers the GlobalPreferenceModel on ready. It needs to be called before you call global_preferences_registry.manager()
. If you call global_preferences_registry.manager()
earlier it will create an instance of manager, but won't register GlobalPreferenceModel for it. Cause you put it in the global scope it will call global_preferences_registry.manager()
on import i.e. before DynamicPreferencesConfig::ready
. Then inside the function you use the object from memory which was created on import. When you place the code inside the function it will create a new object when the function is called.
Potential fixes:
-
Just update the documentation to highlight this behavior. The current doc recommends to add 'dynamic_preferences' after 'django.contrib.auth'. As far as I understand it's required for UserPreferences (which I'm not using). So in a big project, you will need to play a bit with the order of apps I think to find a working configuration if you want to use UserPreferences.
-
Fix line 186 in registries.py:
return PreferencesManager(registry=self, model=self.preference_model, **kwargs)
to something like this:
return PreferencesManager(registry=self, model=self.preference_model or GlobalPreferenceModel, **kwargs)
@EliotBerriot Which one do you think will be better? I didn't look deep at the other parts of the code. So need to rely on your advice. I can work on either fix.
@tyomo4ka I tend to prefer a documentation, because instanciating a manager before the apps are all loaded is likely to create many issues down the road.
Basically, using preferences and managers must happen only after django.setup()
has returned successfully :)
@EliotBerriot That makes sense. Thanks for the hint. I will fix it in my project then.
I will also check what is the best place in the documentation to make this note and open a PR.
Hi @artem, can you share how are you doing? The fix
On Wed, 4 Sep 2019 at 12:59 PM, Artem [email protected] wrote:
@EliotBerriot https://github.com/EliotBerriot That makes sense. Thanks for the hint. I will fix it in my project then.
I will also check what is the best place in the documentation to make this note and open a PR.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/EliotBerriot/django-dynamic-preferences/issues/173?email_source=notifications&email_token=AAHVLY7CN7VBXVFHVO7RAZDQH4B5JA5CNFSM4HCOHZF2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD5Z77ZI#issuecomment-527695845, or mute the thread https://github.com/notifications/unsubscribe-auth/AAHVLY5ONYFDCOEVT54USRTQH4B5JANCNFSM4HCOHZFQ .
--
Gonzalo Amadio
I will also check what is the best place in the documentation to make this note and open a PR.
@tyomo4ka I think we could use a warning here.
Something like:
.. warning::
All your calls to ``global_preferences_registry.manager()`` must occur after ``django.setup()`` has run and all your apps are loaded.
Of course you can elaborate and give some working/not working examples if you feel like it :)