Rails 3 I18n label translation for nested_attributes in has_many relationship - internationalization

Using: Rails 3.0.3, Ruby 1.9.2
Here's the relationship:
class Person < ActiveRecord::Base
has_many :contact_methods
accepts_nested_attributes_for :contact_methods
end
class ContactMethod < ActiveRecord::Base
attr_accessible :info
belongs_to :person
end
Now when I try to customize the contact_method labels in I18n, it doesn't recognize it.
en:
helpers:
label:
person[contact_methods_attributes]:
info: 'Custom label here'
I have also tried:
person[contact_method_attributes]
This works just fine for 1-1 relationships, e.g.
person[wife_attributes]:
name: 'My wife'
but not person[wives_attributes]
Thanks in advance

I did this with :
en:
helpers:
label:
person[contact_methods_attributes][0]:
info: 'First custom label here'
person[contact_methods_attributes][1]:
info: 'Second custom label here'
Which is nice but not ideal when you have unlimited options.. I would just specify a custom translation key in the form builder :)
en:
helpers:
label:
person[contact_methods_attributes][any]:
info: 'Custom label here'
<% fields_for :contact_methods do |builder| %>
<%= builder.label :info, t("helpers.person[contact_methods_attributes][any].info") %>
<%= builder.text_field :info %>
<% end %>
EDIT :
Don't know if it's a new feature but seems to work like a charm doing this :
en:
helpers:
label:
person:
contact_methods:
info: 'Custom label here'

In my Rails 3.2.13 app the attribute labels are picked up automatically from the model whose attributes are embedded. Please note that I am nesting attributes of the belongs_to model, but it might also work the other way around.
My example from working code:
The models:
class User < ActiveRecord::Base
belongs_to :account
accepts_nested_attributes_for :account
# ...
end
class Account < ActiveRecord::Base
has_many :users
end
The view:
<h2><%= t(:sign_up) %></h2>
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :email %><br />
<%= f.email_field :email %></div>
<%= f.fields_for :account do |account_form| %>
<div><%= account_form.label :subdomain %><br />
<%= account_form.text_field :subdomain %>.<%= request.host %> <span class="hint"></span></div>
<% end %>
translations_de.yml:
activerecord:
models:
account: Konto
user: Benutzer
attributes:
account:
name: Firmenname
subdomain: Subdomain
users: Benutzer
user:
# ... no sign of subdomain here ...
And the view is rendered with the subdomain label translated based on
activerecord.attributes.account.subdomain
Nice. :)
I'm not sure but it might require you to use the activerecord path instead of the helpers one.

Related

Unpermitted parameters for nested form many-to-many relationship Rails 4

