Nested form renders unknown attribute error - ruby-on-rails

I am using gem nested form https://github.com/ryanb/nested_form in order to create a Flat with multiple pictures.
The Flat model has many pictures.
the form looks like that :
<%= simple_nested_form_for #flat do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<%= f.input :name %>
<%= f.fields_for :pictures do |pictures_form| %>
<%= pictures_form.file_field :image %>
<%= pictures_form.link_to_remove ('<i class="fa fa-trash"></i>').html_safe %>
<% end %>
<%= f.link_to_add ('<i class="fa fa-plus"></i>').html_safe, :pictures %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
My controller create action :
def create
#flat = Flat.new(flat_params)
authorize #flat
#flat.user = current_user
#flat.pictures.build
if #flat.save
redirect_to flat_path(#flat), notice: 'Flat was successfully created.'
else
render :new
end
end
and my flat_params :
def flat_params
params.require(:flat).permit(:name, pictures_attributes: [:id, :image, :_destroy])
end
I always get the following error :
unknown attribute 'image' for Picture.
I used the gem paperclip for image upload Here's how my table picture looks in my schema :
create_table "pictures", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "image_file_name"
t.string "image_content_type"
t.integer "image_file_size"
t.datetime "image_updated_at"
t.integer "flat_id"
end
What is the problem ?

Why are you building in the create method? This is where you save only:
def new
#flat = Flat.new
#flat.pictures.build #-> pictures.build should be in the new method only
end
def create
#flat = Flat.new flat_params
#flat.user = current_user
authorize #flat
if #flat.save
redirect_to #flat, notice: 'Flat was successfully created.'
else
render :new
end
end
Other than the above (pictures.build), your code looks good.
The other issue you may have is that you've not included the Paperclip reference in your Picture model. You'll need to have the following:
#app/models/picture.rb
class Picture < ActiveRecord::Base
has_attached_file :image #-> + any styling options etc
end
From the code you've provided, that's what I can give.

Related

Undefined method `street' for #<Profile:0x00007fea78589ac0>

I have a User who has a Profile (2 models). Here is the relevant part of my schema:
create_table "profiles", force: :cascade do |t|
t.text "about"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "provider"
t.string "uid"
t.string "first_name"
t.string "last_name"
t.string "street"
t.integer "house_number"
t.string "city"
t.integer "zip_code"
t.string "image"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
The reason I have a Profile as a separate model, as because I thought it was easier to assign roles later, for certain manipulations. So, now I am wondering, if it is possible to ask for
user.first_name , user.last_name, user.email and user.password
in the registration form and for
user.street, user.house_number, user.city and user.zip_code
in the Profile#new _form. Like this:
<%= form_for([#user, #profile], url: user_profiles_path, method: :post) do |form| %>
<div class="field">
<%= form.label :about %>
<%= form.text_area :about %>
</div>
<div class="field">
<%= form.file_field :avatar %>
<% form.label "Profile photo" %>
</div>
<div class="field">
<%= form.label :street %><br />
<%= form.text_field :street, class: 'form-control' %>
</div>
<div class="actions">
<%= form.submit %>
</div>
<% end %>
So here you can see, that avatar and about refer to a Profile, while street if from User table. But somehow this form, doesn't undertand this. I allow nested_attributes for :profile, but I guess, this doesn't matter for this form. I know, that maybe the easier way would be, to rearrange my table, so that all the adress attributes are stored in Profile. But as I am new to Rails and I really wish to learn more, I would love to know, if there is a way of saving to both #user and #profile in one form? Thank you!
You're touching on two somewhat different concepts here that most beginners get stumped on.
The first is nested resources. A nested resource has its path nested under another resource.
# config/routes.rb
resources :magazines do
resources :ads
end
So now instead of /ads we have /magazines/:magazine_id/ads. So the routes themselves describe the relation between the two resources in a RESTful way - awesome.
class AdsController < ApplicationController
before_action :set_magazine
# GET /magazines/:magazine_id/ads/new
def new
#ad = #magazine.ads.new
end
# POST /magazines/:magazine_id/ads/new
def create
#ad = #magazine.ads.new(ad_params)
if #ad.save
redirect_to #ad
else
render :new
end
end
def set_magazine
#magazine = Magazine.find(params[:magazine_id])
end
# ...
end
<%= form_for([#ad, #magazine]) do |f| >
# ...
<% end %>
This will let you create ads that belong to a magazine. It will not magically let you create a magazine at the same time as an add in the same form.
That's where nested attributes comes in. It creates a super-powered setter in the model which lets it accept attributes for an associated model and creates / updates the associated records in the same request as the parent.
This for example would let us create a user and a profile in the same form:
class User < ApplicationRecord
has_one :profile
accepts_nested_attributes_for :profile
end
class Profile < ApplicationRecord
belongs_to :user
end
<%= form_for(#user) do |f|>
<div class="field">
<%= f.label :email %>
<%= f.email_field :street, class: 'form-control' %>
</div>
# ...
<%= f.fields_for(:profile) do |profile_fields| %>
<div class="field">
<%= profile_fields.label :about %>
<%= profile_fields.text_area :about %>
</div>
<% end %>
# ...
<% end %>
class UsersController < ApplicationRecord
POST /users
def create
#user = User.new(user_params)
if #user.save
redirect_to :user
else
render :new
end
end
# ...
private
def user_params
params.require(:user)
.permit(:email, ..., profile_attributes: [:about])
end
end
accepts_nested_attributes_for is one of the most misused, misunderstood and hardest concepts to grasp in rails though. If you're just starting out you should consider bypassing this and circling back around once you have a better understanding of rails.

Cant add date in rails form

I want to add the date in rails form. For example
def create
#land=current_user.lands.build(land_params)
if #land.save
session[:land_id]=#land.id
flash[:success]="Success"
redirect_to lands_path
else
flash[:error]="Fail!!"
render 'new'
end
end
schema.rb
create_table "lands", force: :cascade do |t|
t.date "DateStart"
t.date "DateEnd"
end
new.html.erb
<%= form_for #land do |f| %>
<p>Information</p>
<%= f.label :Start %><br />
<%= f.date_select :DateStart, :default => #DateStart, :order => [:month, :day, :year] %>
<%= f.label :End %><br />
<%= f.date_select :DateEnd,:default => #DateEnd,:order => [:month, :day, :year]%>
<% end %>
show.html.erb
<p>Start : <%= #land.DateEnd %></p>
<p>End : <%= #land.DateEnd %></p>
Land.controller
def show
#land= Land.find(params[:id])
end
But nothing is printed out in my show.html.erb. When I check database my DateStart and DateEnd is nil. I don't know what wrong. Can you give me some advice? Thanks
You can follow like below
def create
#land= Land.new(land_params)
#land.user = current_user
if #land.save
flash[:success] = 'Land was successfully created.'
redirect_to lands_path #=> or anything
else
flash[:error] = 'Land was not created successfully .'
redirect_to lands_path #=> or anything
end
end
I think that's work
You make sure add this before_action :authenticate_user! on your controller header, that's cannot access user without authentication
Hope to help
You can follow my answers -
Method - 1. No association with User and Land model.
lands_controller.rb file look like -
class LandsController < ApplicationController
def new
#DateStart = Date.today
#DateEnd = Date.today + 2.days
#land = Land.new
end
def create
#land = Land.new(land_params)
if #land.save
session[:land_id] = #land.id
flash[:success]= "Success"
redirect_to lands_path
else
flash[:error] = "Fail!!"
render 'new'
end
end
def show
#land= Land.find(params[:id])
end
def land_params
params.require(:land).permit(:DateStart, :DateEnd)
end
end
Database Schema file look like - (schema.rb)
create_table "lands", force: :cascade do |t|
t.date "DateStart"
t.date "DateEnd"
end
Lands controller new action view file - (app/views/lands/new.html.erb)
<%= form_for #land do |f| %>
<p>Information</p>
<%= f.label :Start %><br />
<%= f.date_select :DateStart, :default => #DateStart, :order => [:month, :day, :year] %>
<%= f.label :End %><br />
<%= f.date_select :DateEnd,:default => #DateEnd,:order => [:month, :day, :year]%>
<%= f.submit "Submit" %>
<% end %>
Lands view page (app/views/lands/show.html.erb)
<p>Start : <%= #land.DateEnd %></p>
<p>End : <%= #land.DateEnd %></p>
Method - 2. Association with User and Land model.
lands_controller.rb file look like - (app/controllers/lands_controller.rb)
class LandsController < ApplicationController
before_action :authenticate_user!
def new
#DateStart = Date.today
#DateEnd = Date.today + 2.days
#land = current_user.lands.build
end
def create
#land = current_user.lands.build(land_params)
if #land.save
session[:land_id] = #land.id
flash[:success]= "Success"
redirect_to lands_path
else
flash[:error] = "Fail!!"
render 'new'
end
end
def show
#land= Land.find(params[:id])
end
def land_params
params.require(:land).permit(:DateStart, :DateEnd)
end
end
Database Schema file look like - (schema.rb)
create_table "lands", force: :cascade do |t|
t.date "DateStart"
t.date "DateEnd"
t.index ["user_id"], name: "index_rooms_on_user_id"
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
User model look like - (app/models/user.rb)
class User < ApplicationRecord
has_many :lands
end
Land model look like - (app/models/land.rb)
class Land < ApplicationRecord
belongs_to :user
end
Lands controller new action view file - (app/views/lands/new.html.erb)
<%= form_for #land do |f| %>
<p>Information</p>
<%= f.label :Start %><br />
<%= f.date_select :DateStart, :default => #DateStart, :order => [:month, :day, :year] %>
<%= f.label :End %><br />
<%= f.date_select :DateEnd,:default => #DateEnd,:order => [:month, :day, :year]%>
<%= f.submit "Submit" %>
<% end %>
Lands view page (app/views/lands/show.html.erb)
<p>Start : <%= #land.DateEnd %></p>
<p>End : <%= #land.DateEnd %></p>
I hope it should work.

Creating form for an object which has an association

I have two models: project and todo. Project has many todos.
So I wanna create a form, where I select project category from the combobox and then I add a todo to it.
For instance:
I have following categories: family, work, study.
In form in the combobox I select 'study', and then in textfield I spell a todo like 'make homework for monday' and press submit button.
project.rb
class Project < ActiveRecord::Base
has_many :todos
end
todo.rb
class Todo < ActiveRecord::Base
belongs_to :project
end
my data schema:
create_table "projects", force: :cascade do |t|
t.string "title"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "todos", force: :cascade do |t|
t.string "text"
t.boolean "isCompleted"
t.integer "project_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
_form.html.erb
<%= form_for #project do |f| %>
<div class="form_control">
<%= f.select :title, options_for_select([["Work", "w"],
["Family", "f"],
["Study", "f"],
["TheRest", "t"]]) %>
</div>
<div class="form_control">
*** HERE I NEED TO FIGURE OUT HOW TO ADD SOME DATA TO todo.text ***
</div>
<div class="form_control">
<%= f.submit 'Add' %>
</div>
<% end %>
this is how I show all the projects with their todos:
<% #projects.each do |project| %>
<h2> <%= project.title %> </h2>
<% project.todos.all.each do |todo| %>
<p><%= todo.text %> <%= check_box('tag', todo.__id__, {checked: todo.isCompleted}) %></p>
<% end %>
<% end %>
GitHub link : https://github.com/NanoBreaker/taskmanager
In your todo form, you could have a select box to choose the project the todo belongs to:
# todos/_todo_form.html.erb
<%= select_tag "project_id", options_for_select(Project.pluck(:title, :id)) %>
And in your todos_controller create action:
def create
#project = Project.find(params[:project_id])
#todo = #project.todos.new(todo_params)
if #todo.save
# success
else
# error
end
end
finally, permit the project_id in todo_params:
def todo_params
params.require(:todo).permit(:text, :project_id) # add any other attributes you want
end

How do I save my child records from the parent controller?

I have a bunch of 'kid' objects saved already and I want to create a parent object which is linked to the kids via a 'relative' model.
This object gives me a many-to-many, through relatives.
To be clear: the user visits the 'parents' page, clicks create parents and is presented with a form that lets them name the parent and add up to four children to this parent (by creating 'relatives'), each of these 'relations' is also named - that's an important part. So, I could name the relation 'step son' or 'son', for instance.
Here's the code I have so far:
class Kid < ActiveRecord::Base
has_many :relatives
has_many :parents, through: :relatives
end
class Parent < ActiveRecord::Base
has_many :relatives
has_many :kids, through: :relatives
accepts_nested_attributes_for :relatives,
:reject_if => lambda { |a| a[:content].blank? },
:allow_destroy => true
end
class Relative < ActiveRecord::Base
belongs_to :parent
belongs_to :kid
end
class ParentsController < ApplicationController
before_action :set_parent, only: [:show, :edit, :update, :destroy]
before_action :lookup_kids, only: [:new, :edit]
# GET /parents
# GET /parents.json
def index
#parents = Parent.all
end
# GET /parents/1
# GET /parents/1.json
def show
end
# GET /parents/new
def new
#parent = Parent.new
4.times { #parent.relatives.build }
end
# GET /parents/1/edit
def edit
end
# POST /parents
# POST /parents.json
def create
#parent = Parent.new(parent_params)
parent_params[:relatives_attributes].each do |k,r|
#parent.relatives.build(r.except(:_destroy))
end
respond_to do |format|
if #parent.save
format.html { redirect_to #parent, notice: 'Parent was successfully created.' }
format.json { render :show, status: :created, location: #parent }
else
format.html { render :new }
format.json { render json: #parent.errors, status: :unprocessable_entity }
end
end
end
# cut for brevity.
private
# Use callbacks to share common setup or constraints between actions.
def set_parent
#parent = Parent.find(params[:id])
end
def parent_params
params.require(:parent).permit(:name,
relatives_attributes: [:parent_id, :kid_id, :relationship, :_destroy])
end
def lookup_kids
#kids = Kid.all #for this nursery.
end
end
<%= form_for(#parent) do |f| %>
<% if #parent.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#parent.errors.count, "error") %> prohibited this parent from being saved:</h2>
<ul>
<% #parent.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<h4>Kids:</h4>
<%= f.fields_for :relatives do |r| %>
<%= r.label :kid %>
<%= r.collection_select :kid_id,
#kids, :id, :name, include_blank: true%>
<%= r.label :relationship %>
<%= r.text_field :relationship %>
<%= r.check_box :_destroy %>
<%= r.label :_destroy, "Remove" %>
<br/>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
ActiveRecord::Schema.define(version: 20151030113634) do
create_table "kids", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "parents", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "relatives", force: :cascade do |t|
t.string "relationship"
t.integer "parent_id"
t.integer "kid_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "relatives", ["kid_id"], name: "index_relatives_on_kid_id"
add_index "relatives", ["parent_id"], name: "index_relatives_on_parent_id"
end
When I get to 'create' in the parents controller, I can see the right parameters are getting through but the relationship records aren't being saved. SHouldn't this happen automatically?
I've tried looping through the :relatives_attributes but that doesn't seem to work with 'build'.
How am I suppsed to get the 'relatives' records to save?
EDIT: adding parameters posted:
parent"=>{
"name"=>"Dad",
"relatives_attributes"=>{
"0"=>{"kid_id"=>"2", "relationship"=>"Son", "_destroy"=>"0"},
"1"=>{"kid_id"=>"", "relationship"=>"", "_destroy"=>"0"},
"2"=>{"kid_id"=>"", "relationship"=>"", "_destroy"=>"0"},
"3"=>{"kid_id"=>"", "relationship"=>"", "_destroy"=>"0"}}}
Edit: I've updated this to show my latest edit - note the 'parent_params[:relatives_attributes].each do |k,r|' in the controller. This now saves the kid records but the only problem is, it also saves the fields that are blank! So I have 'relative' records with null values for kid records. How can I stop it saving empty fields (or creating empty relative records)?
The answer was to build each sub-record of relative, like so:
parent_params[:relatives_attributes].each do |k,r|
#parent.relatives.build(r.except(:_destroy))
end
Before calling #parent.save.
However, I'm still having issues getting rid of the blank records. So if anyone has an answer to that problem, please comment here - or if there's a better or more traditional way of doing this, hit me up. Follow up question here: Why is this reject_if in my model not rejecting blank records?
You are almost there, depending upon how your form submission is, you most likely need an accepts_nested_attribute_for in your Relative associative class as well:
class Relative
belongs_to :parent
accepts_nested_attributes_for :parent
belongs_to :kid
accepts_nested_attributes_for :kid
end
If this doesn't work, then please submit your params that are passed into the controller and we can adjust accordingly

Ruby on Rails Association with model

I am beginner Ruby on rails.Application have 4 model.State, Province, District and City.
Model
app/models/state.rb
Class State < ActiveRecord::Base
has_many :provinces
end
app/models/province.rb
Class Province < ActiveRecord::Base
belongs_to :state
has_many :districts
end
app/models/district.rb
Class District < ActiveRecord::Base
belongs_to :province
has_many :cities
end
app/models/city.rb
Class City < ActiveRecord::Base
belongs_to :district
end
Schema.rb
ActiveRecord::Schema.define(version: 20140714165543) do
create_table "states", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "provinces", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "state_id"
end
add_index "provinces", ["state_id"], name: "index_provinces_on_state_id"
create_table "districts", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "province_id"
end
add_index "districts", ["province_id"], name: "index_districts_on_province_id"
create_table "citys", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "district_id"
end
add_index "citys", ["district_id"], name: "index_citys_on_district_id"
end
I am using simple_form gem.I am Create CRUD for all models. My question is
When iam create some state. then i create provinces and assign to state error in browser . state_id is nil
class ProvincesController < ApplicationController
#GET /Provinces/new
def new
#province = Province.new
end
# GET /provinces/1/edit
def edit
end
# POST /provinces
# POST /provinces.json
def create
#province = Province.new(province_params)
respond_to do |format|
if #province.save
format.html { redirect_to #province, notice: 'Province was successfully created.' }
format.json { render :show, status: :created, location: #province }
else
format.html { render :new }
format.json { render json: #province.errors, status: :unprocessable_entity }
end
end
end
end
_form.html.erb in province
<%= simple_form_for(#province) do |f| %>
<% if #province.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#province.errors.count, "error") %> prohibited this province from being saved:</h2>
<ul>
<% #province.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<%= f.association :state %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
You are getting state_id is nil because you haven't have it in the list of permitted params.
Change your province_params method to
def province_params
params.require(:province).permit(:name,:state_id)
end
And with the error you are getting
NoMethodError in Province#show
would be most probably due to this line <%= #state.name %> because #state is not defined in your show method.
It should be <%= #province.state.name %> as you are trying to display the state name of the associated province.
Please run the following generator to add the state_id field to the proviences table.
rails g migration add_state_id_to_proviences state_id:integer
then run the migration command
rake db:migrate

Resources