Arc and Arc_Ecto Storying Multiple images using :map field - echo

Im trying to attach multiple images to a field. I could easily create an association to a images model, but I would like to see how could the same be accomplished with a map/array field.
The model looks as follows.
schema "users" do
field :images, {:array}
end
def changeset(user, params \\ :invalid) do
user
|> cast(params, [:name])
|> cast_attachments(params, [:avatar])
|> validate_required([:name, :avatar])
end

As far as I know using an array/map directly is not supported.
You could use and embedded schema to save it as a map though.
This should work:
defmodule Image do
use Ecto.Schema
use Arc.Ecto.Schema
import Ecto
import Ecto.Changeset
#required_fields ~w(file)
#optional_fields ~w()
embedded_schema do
field :file, MyApp.UserImage.Type
end
def changeset(model, params \\ :empty) do
model
|> cast(params, #required_fields, #optional_fields)
|> cast_attachments(params, [:file])
end
end
defmodule User do
use Ecto.Schema
import Ecto
import Ecto.Changeset
schema "projects" do
field :code, :string
embeds_many :images, MyApp.Image
end
def changeset(model, params \\ :empty) do
model
|> cast(params, #required_fields, #optional_fields)
|> cast_embed(:images) # invoke changeset in the embed module
end
end
And then you can use it like this
images = [%{file: "image1"}, %{file: "image2"}]
changeset = User.changeset(user, %{"images" => images})
new_user = Repo.update!(changeset)
urls = Enum.map new_user.images, fn image ->
UserImage.urls({image.file, new_user})
end
The only drawback is that you can't use the scope param in the UserImage module anymore when saving the images. This is because arc_ecto uses the model as the scope when you invoke cast_attachments function, and now you don't have the original model (User) when calling the function.
In the migration file you should define the images field to be a :map

Related

Rails 4: form object for update action for 2 models

I have a form object that handles 2 associated objects. I'm trying to get some validation working on the form object, but am not having much success.
I think the issue is that the attributes I'm validating are delegated to the model objects, but I'm not sure how else to handle this situation: when the form is displayed, these attributes need to pull from the actual models to show the values on the form in edit mode, but when I update the form, I need to validate the submitted parameters, not the values being pulled from the models themselves.
One thought I have is using a EditBorrowerForm object that pulls attributes from associated models and a UpdateBorrowerForm that performs all the validations and updates the models. But that seems like a lot of extra work for something trivial.
I've seen a lot of form objects used for CREATE actions (where you only validate fields before passing them to the models), but haven't seen any good examples of UPDATE actions (where you need to both pull fields from models as well as update them).
Here are the models I'm working with:
class Client < ActiveRecord::Base
has_many :borrowers
end
class Borrower < ActiveRecord::Base
belongs_to :client
end
And my form object:
class BorrowerForm
include ActiveModel::Model
attr_accessor :client, :borrower
delegate :first_name, :last_name, :date_of_birth, to: :client
delegate :citizenship, :marital_status, to: :borrower
validates :first_name, :last_name, :citizenship, presence: true
def initialize(borrower)
#borrower = borrower
#client = #borrower.client
end
def update_attributes(params)
# Never validates properly...
if valid?
ActiveRecord::Base.transaction do
borrower.update_attributes(params.slice(:citizenship, :marital_status)
client.update_attributes(params.slice(:first_name, :last_name, :date_of_birth)
end
true
else
false
end
end
end
And the controller:
class BorrowersController < ApplicationController
def edit
#borrower = BorrowerForm.new(Borrower.find(params[:id])
end
def update
#borrower = BorrowerForm.new(Borrower.find(params[:id])
if #borrower.update_attributes(params)
redirect_to :index
else
render :edit
end
end
end

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

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

How to pass a Casbah result to view in Play Framework?

Basically what I want to achieve is simply to execute a MongoDB query using Casbah in a Play Framework 2.2 controller, pass the result(s) to a view and present the result(s) in HTML.
I think my main problem is, I don't know how to define the parameters of the view, because the view doesn't know any of the result types I tried and a #import in the line below doesn't seem to help either.
Controller action:
def read = Action {
val mongoClient = MongoClient("localhost", 27017)
val db = mongoClient("sampleapp")
val coll = db("testcoll1")
// Query: get all documents
val docs = coll.find() // Type: coll.CursorType
val list = docs.toList
Ok(views.html.casbahsamples.read(list))
}
As you can see, I try to convert the result to a List, because docs seems to be of type coll.CursorType where coll is the name of my value and when it's passed to the view I don't have a clue how to use this in the parameter line because the compiler asks for type coll.CursorType but doesn't even know about coll.
Anyway, if I declare the type List[Any] for my list parameter in the view, this will partly work, but I won't be able to access any properties of the result documents, because none of the methods work on objects of type Any.
I think, the best way would be to declare List[BasicDBObject] in the view's parameter line, but BasicDBObject is not a known type. So I tried to import it in the line below (as far as I know that's the only place in a view where imports are allowed). But this doesn't change anything.
View:
#(list: List[BasicDBObject])
#import com.mongodb.BasicDBObject
#* also tried:
#import com.mongodb.casbah.Imports._
*#
<h2>Query results</h2>
<h3>Number of query results:</h3>
<p>#list.length</p>
<h3>Results:</h3>
<code>#list</code><!-- this will be a JSON string representation, but that's not what I want -->
<h4>List:</h4>
#import java.math.BigInteger; var i = 0;
#for(doc <- list) {
#{i += 1; i}:<br>
#*
doc is of type BasicDBObject as I found out doing this:
#doc.asInstanceOf[AnyRef].getClass.getSimpleName
*#
#doc
#* The next line results in a compilation error: value filter is not a member of Any *#
#defining(doc.filter(_.isInstanceOf[BasicDBObject]).map(_.asInstanceOf[BasicDBObject])) { docX =>
#docX.asInstanceOf[AnyRef].getClass.getSimpleName
}
<br><br>
<dl style="background: #ccc">
<dt>_id</dt>
#* doc will be of type BasicDBObject *#
#* <dd>#doc.getString("pie")</dd> *#
#* <dd>#docX.getString("pie")</dd> *#
</dl>
<br><br>
}
My attempt to convert Any to BasicDBObject does not work, because the compiler says filter and map are not members of Any.
What do I have to do to use/access/display the results of my query in a view?
(BTW: The full code is on GitHub)
The answer to this is so simple, I can't believe I didn't have this idea earlier...
I just had to add the full package name in the first line of the view...
#(list: List[com.mongodb.casbah.Imports.DBObject])
..and then I don't even have to import the package. I can now display each attribute value of a document separately with e.g. #doc.get("_id").
Should anybody be interested, here's the full view file.

Play 2: idiomatic approach to binding a form to List[Model]

I have several CRUD operations to perform, each one on a collection of models (e.g. game schedule, team roster, game result, game stats, etc.).
Up to this point in my Play experience (just a few months, 1 project live) I have been working with one-to-one form binding to model instance.
I know I can numerically index form field names, but then how to bind the posted form to List[Model]?
This is what my one-to-one binding looks like:
// abstract away bindFromRequest to make binding more concise in controllers
def bindForm[T](f: play.api.data.Form[T])(implicit r: play.api.mvc.Request[_]) =
f.bindFromRequest fold(e=> Left(e.errorsAsJson), Right(_))
and then in controllers:
val result = for {
model <- bindForm(form).right
id <- dao.create(model) as json
} yield id
what I would like to do is the same, but instead of model binding returning a single Model on success, have it return a List[Model], and pass on to overloaded DAO create/edit/delete operations.
I see that there is a list method that one can use as part of a Form mapping, but I have a feeling that that would wreak havoc with my JDBC query wrapper (ScalaQuery/Slick), whose case class/companion object mapping would likely not play well with collections properties.
For example, existing mapping of a game schedule looks like:
object CompositeForm {
import play.api.data.{Form, Forms}, Forms._
import utils.Validator.Bindings.jodaLocalTimeFormat
val mapper = mapping(
'id -> ignored(0),
'gameDate -> jodaDate,
'gameType -> optional(text),
'location -> optional(text),
'team1 -> number,
'team2 -> number
)(Composite.apply)(Composite.unapply)
val form = Form( mapper )
}
using list(gameDate), list(gameType) instead then means that form binding will return a single Composite instance whose properties are all collections -- maybe it will work, but doesn't seem nearly as clean/straightforward as working with a collection of model instances.
Ideas appreciated ;-)
The as yet documented seq() option in play form mapping was pointed out to me on Play google group by #Julien Richard-Foy
Using repeat() and seq() together allows one to repeat a form mapping, thus creating a collection of indexed foo.bar[n] formfield elements.
Example
object ScheduleForm {
import play.api.data.{Form, Forms}, Forms._
val mapper = mapping(
'composite -> seq(CompositeForm.mapper),
'note -> seq(ScheduleNoteForm.mapper)
)(Schedule.apply)(Schedule.unapply)
val form = Form( mapper )
}
and then in a view:
#repeat(_form("composite"), min=#numGames) { f=>
#inputDate(f("gameDate"), '_label-> "Game Date", 'class-> "required")
...
}

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.