I have a simple blog app, where I want to be able to create a Post and also create a new Tag for it in the same form, using a nested form.
Post and Tag have a many-to-many relationship, via a join table:
class PostTag < ActiveRecord::Base
belongs_to :post
belongs_to :tag
end
Here's the tag model:
class Tag < ActiveRecord::Base
has_many :post_tags
has_many :posts, :through => :post_tags
validates_presence_of :name
validates_uniqueness_of :name
end
Post model accepts nested attributes for tags:
class Post < ActiveRecord::Base
has_many :post_tags
has_many :tags, :through => :post_tags
accepts_nested_attributes_for :tags
validates_presence_of :name, :content
end
On the posts controller, I permit tags_attributes:
def post_params
params.require(:post).permit(:name, :content, :tag_ids => [], :tags_attributes => [:id, :name])
end
In my form for a new post, where I want to be able to either associate already existing tags (via checkboxes) or create a new one via a nested form using fields_for:
....
<div class="field">
<%= f.collection_check_boxes :tag_ids, Tag.all, :id, :name %><br>
<%= f.fields_for [#post, Tag.new] do |tag_form| %>
<p>Add a new tag:</p><br>
<%= tag_form.label :name %>
<%= tag_form.text_field :name %>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
My error is "Unpermitted parameters: tag":
Parameters: {"utf8"=>"✓", "authenticity_token"=>"dZnCgFxrvuoY4bIUMMxI7kTLEr/R32pUX55wwHZsS4Q=", "post"=>{"name"=>"post title", "content"=>"post content", "tag_ids"=>[""], "tag"=>{"name"=>"new tag"}}, "commit"=>"Create Post"}
Unpermitted parameters: tag
Change:
<%= f.fields_for [#post, Tag.new] do |tag_form| %>
to
<%= f.fields_for(:tags, Tag.new) do |tag_form| %>

Nested attributes that do not belong to the current object using has_many :through

My end goal is to be able to add costumes to an agreement in the agreement view, regarless of whether or not they exist in the Costume database yet. My main difficulty is that a costume does not belong to an agreement, they exist independently but can be added to an agreement. If a new costume is added that isn't in the Costume database, it will add it to the database. Is there a way to do this? I can't find a tutorial about this anywhere. If I could get the controller from this post, I think that is all I need. I just need to create one costume every time the form is displayed. Thanks so much.
My models are as follows:
# app/models/agreement.rb
class Agreement < ActiveRecord::Base
has_and_belongs_to_many :costumes, join_table: :agreement_costumes
accepts_nested_attributes_for :costumes, :reject_if => :all_blank
end
# app/models/costume.rb
class Costume < ActiveRecord::Base
has_and_belongs_to_many :agreements, join_table: :agreement_costumes
end
# app/models/agreement_costume.rb
class AgreementCostume < ActiveRecord::Base
belongs_to :agreement
belongs_to :costume
end
My agreement controller is as follows:
class AgreementsController < ApplicationController
before_action :set_agreement, only: [:show, :edit, :update, :destroy]
# Some methods ommitted
# GET /agreements/new
def new
#agreement = Agreement.new
#agreement.costumes.build
#costumes = Costume.all
end
private
# Use callbacks to share common setup or constraints between actions.
def set_agreement
#agreement = Agreement.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def agreement_params
params.require(:agreement).permit(:name, :phone, :email, :mailbox, :wesid, :title, :start, :end, :due, :financer, :employee, :costumes_attributes => [:cid, :description, :wd, :back, :photo])
end
:end
And finally, the agreement view
<%= form_for(#agreement) do |f| %>
<% if #agreement.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#agreement.errors.count, "error") %> prohibited this agreement from being saved:</h2>
<ul>
<% #agreement.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<!-- Agreement fields omitted -->
<%= f.fields_for :costumes do |c| %>
<div class="field">
<%= c.label :cid %><br>
<%= c.number_field :cid %>
</div>
<div class="field">
<%= c.label :description %><br>
<%= c.text_field :description %>
</div>
<!-- etc with costume fields -->
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
You need accepts_nested_attributes_for in your Agreement controller if you want to create new costumes there.
class Agreement < ActiveRecord::Base
has_many :agreement_costumes
has_many :costumes, through: :agreement_costumes
accepts_nested_attributes_for :costumes, :reject_if => :all_blank, :allow_destroy => :false,
end
and then in the agreements#new action in your Agreements controller you build a costume entry
def new
#agreement = Agreement.new
#agreement.costumes.build
#costumes = Costumes.all
end
#agreement.costumes.build creates a blank instance of a costume, related to this agreement. You then access the params of that costume in the form using :costumes Don't forget to whitelist your nested params in your Agreements controller:
def agreement_params
params.require(:agreement).permit(:name, :phone, :email, :mailbox, :wesid,
:title, :start, :end, :due, :financer, :employee, costumes_attributes[:name, :price, :id])
end
Now your form has to have a place to choose existing costumes from a list and/or add a new one.
<%= f.label :costumes, "Costumes" %>
<%= f.collection_select :costumes, :agreement_id, :id, :name, price, {}, {multiple: true} %>
<strong>Add a new costume</strong>
<%= f.fields_for :costumes do |c|
<%= c.label :name %>
<%= c.text_field :name %>
<br>
<%= c.label :price %>
<%= c.number_field :price %>
<br>
<% end %>
This should get you most of the way there. I've had to guess at some of your code, so this isn't going to be a cut and paste answer. You'll need to build what you can off of this and probably do a little more Googling here and there. If you wanted to have a popup form to add a costume to the list on the fly and then be able to choose it in the collection_select, you would have to turn on Turbo_links in your app, create a Javascript popup form. Then use AJAX to submit the form, save the costume to the database, run another .js.erb file that would then update the collection_select text list using a reload of just that list via your Javascript. It's actually probably easier than having a new costume form nested in this form.

Not saving on the database from nested form

I want to create a dummy account for a new User object. Whenever a user signs up in my app it should automatically create an account for him.
But what's happening is, I'm saving in the database the new User, but not the new Account. I'm also not aware what's the best way to respect the MVC pattern design when it comes to this situation. I'm afraid of replicating code or to have one controller doing the work of two.
The sign up form, has nested resources (and it's build on top of devise)
<h2>Sign up</h2>
<%= form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<div><%= f.label :name %><br />
<%= f.text_field :name %></div>
<div><%= f.label :email %><br />
<%= f.email_field :email, :autofocus => true %></div>
<div><%= f.label :password %><br />
<%= f.password_field :password %></div>
<div><%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %></div>
<p>Account name</p>
<%= f.fields_for :account do |builder| %>
<fieldset>
<%= builder.label :title %>
<%= builder.text_field :title %>
</fieldset>
<% end %>
<div><%= f.submit "Sign up" %></div>
<% end %>
<%= render "devise/shared/links" %>
My User Model:
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_many :accounts
accepts_nested_attributes_for :accounts
end
and my Account Model:
class Account < ActiveRecord::Base
belongs_to :user
end
My User Controller:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
sign_in #user
##flash[:success] = "Welcome to the HighTide!"
redirect_to #user
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation, accounts_attributes: [:id, :title])
end
end
and finally the Account Controller:
class AccountsController < ApplicationController
def new
#account = #user.accounts.new
end
def create
#account = current_user.accounts.new(account_params)
if #account.save
redirect_to '/'
end
end
private
def account_params
params.require(:account).permit(:title, :user_id, products_attributes: [:id, :title, :units], bookings_attributes: [:id, :name, :check_in, :check_out])
end
end
EDIT:
routes.rb file
Hightide::Application.routes.draw do
devise_for :users, path_names: {sign_in: "login", sign_out: "logout"}
resources :users do
resources :accounts
end
resources :sessions
match '/users/:user/edit', to: 'users#edit', via: [:post, :get]
devise_scope :user do
root to: 'static_pages#home'
match '/sessions/user', to: 'devise/sessions#create', via: :post
end
Your form_for can only send its data to a single controller method, and in this case it is sending the params from your form to the Devise registrations controller. Those params include the values for the new account, but they never reach your users#create or accounts#create methods.
You'll probably have to create a custom Devise controller. Take a look at this SO question and answer, which lays it out nicely. Nested registration data in Rails 3.1 with Devise.

Devise Authentication with Accounts and Users

I am working on implementing a top-level account that has many users.
I am working off the solution for this question: Use both Account and User tables with Devise
What works:
After user submits registration form (below) the user and account is created as desired.
What doesn't work:
The user is not being authenticated before the redirect_to accounts_path. In order to authenticate, the user must click login and enter the credentials they just signed up with. Instead I need the user to be authenticated before the redirect.
I've been working on this for a few hours, tried a few approaches, but nothing seems to work.
Could someone help me with the code to authenticate the user after the user/account is successfully created. THANKS!
Models
class Account < ActiveRecord::Base
has_many :users, :inverse_of => :account, :dependent => :destroy
accepts_nested_attributes_for :users
attr_accessible :name, :users_attributes
end
class User < ActiveRecord::Base
belongs_to :account, :inverse_of => :users
validates :account, :presence => true
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:confirmable, :lockable, :timeoutable
attr_accessible :email, :password, :password_confirmation, :remember_me
end
Routes
resources :accounts, :only => [:index, :new, :create, :destroy]
controllers/accounts_controller.rb
Controllers
class AccountsController < ApplicationController
def new
#account = Account.new
#account.users.build # build a blank user or the child form won't display
end
def create
#account = Account.new(params[:account])
if #account.save
flash[:success] = "Account created"
redirect_to accounts_path
else
render 'new'
end
end
end
views/accounts/new.html.erb view
<h2>Create Account</h2>
<%= form_for(#account) do |f| %>
<%= render 'shared/error_messages', :object => f.object %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<%= f.fields_for :users do |user_form| %>
<div class="field"><%= user_form.label :email %><br />
<%= user_form.email_field :email %></div>
<div class="field"><%= user_form.label :password %><br />
<%= user_form.password_field :password %></div>
<div class="field"><%= user_form.label :password_confirmation %><br />
<%= user_form.password_field :password_confirmation %></div>
<% end %>
<div class="actions">
<%= f.submit "Create account" %>
</div>
<% end %>
You need to manually sign the user in. In the accounts controller you need a sign_in(user), where user is the actual User model record that you want to sign in.
The problem with that is that you have a one account to many users relationship. Therefore, you'll need to get access to the single user somehow, e.g:
def create
#account = Account.new(params[:account])
if #account.save
sign_in(#account.users.first)
flash[:success] = "Account created"
redirect_to accounts_path
else
render 'new'
end
end
You need to add
before_filter :authenticate_user!, :except => [:new,:create]
to accounts controller in order to provide authentication to rest of the actions

How to add additional file fields to ruby on rails project?

Yesterday I have made paperclip multiple upload gallery. Today I want to customize this all and I need to make a button with on click add one more file upload field. Example you can see in this video: http://www.emersonlackey.com/article/rails-paperclip-multiple-file-uploads 28:27.
I have searched in google, but couldn't find anything.
#post form:
<% form_for #post, :html => {:multipart => true} do |t| %>
<p>
<%= t.label :title, 'Virsraksts:' %></br>
<%= t.text_field :title %></br>
</p>
<p>
<%= t.label :content, 'Teksts:' %>
<%= t.text_area :content, :class => "mceEditor"%>
</p>
<p>Pievienot jaunas bildes:</p>
<%= f.link_to_add "Add a task", :assets %>
<%= f.fields_for :assets do |asset_fields| %>
<% if asset_fields.object.new_record? %>
<%= asset_fields.file_field :asset %>
<%= asset_fields.link_to_remove "Noņemt" %>
<% end %>
<% end %>
<p>
<%= f.fields_for :assets do |asset_fields| %>
<% unless asset_fields.object.new_record? %>
<p>
<%= link_to image_tag(asset_fields.object.asset.url(:thumb)), asset_fields.object.asset.url(:original) %>
<%= asset_fields.check_box :_destroy %>
</p>
<% end %>
<% end %>
</p>
<%= t.submit %>
#post model:
class Post < ActiveRecord::Base
attr_accessible :title, :content, :assets_attributes
has_many :assets
accepts_nested_attributes_for :assets, :allow_destroy => true
end
#asset model:
class Asset < ActiveRecord::Base
belongs_to :post
has_attached_file :asset, :styles => { :large => "640x480", :medium => "300x300>", :thumb => "100x100>" },
:url => "/assets/albums/:id/:style/:basename.:extension",
:path => ":rails_root/public/assets/albums/:id/:style/:basename.:extension"
accepts_nested_attributes_for :post, :allow_destroy => true
end
There's a great cast from R.Bates about nested form here. i've modified it so that it's now possible to add, remove photos, works with paperclip, Feel free to clone this rails3.2 app https://github.com/Saidbek/multiple-image-uploader
You are going to need the javascript to make the functions to work.
If you are a railscasts premium user you can access the link above, as Said recommended, but if you not, you should take a look at this link.

Resources