plone.formwidget - Is it possible to set a MasterSelect Field as an AutocompleteFieldWidget? - autocomplete

I am trying to set a MasterSelect field to an AutocompleteFieldWidget.
I'm using AutocompleteFieldWidget from plone.formwidget.autocomplete and the MasterSelectField from plone.formwidget.MasterSelect. The slave field belonging to the MasterSelectField is also a MasterSelectField.
The autocomplete functions as it should (retrieving the values based on input), but the slave field's choices do not change. However, when its not set as an autocomplete, everything works as it should.
Edit:
In my buildout-cache, I looked at widget.py in plone.formwidget.masterselect and tried placing a print statement in getSlaves and that function wasn't getting called. I tried the render function and that wasn't getting called either. Then I placed a print statement in MasterSelectField and that was notgetting called. Setting the field to an Autocomplete widget removes any trace that its a Master Select field.
Edit: In the init.py file in plone.formwidget.masterselect, I placed a print statement in the init function of the MasterSelectField, and the slave widget does print, where as in getSlaves in widget.py it doesn't. This is the output I'm getting from printing in the init and what I should be getting in getSlaves:
({'action': 'vocabulary', 'masterID': 'form-widgets-IMyForm-master_field',
'control_param': 'master_value', 'name': 'IMyForm.slave_field',
'vocab_method': <class 'my.product.vocabulary.SlaveVocab'>},)
I have my interface:
from plone.directives import form
class IMyForm(model.Schema):
form.widget(master_field=AutocompleteFieldWidget)
master_field = MasterSelectField(
title=_(u'Master'),
slave_fields=({'name':'IMyForm.slave_field',
'action':'vocabulary',
'source':MySource,
'control_param':'master_value'
}),
required=True,
)
slave_field = MasterSelectField(title=_(u'Slave Field'),
source=SlaveVocab,
slave_fields=(....
)
required=False,
)
I have my source object for the master field:
class MySource(object):
implements(IQuerySource)
def __init__(self, context):
simple_terms = []
#Query portal catalog for unique indexes, and fill with simple terms
self.vocab = SimpleVocabulary(simple_terms)
def __contains__(self, term):
return self.vocab.__contains__(term)
def getTermByToken(self, token):
return self.getTermByToken(token)
def getTerm(self, value):
return self.getTerm(value)
def search(self, query_string):
return [term for term in self.vocab if query_string in term.title.lower()]
class MySourceBinder(object):
implements(IContextSourceBinder)
def __call__(self, context):
return MySource(context)
My slave field's source is:
class SlaveVocab(object):
grok.implements(IContextSourceBinder)
def __init__(self, **kw):
self.master_value = kw.get('master_value', None)
def __call__(self, context):
if self.master_value is None or self.master_value == "--NOVALUE--"
self.master_value = getattr(context,'master_field',None)
#Still nothing, return empty vocabulary
if self.master_value is None or self.master_value == '--NOVALUE--':
return SimpleVocabulary([])
terms = []
#If not null, building a simple vocabulary to return
return SimpleVocabulary(terms)
I did a print statement in call of the Slave Vocabulary and it was being called, but nothing was being passed in.
I also tried using another widget, ChosenFieldWidget. I get the same results in that it functions as it should, but the slave field's choices do not change. Is it possible to set a master select field to an autocomplete? If so, what am I doing wrong?
Also, I'm using Solgema.fullcalendar and the content type extends the IEventBasic behavior, so I don't have access to using my own form class I would've liked to have used since Solgema seems to render its own forms.
Edit:
I am using Plone 4.3

Related

In WTForms, how do I make optionally required field if another one is empty?

I have two fields where user can choose one to enter but they can't enter both and they can't skip both either. Requiered() makes it that they can't skip one and Optional() let them skip both.
I can only find an example for when you want another to be mandatory when you fill one field but I don't know how to modify it to my case because the example inherits a Required() validator which makes the two fields both required.
Following is the example I found here. Does anybody know if there's a native way to do what I described now or know how to modify this to suit my case?
class RequiredIf(Required):
# a validator which makes a field required if
# another field is set and has a truthy value
def __init__(self, other_field_name, *args, **kwargs):
self.other_field_name = other_field_name
super(RequiredIf, self).__init__(*args, **kwargs)
def __call__(self, form, field):
other_field = form._fields.get(self.other_field_name)
if other_field is None:
raise Exception('no field named "%s" in form' % self.other_field_name)
if bool(other_field.data):
super(RequiredIf, self).__call__(form, field)

Slick insert not working while trying to return inserted row

