Several polymorphic associations using class_name? - activerecord

I am trying to create an article which holds in_prices and out_prices (one for each country) using rails polymorphic associations and nested forms.
I have a data model which looks something like this:
# app/models/article.rb
class Article < ActiveRecord::Base
has_many :out_prices, :class_name => "Price", :as => :priceable
has_many :in_prices, :class_name => "Price", :as => :priceable
end
# app/models/price.rb
class Price < ActiveRecord::Base
belongs_to :priceable, :polymorphic => true
end
# db.schema for prices
create_table "prices", :force => true do |t|
t.integer "value"
t.integer "country_id"
t.integer "priceable_id"
t.string "priceable_type"
end
The article and its associations are created using a nested form like:
# app/views/articles/_article_form.html.erb
<%= form_for setup_article(#article) do |f| %>
<%= f.fields_for :in_prices do |ff| %>
<%= ff.text_field :value %>
<% end %>
<%= f.fields_for :out_prices do |ff| %>
<%= ff.text_field :value %>
<% end %>
<% end %>
The setup_article method is a helper method to build the associations:
# app/helpers/articles_helper.rb
def setup_article(article)
if article.in_prices.nil?
# Setup one in price for each country
Country.all.each do |country|
article.in_prices.build(:value => 0, :country_id => country.id)
end
end
if article.out_prices.nil?
# Setup one out price for each country
Country.all.each do |country|
article.out_prices.build(:value => 0, :country_id => country.id)
end
end
article
end
setup_article is needed to make sure empty price form fields (one for each country) shows up when creating a new article.
Now to the actual problem. When I edit an already created Article (which have associated in_prices and out_prices) Rails won't be able to differentiate between these different types of polymorphic associations (in_prices and out_prices). Because of this both nested form helpers renderes form fields for all associated prices, which isn't the desired behavior. I only want to list in_prices in the one of the nested forms, and the out_prices in the other.
How should these associations be configured to make sure rails can differentiate between the in_prices and out_prices associations in the two different nested form helpers?
EDIT (SOLVED)
A friend of mine pointed out I need to add yet another field in the prices table to flag what type of price it is. I called this field price_type and the db.schema ended up looking like this:
# db.schema for prices
create_table "prices", :force => true do |t|
t.integer "value"
t.integer "price_type"
t.integer "country_id"
t.integer "priceable_id"
t.string "priceable_type"
end
Note: Don't name this field 'type' since this is a reserved name.
The 'price_type' field can be populated either by adding a hidden field in the nested forms (less safe), or to handle it in the controller before saving the article and its associated data. I chose to add it as hidden params like:
# app/views/articles/_article_form.html.erb
<%= form_for setup_article(#article) do |f| %>
<%= f.fields_for :in_prices do |ff| %>
<%= ff.text_field :value %>
<%= ff.text_field :price_type, :value => "in" %>
<% end %>
<%= f.fields_for :out_prices do |ff| %>
<%= ff.text_field :value %>
<%= ff.text_field :price_type, :value => "out" %>
<% end %>
<% end %>
To make sure the associations gets filtered correctly they need to be declared with the ':conditions' tag, like:
# app/models/article.rb
class Article < ActiveRecord::Base
has_many :out_prices, :class_name => "Price", :as => :priceable, :conditions => { :price_type => "in" }
has_many :in_prices, :class_name => "Price", :as => :priceable, :conditions => { :price_type => "out" }
end
.. and now everything works as expected. cheers!

Related

Padrino and sequel nested attributes form

I am trying to make a form for storing data hierarchy through the associations.
My structure of database is
meta
- ID (PK)
version
- meta_id (FK of meta)
- ID (PK)
- lang (FK from lang ...)
- ....
data
- ID (FK of version)
I need to store multiple language content under one ID.
Models are
class Meta < Sequel::Model(:link_meta)
one_to_many :version
end
class Version < Sequel::Model(:link_version)
one_to_one :meta, key: :meta_id
one_to_many :data, key: :id
one_to_one :lang, key: :lang
end
class Link < Sequel::Model(:link)
plugin :timestamps, :create => :created, :update => :updated
many_to_one :version, key: :id
end
And my form for creating input is.
<% form_for :links, url(:links, :create), :class => 'form-horizontal' do |f| %>
<%= partial 'links/form', :locals => { :f => f } %>
<% end %>
And this is my partial
<% f.fields_for :version, :class => 'form-horizontal' do |cz| %>
<%= cz.hidden_field :lang, :value => Lang.select(:id).where(:lang => "cz").map(:id) %>
<% cz.fields_for :data, :class => 'form-horizontal' do |link| %>
<% error = #links.errors.key?(:url) && #links.errors[:url].count > 0 %>
<fieldset class='control-group <%= error ? 'has-error' : ''%>'>
... form content
</fieldset>
<% end %>
<% f.fields_for :version, :class => 'form-horizontal' do |en| %>
<%= en.hidden_field :lang, :value => Lang.select(:id).where(:lang => "cz").map(:id) %>
<% en.fields_for :data, :class => 'form-horizontal' do |link| %>
<% error = #links.errors.key?(:url) && #links.errors[:url].count > 0 %>
<fieldset class='control-group <%= error ? 'has-error' : ''%>'>
... another form content
</fieldset>
<% end %>
<% end %>
But it fails on
undefined method `data' for [:class, "form-horizontal"]:Array
I tried different association combinations etc, I feel lost.
Thanks
Levi

Access polymorphic attribute in the view

Trying to work out this small issue, I have finally got my polymorphic association working but now cant seem to figure out how to show an image in the view once a record has been saved.
My setup
class Image < ActiveRecord::Base
belongs_to :imageable, polymorphic: true
attr_accessible :photo
has_attached_file :photo, :styles => { :small_blog => "250x250#", :large_blog => "680x224#", :thumb => "95x95#" }
end
class Post < ActiveRecord::Base
has_many :images, as: :imageable
accepts_nested_attributes_for :images
attr_accessible :comments, :title, :images_attributes
end
So in my view i have this at the moment, which is throwing undefined method 'photo' error
<% #posts.each do |p| %>
<%= image_tag(p.photo.url(:large_blog), :class => 'image') %>
<% end %>
Any pointers appreciated
Post has many images, and each image has a photo. So it can go like this:
<% #posts.each do |p| %>
<% p.images.each do |i| %>
<%= image_tag(i.photo.url(:large_blog), :class => 'image') %>
<% end %>
<% end %>

Form not saving if try to upload image with Paperclip

so paperclip, seems as if there is a different way to get it working every time i use it.
So at the moment i try and submit a form but it fails and re renders the form (which is what its supposed to do if the form does not save).
This is my setup so far
Gemfile
gem "paperclip", "~> 3.0"
Controller
def new
#post = Post.new
end
def create
#user = current_user
#post = #user.posts.new(params[:post])
if #post.save
redirect_to root_path, :notice => 'Post Successfully Created'
else
render :action => 'new'
end
end
Post Model
class Post < ActiveRecord::Base
belongs_to :category
belongs_to :user
attr_accessible :comments, :title, :category_id, :user_id, :photo
has_attached_file :photo, :styles => { :medium => "300x300>", :thumb => "100x100>" }
end
Form
<%= form_for #post, :class => 'post-form', :html => { :multipart => true } do |f| %>
<%= f.label :title, "Title", :class => 'title_label' %>
<%= f.text_field :title %>
<%= f.label :category_id, "Choose Category", :class => 'title_label' %>
<%= f.collection_select(:category_id, Category.all, :id, :name, :prompt => "Please Select a Category") %>
<%= f.label :comments, "Comments", :class => 'title_label' %>
<%= f.text_area :comments %><br>
<%= f.file_field :photo %>
<%= f.submit 'Submit', :class => 'btn' %>
<% end %>
My migration to add photo was successful as my schema looks like so
create_table "posts", :force => true do |t|
t.string "title"
t.text "comments"
t.integer "category_id"
t.integer "user_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "photo_file_name"
t.string "photo_content_type"
t.integer "photo_file_size"
t.datetime "photo_updated_at"
end
Can anyone see reason why this is not working as expected?
EDIT
Do i need ImageMagick installed to allow the upload of an image or is this just for rendering an image in the view?
ok so from the comments i have started to try and debug and put this in my view
<%= #post.errors.full_messages %>
I get this returned
["Photo C:/Users/RICHAR~1/AppData/Local/Temp/bitman20130724-5600-agvtgn.png is not recognized by the 'identify' command.", "Photo C:/Users/RICHAR~1/AppData/Local/Temp/bitman20130724-5600-agvtgn.png is not recognized by the 'identify' command."]
Any ideas?
Thanks
Step 1
From paperclip documentation:
ImageMagick must be installed and Paperclip must have access to it. To ensure that it does, on your command line, run which convert (one of the ImageMagick utilities). This will give you the path where that utility is installed. For example, it might return /usr/local/bin/convert.
Then, in your environment config file, let Paperclip know to look there by adding that directory to its path.
In development mode, you might add this line to config/environments/development.rb:
Paperclip.options[:command_path] = "/usr/local/bin/"
Step 2
For agvtgn.png is not recognized by the 'identify' command. error:
Not sure how you do this in windows, for linux this is what you need to do:
$ which identify
/path/to/identify
Set command_path to that path in config/environments/development.rb:
Paperclip.options[:command_path] = "/path/to"
also you need ImageMagick to be installed
http://ganeshprasadsr.blogspot.com/2010/12/paperclip-issue-is-not-recognized-by.html
What I think - You just need to install ImageMagick.
p.s. Windows is the worst development machine. You could install at least a virtual machine running on linux.

Nested Form Doesn't Save Nested Model Rails 3

I am trying to create a form that creates a game and game_players at the same time.
The problem I am having is that when I submit the form, the game is created, but the game_players are not.
I've looked around, but haven't found any helpful answers.
Game Model
class Game < ActiveRecord::Base
belongs_to :league
has_many :game_players, :dependent => :destroy
accepts_nested_attributes_for :game_players
attr_accessible :league_id, :game_date
validate :league_id, :presence => true
end
Game_Player Model
class GamePlayer < ActiveRecord::Base
belongs_to :game
has_many :users
validate :game_id, :presence => true
validate :user_id, :presence => true
end
Game Controller
class GamesController < ApplicationController
def new
#title = "New Game"
#game = Game.new
3.times { #game.game_players.build }
end
def create
#game = Game.new(:league_id => cookies[:league_id])
if #game.save
flash[:success] = "Succesfully Created Game"
redirect_to League.find_by_id(cookies[:league_id])
else
#title = "New Game"
render 'new'
end
end
Form
<%= form_for #game do |f| %>
<%= f.fields_for :game_players do |builder| %>
<p>
<%= builder.label :user_id, "User" %><br />
<%= builder.text_field :user_id %><br />
</p>
<% end %>
<p><%= f.submit "Submit" %></p>
<% end %>
most likely you need to pass :game_players_attributes to attr_accessible since .new respect mass-assignment security

How to write a custom SimpleForm Builder to replace <INPUT> by <P>?

Extract of my Gemfile:
gem 'rails', '3.0.3'
gem 'inherited_resources', '1.2.1'
gem 'simple_form', '1.4.0'
For any resource, I have 1 view for the 3 actions (new, edit & show). Example:
<h1><%= I18n.t('admin.form.'+action_name.downcase, :name => controller_friendly_name) %></h1>
<%= simple_form_for([:admin, resource]) do |f| %>
<%= render "admin/shared/errors" %>
<%= f.input :title,
:label => "Title",
:hint => I18n.t('admin.form.input.title.hint', :name => controller_friendly_name),
:required => true,
:error => false,
:input_html => { :class => :large, :placeholder => I18n.t('admin.form.input.title.placeholder', :name => controller_friendly_name) }
%>
<%= f.input :is_visible,
:as => :radio,
:label => "Visible",
:error => false,
:required => true,
:collection => [['Yes', true], ['No', false]],
:wrapper_class => 'checkboxes-and-radiobuttons',
:checked => true
%>
<%= render "admin/shared/validation", :f => f %>
<% end %>
<% init_javascript "MyApplication.Form.disable();" if [:show].include?(action_name.to_sym) %>
See how the #show action set all the fields to disabled ? This is ugly.
Consider I can't refactor the views to have a show.html.erb file.
What I want to do:
When the action is #show, the simple_form builder use a custom builder wich replace <input>, <textarea>, <select> by <p> html tag, with the value.
Furthermore, I will customise the radiobuttons, checkboxes to.
My first step:
# app/inputs/showvalue_input.rb
class ShowvalueInput < SimpleForm::Inputs::Base
def input
# how to change 'text_field' by <p> container ?
#builder.text_field(attribute_name, input_html_options)
end
end
Can't find the way to do it. Custom Form Builders or Custom Inputs (with monkey patching) ?
Thank for the help !
Here's my solution
in my application_helper.rb:
def set_show_method_to_builder(builder)
builder.instance_eval <<-EVAL
def show?
#{action_name == "show"}
end
EVAL
end
In my forms (in the simple_form block):
<%- set_show_method_to_builder(f) -%>
And finally, in #app/inputs/string_input.rb:
class StringInput < SimpleForm::Inputs::StringInput
def input
if #builder.show?
content_tag(:p, #builder.object[attribute_name], :class => :show)
else
super
end
end
end
There's some problem with data types not mapped, but it's another story:
Can't create Custom inputs for some (Text, Booleans, ...) types, with SimpleForm

Resources