How could I render just the form from an autoform? - plone

I'm in the context of a BrowserView. I want to render a form inside the page.
class CommandeView(BrowserView):
...
def render_myform(self):
livraison_form = LivraisonForm(self.context, self.request)
livraison_form.update()
return livraison_form.render()
class ILivraisonForm(interface.Interface):
livraison = schema.Choice(title=_(u"Mode de livraison"),
vocabulary=livraison_vocabulary)
class LivraisonForm(AutoExtensibleForm, form.EditForm):
schema = ILivraisonForm
class LivraisonFormAdapter(object):
component.adapts(ICommande)
interface.implements(ILivraisonForm)
def __init__(self, context):
self.context = context
self.livraison = self.context.livraison
I would like render_myform to render only the form but it return a HTML Plone page.
Any tips are welcomed.

Haven't checked this in-depth, but the form is probably wrapped by the plone FormWrapper class.
Look for a .form attribute on the LivraisonForm instance:
def render_myform(self):
livraison_form = LivraisonForm(self.context, self.request).form
livraison_form.update()
return livraison_form.render()
It may be you need to add an additional interface to the request for this to work (the wrapper normally does this for you. I'm a little low on time right now, so you get this untested:
from plone.z3cform import z2
class CommandeView(BrowserView):
def render_myform(self):
z2.switch_on(self) # Apply layer interface, set up locale, add `getURL` method
livraison_form = LivraisonForm(self.context, self.request).form
livraison_form.update()
return livraison_form.render()
The Plone Knowledgebase uses this same setup to show a form in a viewlet.

Related

I am trying to bind my FormView to show the Form in djangocms but i am not able to

I am trying to bind my FormView to show the Form in djangocms but I am not able to,
my model is:
class ContactFormView(FormView, CMSPlugin):
template = '/ContactForm/ContactForm.html'
form_class = contact_form
success_url = reverse_lazy('success-page')
def post(self, request, **kwargs):
assert request.is_ajax()
request_data = json.loads(request.body)
form = self.form_class(data=request_data[self.form_class.scope_prefix])
if form.is_valid():
return JsonResponse({'success_url': force_text(self.success_url)})
else:
response_data = {form.form_name: form.errors}
return JsonResponse(response_data, status=422)
cms_plugins.py:
class ContactFormPlugin(CMSPluginBase):
model = ContactForm.ContactFormView
render_template = '/ContactForm/ContactForm.html'
name = 'Contact Form'
allow_children = False
plugin_pool.register_plugin(ContactFormPlugin)
Looking at what you've got there, I'm not sure you've given your plugin a proper model but a view!? And the form class isn't a form class, form_class = contact_form but what is contact_form? You need it to be the class of your form e.g.
class ContactForm(forms.Form):
name = forms.CharField(label='Your name', max_length=100)
email = forms.EmailField(label='Your email', max_length=100)
class ContactFormView(FormView):
template = '/ContactForm/ContactForm.html'
form_class = ContactForm
success_url = reverse_lazy('success-page')
And if you've got a form you want your plugin to render, you need to tell it what to do...
from cms.models.pluginmodel import CMSPlugin
class ContactFormPlugin(CMSPluginBase):
model = CMSPlugin
render_template = '/ContactForm/ContactForm.html'
name = 'Contact Form'
allow_children = False
def render(self, context, instance, placeholder):
context = super(ContactFormPlugin, self).render(context, instance, placeholder)
context['form'] = ContactForm()
return context
However you're working with a CMS plugin, so you don't define a View at all for a plugin. I think you could make this do what you're trying to do but the view doesn't want to inherit CMSPlugin because you just want a view to receive data from the plugin.
If you need to define views then you're building an app which in CMS requires an Apphook, not a plugin. (Technically you could post to a URL without an Apphook, but depends how much CMS integration you want)
There is a great example that starts with a plugin posting to a view then progresses into the app integration which is possible here; http://docs.django-cms.org/en/latest/introduction/plugins.html

Django CMS plugin nesting --- child doesn't show up in the "structure" interface

I'm trying to create a Django CMS custom plugin that can assemble other plugins.
As far as I can tell, Django CMS can do this using Plugin nesting, and I've followed the examples to create a simple test case.
My expectation is that when you go into the "Structure" tab for a record in the model that has a PlaceholderField that includes the parent plugin, when you add a parent plugin, the pop-up for that model should ALSO have some way to edit/create/add an instance of the child plugin. But it doesn't --- all I see are the fields for the parent plugin and NOTHING about the children (see screenshot below).
Or am I missing the point of Plugin nesting entirely?
models.py:
from django.db import models
from cms.models import CMSPlugin
from cms.models.fields import PlaceholderField
from djangocms_text_ckeditor.models import AbstractText
class CustomPlugin(CMSPlugin):
title = models.CharField('Title', max_length=200, null=False)
placeholder_items = PlaceholderField ('custom-content')
renderer = models.CharField('Renderer', max_length=50, null=True, blank=True,
help_text='This is just to show that a custom renderer CAN be done here!')
class ChildTextPlugin(AbstractText):
pass
cms_plugins.py:
from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool
from django.utils.translation import ugettext as _
from .models import CustomPlugin, ChildTextPlugin
class CMSCustomPlugin(CMSPluginBase):
model = CustomPlugin
name = _('Custom Plugin')
render_template = 'custom/custom_plugin.html'
allow_children = True
def render(self, context, instance, placeholder):
context = super(CMSCustomPlugin, self).render(context, instance, placeholder)
return context
class CMSChildTextPlugin(CMSPluginBase):
model = ChildTextPlugin
name = _('Child Text Plugin')
render_template = 'custom/child_text_plugin.html'
parent_classes = ['CMSCustomPlugin',]
def render(self, context, instance, placeholder):
context = super(ChildTextPlugin, self).render(context, instance, placeholder)
return context
plugin_pool.register_plugin(CMSCustomPlugin)
plugin_pool.register_plugin(CMSChildTextPlugin)
... and the answer is "it was working all the time" --- the interface comes AFTER the screen I posted above is submitted --- the Custom Plugin entry will have a "+" icon, and it's THERE that the children are found.

Extend Plone-Controlpanel Form

Is it possible to extend the Controlpanel-View with each addon?
For Example
ca.db.core -> Makes basic fieldset/tab for DB Connection Settings
ca.db.person -> If installed, Adds to the "core" settings a new fieldset/tab for Person specific fields/settings
ca.db.schema -> If installed, also adds an new fieldset/tab for schema.org Fields
Yes it's possible, I once discussed this problem with a guy, who wrote the bda.plone.shop addon.
They faced the same problem, and solved it by using a ContextProxy object, which puts the different schema definitions together in one proxy object.
Using a proxy is IMHO a hack, but I don't know a better solution.
The proxy, tries to get/set a attribute from a list of schemas.
Be aware, you need to handle conflicting names, means if you have the same field name in more than one schema.
class ContextProxy(object):
def __init__(self, interfaces):
self.__interfaces = interfaces
alsoProvides(self, *interfaces)
def __setattr__(self, name, value):
if name.startswith('__') or name.startswith('_ContextProxy__'):
return object.__setattr__(self, name, value)
registry = getUtility(IRegistry)
for interface in self.__interfaces:
proxy = registry.forInterface(interface)
try:
getattr(proxy, name)
except AttributeError:
pass
else:
return setattr(proxy, name, value)
raise AttributeError(name)
def __getattr__(self, name):
if name.startswith('__') or name.startswith('_ContextProxy__'):
return object.__getattr__(self, name)
registry = getUtility(IRegistry)
for interface in self.__interfaces:
proxy = registry.forInterface(interface)
try:
return getattr(proxy, name)
except AttributeError:
pass
raise AttributeError(name)
Now you need to use the proxy in your ControlPanel form.
I assume you're using the RegistryEditForm from plone.registry:
class SettingsEditForm(controlpanel.RegistryEditForm):
schema = ISettings
label = _(u"Settings")
description = _(u"")
# IMPORTANT Note 1 - This is where you hook in your proxy
def getContent(self):
interfaces = [self.schema] # Base schema from ca.db.core
interfaces.extend(self.additionalSchemata) # List of additional schemas
return ContextProxy(interfaces)
# IMPORTANT Note 2 - You may get the additional schemas dynamically to extend the Settings Form. For example by name (startswith...)
# In this case they have a separate interface, which marks the relevant interfaces.
#property
def additionalSchemata(self):
registry = getUtility(IRegistry)
interface_names = set(record.interfaceName for record
in registry.records.values())
for name in interface_names:
if not name:
continue
interface = None
try:
interface = resolve(name)
except ImportError:
# In case of leftover registry entries of uninstalled Products
continue
if ISettingsProvider.providedBy(interface):
yield interface
...
You can find the full code here

How to customize z3c.form widget for fields inside groups (fieldsets)

I have a Plone custom control panel registry and I'm trying to use a well know method to customize some of the widgets properties for zope.schema.Text and zope.schema.TextField.
I commonly customize the updateWidgets in that way:
def updateWidgets(self):
super(MyEditForm, self).updateWidgets()
self.widgets['my_text_area'].style = 'width: 100%'
self.widgets['my_text_area'].rows = 7
But now I'm working on a form where fields are splitted in two fieldsets:
class MySettingsEditForm(controlpanel.RegistryEditForm):
schema = IMySettingsSchema
groups = (Form1, Form2)
# fields = nothing
If I try to access self.widgets['my_text_area'] I get KeyError. It seems that as I did't defined the fields attribute I can't access directly widgets.
I found that I have groups so I can call something like self.groups[0].fields['my_text_area'] but still I find no way to access widgets for fields inside groups.
How can I customize widgets attributes when using groups?
I think what you need is playing with widget subform, see this code:
def fix_table_widget(self, name, widgets):
sub_widgets = widgets[name].widgets
for widget in sub_widgets:
new_label = widget.subform.widgets['weekday'].value
widget.subform.widgets['selected'].items[0]['label'] = new_label
widget.subform.widgets['weekday'].mode = 'hidden'
def schoolrequest_customizations(self):
''' Customizations for the schoolrequest base views
'''
for group in self.groups:
widgets = group.widgets
if 'table_bus_to_school' in widgets:
self.block_widget_table('table_bus_to_school', widgets)
self.fix_table_widget('table_bus_to_school', widgets)
if 'table_bus_to_home' in widgets:
self.block_widget_table('table_bus_to_home', widgets)
self.fix_table_widget('table_bus_to_home', widgets)
def update(self):
super(MyForm, self).update()
self.schoolrequest_customizations()

relating models to one another using generic views

I'm new to Django and programming in general. I'm trying to make a simple site that allows players of a sport sign up for leagues that have been created by the admin. In my models.py, I created two models:
from django.db import models
from django.forms import ModelForm
class League(models.Model):
league_name = models.CharField(max_length=100)
pub_date = models.DateTimeField('date published')
class Info(models.Model):
league = models.ManyToManyField(League)
name = models.CharField(max_length=50)
phone = models.IntegerField()
email = models.EmailField()
def __unicode__(self):
return self.info
class InfoForm (ModelForm):
class Meta:
model = Info
exclude = ('league')
From what I've read, I can probably use the Create/Update/Delete generic views to display a form for the user to sign up for the league. So with my app, I want the user to come to a simple homepage that lists the leagues, be able to click on the league and enter their info to sign up. Here's what my urlconf looks like:
from django.conf.urls.defaults import *
from mysite.player_info.models import League, Info, InfoForm
info_dict = {
'queryset': League.objects.all(),
}
InfoForm = {'form_class' : InfoForm}
urlpatterns = patterns('',
(r'^$', 'django.views.generic.list_detail.object_list', info_dict),
(r'^(?P<object_id>\d+)/$', 'django.views.generic.list_detail.object_detail', info_dict),
url(r'^(?P<object_id>\d+)/results/$', 'django.views.generic.list_detail.object_detail', dict(info_dict, template_name='player_info/results.html'), 'league_results'),
(r'^(?P<object_id>\d+)/info/create/$', 'django.views.generic.create_update.create_object', InfoForm),
)
Here's my problem: When I click on a league to sign up for on the homepage with my current setup, I get this error: TypeError at /league/1/info/create.... create_object() got an unexpected keyword argument 'object_id'. What am I doing wrong?
The issue isn't with your models, but rather with the function your "create" URL calls -- the line that calls django.views.generic.create_update.create_object() in urls.py. create_object() doesn't take an object_id argument, but you specified one in your url (r'^(?P<object_id>\d+)/info/create/$'). This makes sense -- you're creating an object, so you don't know its ID yet. create_object() only takes a form_class or model argument, as noted in the docs.
I'm guessing you're trying to create an Info object that is attached to a League object, and in that URL, <object_id> is the ID number of the League object; in which case, you shouldn't name that ID number, and instead should just use r"^\d+/info/create/$" as the URL. I'm not sure how you'll grab the league ID number using Django's create_object() function, though. You might have to write your own view handler. You may be able to use a custom ModelForm and pass it in with the form_class parameter, but I'm not sure.

Resources