Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to export to Excel (no attribute 'xlsx') #8

Closed
wesleyboar opened this issue Jun 7, 2022 · 11 comments
Closed

Unable to export to Excel (no attribute 'xlsx') #8

wesleyboar opened this issue Jun 7, 2022 · 11 comments

Comments

@wesleyboar
Copy link

wesleyboar commented Jun 7, 2022

Similar to mishbahr#88, I am unable to export to excel (nor yaml).

The error is an AttributeError, 'Dataset' object has no attribute 'xlsx'.

Traceback
Environment:


Request Method: POST
Request URL: http://localhost:8000/admin/djangocms_forms/formsubmission/export/

Django Version: 2.2.27
Python Version: 3.8.13
Installed Applications:
['djangocms_admin_style',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.admin',
 'django.contrib.sites',
 'django.contrib.sitemaps',
 'taccsite_cms.django.contrib.staticfiles_custom',
 'django.contrib.messages',
 'cms',
 'menus',
 'sekizai',
 'treebeard',
 'djangocms_text_ckeditor',
 'filer',
 'easy_thumbnails',
 'meta',
 'djangocms_page_meta',
 'djangocms_column',
 'djangocms_file',
 'djangocms_link',
 'djangocms_picture',
 'djangocms_style',
 'djangocms_snippet',
 'djangocms_googlemap',
 'djangocms_transfer',
 'djangocms_video',
 'djangocms_icon',
 'djangocms_bootstrap4',
 'djangocms_bootstrap4.contrib.bootstrap4_alerts',
 'djangocms_bootstrap4.contrib.bootstrap4_badge',
 'djangocms_bootstrap4.contrib.bootstrap4_card',
 'djangocms_bootstrap4.contrib.bootstrap4_carousel',
 'djangocms_bootstrap4.contrib.bootstrap4_collapse',
 'djangocms_bootstrap4.contrib.bootstrap4_content',
 'djangocms_bootstrap4.contrib.bootstrap4_grid',
 'djangocms_bootstrap4.contrib.bootstrap4_jumbotron',
 'djangocms_bootstrap4.contrib.bootstrap4_link',
 'djangocms_bootstrap4.contrib.bootstrap4_listgroup',
 'djangocms_bootstrap4.contrib.bootstrap4_media',
 'djangocms_bootstrap4.contrib.bootstrap4_picture',
 'djangocms_bootstrap4.contrib.bootstrap4_tabs',
 'djangocms_bootstrap4.contrib.bootstrap4_utilities',
 'haystack',
 'aldryn_apphooks_config',
 'test_without_migrations',
 'djangocms_forms',
 'django.forms',
 'captcha',
 'taccsite_cms',
 'taccsite_cms.contrib.bootstrap4_djangocms_link',
 'taccsite_cms.contrib.bootstrap4_djangocms_picture',
 'taccsite_cms.contrib.taccsite_blockquote',
 'taccsite_cms.contrib.taccsite_callout',
 'taccsite_cms.contrib.taccsite_sample',
 'taccsite_cms.contrib.taccsite_offset',
 'taccsite_cms.contrib.taccsite_system_specs',
 'taccsite_cms.contrib.taccsite_system_monitor',
 'taccsite_cms.contrib.taccsite_data_list',
 'taccsite_custom.a2cps-cms',
 'taccsite_custom.apcd-cms',
 'taccsite_custom.brainmap-cms',
 'taccsite_custom.ecep-cms',
 'taccsite_custom.example-cms',
 'taccsite_custom.frontera-cms',
 'taccsite_custom.lccf-tacc',
 'taccsite_custom.neuronex-cms',
 'taccsite_custom.protx-cms',
 'taccsite_custom.sciviscolor-cms',
 'taccsite_custom.tapis-project-org',
 'taccsite_custom.texascale-org',
 'taccsite_custom.tup-cms',
 'taccsite_custom.utrc-cms']
Installed Middleware:
['cms.middleware.utils.ApphookReloadMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.locale.LocaleMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'cms.middleware.user.CurrentUserMiddleware',
 'cms.middleware.page.CurrentPageMiddleware',
 'cms.middleware.toolbar.ToolbarMiddleware',
 'cms.middleware.language.LanguageCookieMiddleware']



Traceback:

File "/opt/pysetup/.venv/lib/python3.8/site-packages/django/core/handlers/exception.py" in inner
  34.             response = get_response(request)

File "/opt/pysetup/.venv/lib/python3.8/site-packages/django/core/handlers/base.py" in _get_response
  115.                 response = self.process_exception_by_middleware(e, request)

File "/opt/pysetup/.venv/lib/python3.8/site-packages/django/core/handlers/base.py" in _get_response
  113.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/opt/pysetup/.venv/src/djangocms-forms-maintained/djangocms_forms/admin.py" in wrapper
  105.                 return self.admin_site.admin_view(view)(*args, **kwargs)

File "/opt/pysetup/.venv/lib/python3.8/site-packages/django/utils/decorators.py" in _wrapped_view
  142.                     response = view_func(request, *args, **kwargs)