My goal here is to retrieve the Board entity upon insert. If the entity exists then I just want to return the existing object (which coincides with the argument of the add method). Otherwise I'd like to return the new row inserted in the database.
I am using Play 2.7 with Slick 3.2 and MySQL 5.7.
The implementation is based on this answer which is more than insightful.
Also from Essential Slick
exec(messages returning messages +=
Message("Dave", "So... what do we do now?"))
DAO code
#Singleton
class SlickDao #Inject()(db: Database,implicit val playDefaultContext: ExecutionContext) extends MyDao {
override def add(board: Board): Future[Board] = {
val insert = Boards
.filter(b => b.id === board.id && ).exists.result.flatMap { exists =>
if (!exists) Boards returning Boards += board
else DBIO.successful(board) // no-op - return specified board
}.transactionally
db.run(insert)
}
EDIT: also tried replacing the += part with
Boards returning Boards.map(_.id) into { (b, boardId) => sb.copy(id = boardId) } += board
and this does not work either
The table definition is the following:
object Board {
val Boards: TableQuery[BoardTable] = TableQuery[BoardTable]
class BoardTable(tag: Tag) extends Table[BoardRow](tag, "BOARDS") {
// columns
def id = column[String]("ID", O.Length(128))
def x = column[String]("X")
def y = column[Option[Int]]("Y")
// foreign key definitions
.....
// primary key definitions
def pk = primaryKey("PK_BOARDS", (id,y))
// default projection
def * = (boardId, x, y).mapTo[BoardRow]
}
}
I would expect that there would e a new row in the table but although the exists query gets executed
select exists(select `ID`, `X`, `Y`
from `BOARDS`
where ((`ID` = '92f10c23-2087-409a-9c4f-eb2d4d6c841f'));
and the result is false there is no insert.
There is neither any logging in the database that any insert statements are received (I am referring to the general_log file)
So first of all the problem for the query execution was a mishandling of the futures that the DAO produced. I was assigning the insert statement to a future but this future was never submitted to an execution context. My bad even more so that I did not mention it in the description of the problem.
But when this was actually fixed I could see the actual error in the logs of my application. The stack trace was the following:
slick.SlickException: This DBMS allows only a single column to be returned from an INSERT, and that column must be an AutoInc column.
at slick.jdbc.JdbcStatementBuilderComponent$JdbcCompiledInsert.buildReturnColumns(JdbcStatementBuilderComponent.scala:67)
at slick.jdbc.JdbcActionComponent$ReturningInsertActionComposerImpl.x$17$lzycompute(JdbcActionComponent.scala:659)
at slick.jdbc.JdbcActionComponent$ReturningInsertActionComposerImpl.x$17(JdbcActionComponent.scala:659)
at slick.jdbc.JdbcActionComponent$ReturningInsertActionComposerImpl.keyColumns$lzycompute(JdbcActionComponent.scala:659)
at slick.jdbc.JdbcActionComponent$ReturningInsertActionComposerImpl.keyColumns(JdbcActionComponent.scala:659)
So this is a MySQL thing in its core. I had to redesign my schema in order to make this retrieval after insert possible. This redesign includes an introduction of a dedicated primary key (completely unrelated to the business logic) which is also an AutoInc column as the stack trace prescribes.
In the end the solution becomes too involved and instead decided to use the actual argument of the add method to return if the insert was actually successful. So the implementation of the add method ended up being something like this
override def add(board: Board): Future[Board] = {
db.run(Boards.insertOrUpdate(board).map(_ => board))
}
while there was some appropriate Future error handling in the controller which was invoking the underlying repo.
If you're lucky enough and not using MySQL with Slick I suppose you might have been able to do this without a dedicated AutoInc primary key. If not then I suppose this is a one way road.

using a method trait in a traitsui factory

I am trying to figure out how Method traits work and if I can use one in a factory defined in a traits View object in order to have dynamic values passed to the factory.
Here is what I mean, a minimal test case which works except for the factory behavior (it runs, but using the factory will cause a crash):
from traits.api import HasTraits, Method, Str, List, Instance, method, Int, Any
from traitsui.api import View, Item, TableEditor, ObjectColumn, TextEditor
class Dog(HasTraits):
name = Str('Fido')
value = Int(5)
class DogTable(HasTraits):
def factory(self):
return Dog(value=self.current_user_value)
dog_factory = Method(factory)
dogs = List(Instance(Dog))
current_user_value = Int(3)
def _dogs_default(self):
return [
Dog(name='Joe', value=6),
Dog(name='Ted', value=2),
]
traits_view = View(
Item('dogs',
editor=TableEditor( columns =
[
ObjectColumn(label='name', editor=TextEditor(), name='name'),
ObjectColumn(label='value', editor=TextEditor(), name='value'),
],
row_factory = dog_factory,
)
),
Item('current_user_value'),
height=300, width=300,
)
DogTable().configure_traits()
So what I am trying to do here is set up the factory so that the user can add new items to the table which contain as an initial value whatever value is currently specified by the user in the GUI.
Is there some way to do this? I thought that using a Method trait would resolve this problem by referring to the bound method and allow me to actually call the bound method in this instance, but it seems like Method's semantics are no different from Callable. And I can't figure out any way to supply arguments to the factory dynamically, other possibly than by hacky use of eval or global variables (factory_row_args rejects dynamic arguments).
Yesterday I was very tired, today I thought of an obvious way to do it:
def dog_factory(self):
return Dog(value=self.current_user_value)
def view_getter(self):
return View(
Item('dogs',
editor=TableEditor( columns =
[
ObjectColumn(label='name', editor=TextEditor(), name='name'),
ObjectColumn(label='value', editor=TextEditor(), name='value'),
],
row_factory = self.dog_factory
)
),
Item('current_user_value'),
height=300, width=300,
)
def configure_traits(self):
super(DogTable, self).configure_traits(view=self.view_getter())

flask redirect from closure

def check_login(func):
"""Check if user is logged in."""
def decorator(*args, **kwargs):
if not login_session_test():
print ("Not logged in - redirect to /login")
flash ("Well that was wrong. Chicken winner. No more dinner.")
return redirect(url_for('login'))
print ("Logged in, do what needs to be done.")
return func(*args, **kwargs)
return decorator
#check_login
#app.route("/sacred/secret/stuff", methods=['GET'])
def funfunfun():
return "Super fun"
It never redirects to /login but gives some garbage like page.
Swapping the #/closure order yields:
AssertionError: View function mapping is overwriting an existing endpoint function: decorator
I am not yet fully pythonized.
Your decorator order is incorrect, and you are not copying across the function name to the wrapper function.
Use this order:
#app.route("/sacred/secret/stuff", methods=['GET'])
#check_login
def funfunfun():
return "Super fun"
Otherwise the undecorated function is registered for the view.
Use #functools.wraps() to have various pieces of metadata copied over from the original wrapped function to the wrapper that replaces it:
from functools import wraps
def check_login(func):
"""Check if user is logged in."""
#wraps(func)
def decorator(*args, **kwargs):
if not login_session_test():
print ("Not logged in - redirect to /login")
flash ("Well that was wrong. Chicken winner. No more dinner.")
return redirect(url_for('login'))
print ("Logged in, do what needs to be done.")
return func(*args, **kwargs)
return decorator
Routes need an endpoint name, and if you don't specify one explicitly, Flask uses the name of the function (from functionobj.__name__). But your decorator wrapper object has the name decorator, so if you use the decorator more than once Flask complains that it already has used that endpoint name.
#functools.wraps() copies across the __name__ attribute, so now your decorator wrapper is also called funfunfun, whereas another decorated route function gets to keep its name too.

Django-Nonrel with Mongodb listfield

I am trying to implement manytomany field relation in django-nonrel on mongodb. It was suggessted at to:
Django-nonrel form field for ListField
Following the accepted answer
models.py
class MyClass(models.Model):
field = ListField(models.ForeignKey(AnotherClass))
i am not sure where the following goes, it has been tested in fields.py, widgets,py, models.py
class ModelListField(ListField):
def formfield(self, **kwargs):
return FormListField(**kwargs)
class ListFieldWidget(SelectMultiple):
pass
class FormListField(MultipleChoiceField):
"""
This is a custom form field that can display a ModelListField as a Multiple Select GUI element.
"""
widget = ListFieldWidget
def clean(self, value):
#TODO: clean your data in whatever way is correct in your case and return cleaned data instead of just the value
return value
admin.py
class MyClassAdmin(admin.ModelAdmin):
form = MyClassForm
def __init__(self, model, admin_site):
super(MyClassAdmin,self).__init__(model, admin_site)
admin.site.register(MyClass, MyClassAdmin)
The following Errors keep popping up:
If the middle custom class code is used in models.py
name 'SelectMultiple' is not defined
If custom class code is taken off models.py:
No form field implemented for <class 'djangotoolbox.fields.ListField'>
You just need to import SelectMultiple by the sound of it. You can put the code in any of those three files, fields.py would make sense.
Since it's pretty usual to have:
from django import forms
at the top of your file already, you probably just want to edit the code below to:
# you'll have to work out how to import the Mongo ListField for yourself :)
class ModelListField(ListField):
def formfield(self, **kwargs):
return FormListField(**kwargs)
class ListFieldWidget(forms.SelectMultiple):
pass
class FormListField(forms.MultipleChoiceField):
"""
This is a custom form field that can display a ModelListField as a Multiple Select GUI element.
"""
widget = ListFieldWidget
def clean(self, value):
#TODO: clean your data in whatever way is correct in your case and return cleaned data instead of just the value
return value
You probably also want to try and learn a bit more about how python works, how to import modules etc.