RamezIssac / django-slick-reporting

The Reporting Engine for Django. Create dashboards and standalone Reports and Charts.

Home Page:https://django-slick-reporting.com/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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!