Deferred initialization for string based model names
squio opened this issue · comments
While our app grew more complex it became necessary to break circular imports by specifying models in many to many fields as string, not fully imported model.
During app initialization this leads to the decorator @report_field_register
to be called more than once which results in the error:
django.contrib.admin.sites.AlreadyRegistered:
The field name __total_distance__ is used before and `override` is False
Hard coding override=True
leads to the following error in slick_reporting/generator.py
:
line 465, in check_columns
field = model_to_use._meta.get_field(col)
AttributeError: 'str' object has no attribute '_meta'
Following the stack trace back shows that this error originates at the point where the site's urls.py
is initialized and the views classes are loaded.
I got stuck at this point...
Is there any way to initialize the reports at runtime, on demand or at least after all Django models have been initialized?
Ok i debugged this a bit...
i can say that you did alright making the override=True, and actually the error moved from the field to the model_to_use
.
i'm interested to know the value of model_to_use
in your case.
If it's 'app.ModelName' then we can then add a check and apps.get_model(model_to_use) around line 458
Let's see if that works
You are right about the app.ModelName
but the issue is that this is actually the desired behavior caused by lazy-loading a model with a foreign key where the model class is specified as string.
Directly importing the class gives the dreaded "Can not use partially initialized model ... caused by circular import" error.
I tried indeed forcing the import with apps.get_model(model_to_use)
but this leads to a long list of errors like "attribute my_attr
doesn't exist on model MyModel
" - which is likely due to the partially initialized state of the model.
My guess is that a deferred initialization of the whole report field list could help here... not sure though
Hmmm, The aim of this check is to provide a sanity check on the fields specified in columns
attribute.
also this check is done once in the life time of the app ... Not sure how to defer that.
Anyway, one solution can be that you inherit from SlickReportViewBase
instead of SlickReportView
, that way you wont have checks on load time.. Let's see how that works.
Also, if you can reproduce this in tests i will be happy to try and address it.
This indeed works very well:
from slick_reporting.views import SlickReportViewBase as SlickReportView
The problem is that the origin of the circular import is very deep buried somewhere in a rather complex application; alas I haven't been able to isolate the source.
If I do find it I will try and make a test case, for now this solves the issue, admittedly not in the most elegant way though 😞
Thanks for your help and quick response!