File "/opt/pysetup/.venv/lib/python3.8/site-packages/django/views/decorators/cache.py" in _wrapped_view_func
  44.         response = view_func(request, *args, **kwargs)

File "/opt/pysetup/.venv/lib/python3.8/site-packages/django/contrib/admin/sites.py" in inner
  223.             return view(request, *args, **kwargs)

File "/opt/pysetup/.venv/src/djangocms-forms-maintained/djangocms_forms/admin.py" in export_view
  239.                 getattr(dataset, file_type), {

Exception Type: AttributeError at /admin/djangocms_forms/formsubmission/export/
Exception Value: 'Dataset' object has no attribute 'xlsx'
@wesleyboar
Copy link
Author

wesleyboar commented Jun 7, 2022

I may have found the solution (source)1:

pip install tablib[all]

The tablib library in python3 reports an error "'Dataset' object has no attribute'xlsx' tablib"

The cause of the problem: Because the code is running in the new environment, only pip install tablib is installed; the installation of the tablib library is not comprehensive

Solution: pip install tablib[all]

Because support for specific formats became opt-in since late 2019.

Footnotes

  1. I have not tested this solution, because I do not want to install "all". (I only want the ones that djangocms-forms` supports.) If my client needs Excel, I will revisit. (I am okay to only offer CSV & JSON for now.)

@wesleyboar
Copy link
Author

I wanted to overwrite the dropdown list options so that I would not show editors unusable fields, but FORMAT_CHOICES in /djangocms_forms/forms.py#L355 suggests that I cannot do this with a setting.

I tried to override FORMAT_CHOICES, but I failed.

I can not test this code because I do not where to put it nor how to load it without generating errors.

# Limit export format to only those that work
# https://github.com/avryhof/djangocms-forms/issues/8
# https://github.com/avryhof/djangocms-forms/blob/202008311510/djangocms_forms/forms.py#L349
FORMAT_CHOICES = (
    ("csv", _("CSV")),
    ("json", _("JSON")),
)

SubmissionExportForm.base_fields['file_type'] = forms.ChoiceField(choices=FORMAT_CHOICES, initial="csv", required=False)

@wesleyboar
Copy link
Author

wesleyboar commented Jun 7, 2022

I tried installing tablib extras like I suggested (source).

pip install "tablib[all]"
pip install "tablib[xlsx, yaml]"

Neither fixed the problem. It could still be my fault. Please assist.


I propose that this repo document (at least summarize) the steps necessary to make Excel and YAML exports work.

@avryhof
Copy link
Owner

avryhof commented Jun 14, 2022

I will take some time to look at this soon. The main reason this fork exists is because one of our teams was using it in a project before it stopped being supported. They weren't ready to let it go, so I just went through and made sure all of the warnings/exceptions were addressed.

I have some tools that can import/export xlsx and csv with openpyxl (and pyxlsb). I'm a little less familiar with tablib, but it looks like it might be useful for YAML export, although I think building a JSON export wouldn't be difficult, (I have excel to dicts, and dicts to excel converters, as well as to and from csv) and going from JSON to YAML isn't too bad either.

@avryhof
Copy link
Owner

avryhof commented Jun 14, 2022

Here's my quick fix for this -

I added **kwargs to a newly created init method. This allows you to pass in format_choices=() as a keyword arg.

I also added an option you can put in your settings.py file to make it happen globally.

DJANGOCMS_FORMS_FORMAT_CHOICES = (
        ("csv", _("CSV")),
        ("json", _("JSON")),
        ("yaml", _("YAML")),
        ("xlsx", _("Microsoft Excel")),
    )

I look for a tuple specifically, since kwargs.get() and getattr(settings,'VAR') will default to None.

@wesleyboar
Copy link
Author

That should work! I'll try it out today.

Reminder

Branch/Diff: avryhof:config_format_choices

P.S. Thank you very much for having maintained this fork and fixing the bugs. I found no other such Django CMS plugin that lets Django CMS editor create forms (besides the broken upstream). Our primary CMS editor has many form requests, and it's far more efficient that she can build forms herself.

@avryhof
Copy link
Owner

avryhof commented Jun 14, 2022

@wesleyboar no problem. I'll keep it going as long as I can!

@wesleyboar
Copy link
Author

wesleyboar commented Jun 14, 2022

I don't think the change worked. Maybe I'm doing something wrong. Did it work for you?

I performed re-build of my CMS Docker container, confirmed Poetry dependency install occurred (i.e. Docker did not use cache) and re-up'ed them.

Added to _settings/form_plugin.py (Read by settings.py)

DJANGOCMS_FORMS_FORMAT_CHOICES = (
    ("csv", _("CSV")),
    ("json", _("JSON")),
)

Changed pyproject.toml
From

djangocms-forms-maintained = { git = "https://github.com/avryhof/djangocms-forms", rev = "30f464303d317cf7a3170ad64e11ab4471b4287b" }

To

# Get 202008311510 release PLUS support for DJANGOCMS_FORMS_FORMAT_CHOICES
# https://github.com/avryhof/djangocms-forms/issues/8#issuecomment-1155288059
djangocms-forms-maintained = { git = "https://github.com/avryhof/djangocms-forms", rev = "a171bd6b7dd7a8307b4a6437fcdac169a05d6e93" }

DJANGOCMS_FORMS_FIELDSETS Usage Diff

@avryhof
Copy link
Owner

avryhof commented Jun 14, 2022

You are correct. Sorry about that.

I had to setup a new test environment to dig further into the issue, and have a working version up in that branch. It takes away the new init method, and just uses an if statement at the top of the form class to check if the setting has been overridden in settings

pip install --upgrade git+https://github.com/avryhof/djangocms-forms.git@config_format_choices

I'm also in the process of pushing a new release that will support newer versions of django (<4) and includes this fix, as well as a fix for admin_static template tags, and the removal if the url import.

pip install --upgrade djangocms-forms-maintained

@wesleyboar
Copy link
Author

Hey, finally got to testing this. Thanks, it works.

I tested a commit (ab38b22) after the fix.

wesleyboar added a commit to TACC/Core-CMS that referenced this issue Jul 8, 2022
1. Only let users export with supported formats.
2. Update form version to rev. that supports this.

Source: avryhof/djangocms-forms#8
@avryhof
Copy link
Owner

avryhof commented Jul 8, 2022

Great! Glad to help.

wesleyboar added a commit to TACC/Core-CMS that referenced this issue Jul 8, 2022
1. Only let users export with supported formats.
2. Update form version to rev. that supports this.

Source: avryhof/djangocms-forms#8
wesleyboar added a commit to TACC/Core-CMS that referenced this issue Jul 15, 2022
* feat(tup-230): working form plugin

* docs(tup-230): clarify form submission failure

* feat(tup-230): use jquery version that has ajax

The django-forms(-maintained) plugin expects jquery with ajax features.

Our CMS was using a slim build of jQuery.

The size difference is 17KB, which I consider negligible.

* feat(tup-230): make spam protection available

* feat(tup-230): e-mail template (when e-mail works)

* docs(tup-230): tweak comment in settings

* fix(tup-230): comments & semantic html in template

* fix(tup-230): revert and make some html changes

- less changes form original
- sometimes the original html was better for styling

* feat(tup-230): style form (no design review yet)

Also, no button styling yet.

* chore(tup-230): rename css file, add pointer file

Pointer file mimics what I do for blog CSS, which is also in source.

* chore(settings): tup-308, how to set captcha keys

Ref: TACC/Core-Portal-Deployments@48e6e9c

* noop(settings): tup-308, add new line

* fix(settings): tup-308, use new form css from #500

* fix(settings): tup-308, new form templates by #500

Not a straight clone of #500.

I added a little CSS and a small document to avoid cloning one template.

See:
- default.html.md
- django.cms.forms.css

* noop(settings): tup-308, form settings from #501

* fix(settings): tup-308, css changes from #501

* fix(settings): tup-308, "*" to "(required)"

I use a different solution than #501.

In #501, markup was changed to "(require)":
https://github.com/TACC/Core-CMS/blob/5f63bdf/taccsite_cms/templates/djangocms_forms/form_template/default.html#L27

But here, I do not edit the markup, so no need to clone template:
https://github.com/TACC/Core-CMS/blob/8a46a21/taccsite_cms/templates/djangocms_forms/form_template/default.html.md

* fix(css): tup-308, small form style issues

- bigger gap between single checkbox and input
- add gap between multiple checkbox and input
- align multiple checkbox and input
- simpler, less specific checkbox label selector
- remove undesired label margin*

* This style was ineffectual—good—in 3.6, but "fixed"—bad—when ported.

* fix(css): tup-308, small form style issues

- limit which help text gets margin
- remove ineffectual, unnecessary margin removal
- bigger gap between single checkbox and input
- align single checkbox and input
- add gap between multiple checkbox and input
- align multiple checkbox and input
- remove label margin on last of multiple checkboxes/radios
- simpler, less specific checkbox label selector
- remove undesired label margin*

* This style was ineffectual—good—in 3.6, but "fixed"—bad—when ported.

* docs(styles): typo fix for KSS comment

* fix(form): tup-308 only working export formats

1. Only let users export with supported formats.
2. Update form version to rev. that supports this.

Source: avryhof/djangocms-forms#8

* fix(form): delete unnecessary cloned template

* fix(form): tup-308, clean up templates

- remove unedited cloned template
- update commit in a comment

* fix(form): tup-308, move form export settings

* fix(form): tup-308, remove unused file

I forgot to delete this file earlier.

* fix(form): tup-308, remove redundant script

The removal happens form a merged PR on the plugin:
avryhof/djangocms-forms#12

* chore(core-styles): v0.7.0-beta

* fix(taccsite_custom): get main relevant PRs merged

* docs(form): no commented settings
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants