mkoistinen / cmsplugin-form-handler

This package provides a mechanism for handling form-submissions in django-CMS plugins.
MIT License
14 stars 10 forks source link

TypeError using cmsplugin-form-handler #5

Closed pinplex closed 6 years ago

pinplex commented 6 years ago

Dear Martin,

thank you very much for your the module! I implemented it exactly as you described, however the following error is raised for my forms.py file

super(MyForm, self).__init__(*args, **kwargs) TypeError: __init__() takes at least 2 arguments (1 given)

The error is raised at the point in my forms.py file:

class MyForm(FormPluginFormMixin, forms.Form): ## my fields def __init__(self, *args, **kwargs): super(MyForm, self).__init__(*args, **kwargs)

I try to use it together with crispy-forms, by the way. Do you have an idea what the problem might be? Just let me know if you need more detailed info.

Thank you so much. pinplex

mkoistinen commented 6 years ago

Thanks for the issue report. Which version of Django and CMS are you using? Have you tried it without Crispy Forms just to eliminate that as a factor?

pinplex commented 6 years ago

Thank you very much for your rapid response. I use django-cms (3.4.4) and Django (1.10.7). Yes, I have tried it without Crispy Forms, but the TypeError still occurs. Now it is raised in cms_plugin.py where MyForm() is called the first time at:

class MyFormPluginPublisher(FormPluginBase):

    def render(self, context, instance, placeholder):
        # CMSPlugin attributes
        context = super(MyFormPluginPublisher, self).render(context, instance, placeholder)
        context['myform'] = MyForm()

        return context
mkoistinen commented 6 years ago

Remove the line context['myform'] = MyForm() and let me know how that goes. The CMS Plugin's base class will automatically include cmsplugin_form in the context for your form.

pinplex commented 6 years ago

Thanks. I removed the line. Now, the TypeError is not called anymore, but the following is now the problem in cms_plugins.py:

    File "/[...]/cms_plugins.py", line 45, 
    in form_valid
        form.save()
    AttributeError: 'MyForm' object has no attribute 'save'

I just realized that cmsplugin-form-handler is programmed in python 3.4, so that could also cause the problem, since I am using 2.7. What do you think?

mkoistinen commented 6 years ago

It should work fine in both 2.7 and 3.x.

I'm going to need to see your cms_plugins.py and forms.py files. Please use a gist or similar. But, I'm fairly certain this issue isn't due to cmsplugin-form-handler at all.

pinplex commented 6 years ago

Alright, interesting. The error occurs after implementing cmsplugin-form-handler so I figured it should be connected somehow. Here is the gist.

Thank you very much for your support.

mkoistinen commented 6 years ago

Hmmm, your code looks pretty good. What if you commented out your lines in cms_plugins.py:

    def form_valid(self, request, instance, form):
        # Optionally do something with the rendered form here
        # This is what this method does by default:
        form.save()

Any change?

pinplex commented 6 years ago

I already tried that. Than it falls back to the form.save() method in /[...]/lib/python2.7/site-packages/cmsplugin_form_handler/cms_plugins.py

mkoistinen commented 6 years ago

OK, add those lines back into your cms_plugins.py file but replace form.save() with pass does that help?

pinplex commented 6 years ago

Yes, that helps, so now no further Error is called. The form gets submitted, however, of course now no data is saved. Thanks!

mkoistinen commented 6 years ago

I'm not understanding why your form has no save method.

pinplex commented 6 years ago

So, there is a save method in my views.py file, but after replacing form.save() with pass in cms_plugin.py no data is saved using the plugin. Also, I have to admit, I don't fully understand the functionality yet. I also tried it again with the crispy forms layout to check if the form is rendered correctly now, but than the initial TypeError reoccurs. So, maybe I was thinking of going a different way. So, if you want we can close this issue, since it seems to be a problem for my specific setup. Thanks a lot!

mkoistinen commented 6 years ago

Well, I want to leave this open until we definitively know if I need to release an update or not. Right now, its not clear and I'm not comfortable with that =). Django and CMS have had new releases since this was released, so, it'd be good to know. I may set up a test project this weekend to ensure myself, but if you find more clues, please post them here.

pinplex commented 6 years ago

Ok, good to know. Yes, I will also continue to find the problem and let you know what I find out. If you need more information on my setup or specific files, just let me know. Thank you!

mkoistinen commented 6 years ago

OK, @pinplex, I've now setup a test project with the current, released version of this package. I believe the reason your project is failing because your form's __init__ signature should have been:

def __init__(self, source_url, *args, **kwargs):
    super(MyForm, self).__init__(source_url, *args, **kwargs)
    ...

Note the additional parameter: source_url. This is defined in this packages FormPluginFormMixin for reference. I'm sorry I didn't spot this earlier in this thread.

Once you have fixed that, Crispy forms should work the way you intended. However, please note that for the current release version of this package (0.1.3), you will not really be able to use some of Crispy Forms features such as allowing it to produce the whole form. You would still have to "manually" generate the form-tag itself so that you could use this package's templatetag: cmsplugin_form_action to produce the form's action. Otherwise, Crispy Form and this package can be used together just fine.

I am shortly going to release a version that alleviates that issue.

mkoistinen commented 6 years ago

@pinplex I have prepared a develop branch which contains the next release. I would like ask if you could test this branch in your project(s).

If you do use it, please note that the signature for your form should now be:

def __init__(self, source_url, instance, *args, **kwargs):
    super(MyForm, self).__init__(source_url, instance, *args, **kwargs)
    ...

And, now, if you'd like to use Crispy to render the entire form, you can add this to your form's helper:

self.helper.form_action = reverse(
   'cmsplugin_form_handler:process_form',
   args=(self.plugin_id, )
)

There should be no need to manually render the form and use the action={% cmsplugin_form_action %} templatetag in your form's declaration. Please let me know if you face any issues with this updated code.

mkoistinen commented 6 years ago

Closing.

pinplex commented 6 years ago

Wow, thanks! I will try it out asap and let you know if everything works as expected. Thanks again and best wishes.

pinplex commented 6 years ago

I tested it for my project and now the form renders perfectly in the layout specified with crispy-forms. However, when I hit 'submit' the following error is called. I see if I can fix the bug and let you know if I come up with something.

Traceback:

File "/.../lib/python2.7/site-packages/django/core/handlers/exception.py" in inner
  42.             response = get_response(request)

File "/.../lib/python2.7/site-packages/django/core/handlers/base.py" in _get_response
  187.                 response = self.process_exception_by_middleware(e, request)

File "/.../lib/python2.7/site-packages/django/core/handlers/base.py" in _get_response
  185.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/.../lib/python2.7/site-packages/django/views/generic/base.py" in view
  68.             return self.dispatch(request, *args, **kwargs)

File "/.../lib/python2.7/site-packages/django/views/generic/base.py" in dispatch
  88.         return handler(request, *args, **kwargs)

File "/.../lib/python2.7/site-packages/django/views/generic/edit.py" in post
  185.             return self.form_invalid(form)

File "/.../lib/python2.7/site-packages/cmsplugin_form_handler-0.1.3-py2.7.egg/cmsplugin_form_handler/views.py" in form_invalid
  123.         return redirect(url)

File "/.../lib/python2.7/site-packages/django/shortcuts.py" in redirect
  56.     return redirect_class(resolve_url(to, *args, **kwargs))

File "/.../lib/python2.7/site-packages/django/shortcuts.py" in resolve_url
  153.         if '/' not in to and '.' not in to:

Exception Type: TypeError at /plugin_forms/7/
Exception Value: argument of type 'NoneType' is not iterable
mkoistinen commented 6 years ago

Hi @pinplex, I'm running a test project here using django-cms==3.4.4, django==1.10.7, django-crispy-forms==1.7.0. And I'm not seeing any issues posting forms with the current develop version of this package.

I wonder if you have setup your URLs as directed in the readme? Perhaps you could post your urls.py to a gist for review?

mkoistinen commented 6 years ago

I've now run tests for Djangos 1.8-1.11, Pythons 2.7, 3.4-3.6 and CMSs 3.3, 3.4, all with crispy forms included. All pass.

pinplex commented 6 years ago

Yes, I did set up the URLs as specified in the README. It must be something with my setup, but I cannot spot the problem. When the __init__() function in forms.py is called the first time at rendering the form (before submitting the form), printing the source_url shows it's correct. After submitting the form, the source_url is None. This happens only when using crispy forms for the rendering. Maybe there is the problem.

In my template, rendering without crispy forms:

<form class="form-main" enctype="multipart/form-data" action="{% cmsplugin_form_action %}" method="post">
          {% csrf_token %}
          {{ cmsplugin_form }}
          <input type="submit">
        </form>

Rendering with crispy forms to get the defined layout:

<form class="form-main" enctype="multipart/form-data" action="{% cmsplugin_form_action %}" method="post">
          {% csrf_token %}
          {% crispy cmsplugin_form %}
          <input type="submit">
        </form>

Should I do something different?

mkoistinen commented 6 years ago

If you're using the develop branch, you define your form like:

from django import forms
from django.core.urlresolvers import reverse

from cmsplugin_form_handler.forms import FormPluginFormMixin
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit

from .models import Sample

class SampleCrispyModelForm(FormPluginFormMixin, forms.ModelForm):
    """
    Simple ModelForm, using Django Crispy Forms.
    """
    class Meta:
        model = Sample
        fields = '__all__'

    def __init__(self, source_url, instance, **kwargs):
        super(SampleCrispyModelForm, self).__init__(
            source_url, instance, **kwargs)
        self.helper = FormHelper()
        self.helper.form_action = reverse(
            'cmsplugin_form_handler:process_form', args=(self.plugin_id, ))
        self.helper.add_input(Submit('submit', 'Submit'))

Then your template can look this simple:

{% load crispy_forms_tags %}

<h2>{{ plugin_name }}</h2>
<div id="sample-crispy-create">
    {% crispy cmsplugin_form %}
</div>
mkoistinen commented 6 years ago

FYI, I've now released version 0.2.0.

mkoistinen commented 6 years ago

Closing.

pinplex commented 6 years ago

Alright, thank you very much for your help and for your work!