Rails 3 - saving model which belongs_to few models via nested attributes - ruby-on-rails

I have few models - User, Teacher and TeacherLeader.
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :user_login,
:password,
:teacher_attributes,
:teacher_leader_attributes
has_one :teacher
has_one :teacher_leader
accepts_nested_attributes_for :teacher_leader
end
class Teacher < ActiveRecord::Base
belongs_to :user
has_one :teacher_leader
end
class TeacherLeader < ActiveRecord::Base
belongs_to :user
belongs_to :teacher
end
I want to save data in TeacherLeader via nested attributes. So, in User model i added accepts_nested_attributes_for :teacher_leader and attr_accessible :teacher_leader_attributes.
Also i have controller:
class TeacherLeadersController < ApplicationController
def new
...
#user = User.new
#teacher_leader = #user.build_teacher_leader
#teachers_collection = Teacher.all
.collect do |t|
[ "#{t.teacher_last_name} #{t.teacher_first_name} #{t.teacher_middle_name}", t.id ]
end
#choosen_teacher = #teachers_collection.first.last unless #teachers_collection.empty?
end
def create
user = User.new( params )
user.user_role = "class_head"
if user.save
flash[:success] = "Successfully created class head!"
else
flash[:error] = user.errors.full_messages
end
end
end
Also i i have view for TeacherLeader controller (new.html.erb):
<%= form_for #user, :url => teacher_leaders_url, :html => {:class => "form-horizontal"} do |f| %>
<%= field_set_tag do %>
<%= f.fields_for :teacher_leader do |tl| %>
<div class="control-group">
<%= tl.label :teacher_id, "Teacher names", :class => "control-label" %>
<div class="controls">
<%= select_tag( :teacher_id,
options_for_select( #teachers_collection, #choosen_teacher )) %>
</div>
</div>
<% end %>
<div class="control-group">
<%= f.label :user_login, "Login", :class => "control-label" %>
<div class="controls">
<%= f.text_field :user_login %>
</div>
</div>
<div class="control-group">
<%= f.label :password, "Pass", :class => "control-label" %>
<div class="controls">
<%= f.text_field :password %>
</div>
</div>
<% end %>
<%= f.submit "Create", :class => "btn btn-large btn-success" %>
When i'm trying to save my models, i get strange errors such "User login can't be empty" and others. I know that validations of User models generate them (i don't know why, i get such errors even if have values in params). I suppose, that i do something wrong in view, because after submiting i have such params:
teacher_id: '1'
user: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
user_login: schoolh_1rF32
password: txaqxuTXz96auhX
commit: Create
action: create
controller: teacher_leaders
But i should have something like this:
user: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
user_login: schoolh_1rF32
password: txaqxuTXz96auhX
teacher_leader_attributes:
teacher_id: '1'
What is wrong? Can i fix that?
UPD: HTML code for class head creation page:
<form accept-charset="UTF-8" action="http://0.0.0.0:3000/teacher_leaders" class="form-horizontal" id="new_user" method="post"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="✓" /><input name="authenticity_token" type="hidden" value="ZKlDTKG8SU8fZuMrUfQoCOSGknOhj651DT2LJDrfliA=" /></div>
<fieldset>
<div class="control-group">
<label class="control-label" for="user_teacher_leader_attributes_teacher_id">Teacher names</label>
<div class="controls">
<select id="teacher_id" name="teacher_id">
<option value="1" selected="selected">Jack P. Tompson </option>
<option value="2">Ronald V. Herwud</option>
</div>
</div>
<div class="control-group">
<label class="control-label" for="user_user_login">Login</label>
<div class="controls">
<input id="user_user_login" name="user[user_login]" size="30" type="text" />
</div>
</div>
<div class="control-group">
<label class="control-label" for="user_password">Password</label>
<div class="controls">
<input id="user_password" name="user[password]" size="30" type="text" />
</div>
</div>
</fieldset>
<input class="btn btn-large btn-success" name="commit" type="submit" value="Create" />
</form>

You need not
<%= select_tag( :teacher_id,
options_for_select( #teachers_collection, #choosen_teacher )) %>
but something like
<%= tl.select( :teacher_id,
options_for_select( #teachers_collection, #choosen_teacher )) %>

Related

I can't save record in sqlite Rails

I try save record on database, in pivot table but I have a error(screen) if I write #video.category_ids = 1 it works but category id = 1 in database
Below I put my code if you neeed more information please write.
videos_controller.rb
class VideosController < ApplicationController
layout 'master'
before_action :chceck_login
def index
#videos = Video.sortASC
end
def new
#categories = Category.all
end
def create
#video = Video.new(video_params)
#video.category_ids = [video_params]
if #video.save
flash[:notice] = 'The movie has been added successfuly'
redirect_to(:action => 'index')
else
#categories = Category.all
render('new')
end
end
def show
end
def edit
end
def delete
end
private
def video_params
params.require(:video).permit(:title, :url, :description, category_attributes: [:id]).merge(user_id: session[:user_id])
end
end
_form_html.erb
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon1"><i class="fas fa-tag"></i></span>
</div>
<%= f.text_field :title, placeholder: 'Title', :class => 'form-control', :id => 'title' %>
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon1"><i class="fas fa-link"></i></span>
</div>
<%= f.text_field :url, placeholder: 'URL', :class => 'form-control', :id => 'url' %>
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon1"><i class="fas fa-edit"></i></span>
</div>
<%= f.text_area :description, placeholder: 'Description', rows: 5, :class => 'form-control', :id => 'description' %>
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon1"><i class="fas fa-sitemap"></i></span>
</div>
<%= f.select(:category_id, #categories.map{|t| [t.name, t.id]}, {}, :multiple => true, :class => 'custom-select') %>
</div>
video.rb
class Video < ApplicationRecord
belongs_to :user
has_and_belongs_to_many :categories
scope :sortASC, lambda{order("videos.created_at ASC")}
end
new.html.erb
<% #page_title = 'Add Film' %>
<div class='container'>
<div class='row'>
<div class='col s-6'>
<div class="jumbotron jumbotron-fluid bg-info">
<div class="container">
<h1 class="display-4 text-center">ADD NEW FILM <i class="fas fa-plus"></i></h1>
<p class="lead">This is a modified jumbotron that occupies the entire horizontal space of its parent.</p>
</div>
</div>
</div>
<div class='col s-6'>
<%= form_for(:video, :url => {:action => 'create'}) do |f| %>
<%= render(:partial => 'form', :locals => {:f => f}) %>
<%= submit_tag('Add Film', :class => 'btn btn-primary float-right') %>
<% end %>
</div>
</div>
</div>
Error
https://imgur.com/7P4WpMU
If you want I have put my project on Github.
I would suggest the following changes:
In _form.html.erb pluralize the name of the select, i.e. change
<%= f.select(:category_id, #categories.map{|t| [t.name, t.id]}, {}, :multiple => true, :class => 'custom-select') %>
to
<%= f.select(:category_ids, #categories.map{|t| [t.name, t.id]}, {}, :multiple => true, :class => 'custom-select') %>
In videos_controller.rb change
def video_params
params.require(:video).permit(:title, :url, :description, category_attributes: [:id]).merge(user_id: session[:user_id])
end
to
def video_params
params[:video][:category_ids].reject!(&:blank?) unless params[:video][:category_ids].nil?
params.require(:video).permit(:title, :url, :description, category_ids: []).merge(user_id: session[:user_id])
end
Also in videos_controller.rb remove
#video.category_ids = [video_params]
to leave things to the constructor
Regarding 2. I am not 100% sure about the exact solution, maybe you should also reject the empty id you have in your parameters (according to the provided screenshot, use .reject!(&:blank?) for that) - for analyzing further problems check the output of the video_params function before passing it to the constructor - it basically should look like a hash representation of the expected object.
edit Updated the change proposal for 2. regarding category_ids

Rails - Rendering form_for #user in a different view

I'm trying to render this code (partial to update users payment info).
/devise/registrations/_credit.html.erb
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
<form action="#">
<div class="form-group">
<label class="control-label">Card Holder Name</label>
<%= f.text_field :name, autofocus: true, class:"form-control" %></div>
<div class="form-group">
<label class="control-label">Card Number</label>
<%= f.text_field :card, autofocus: false, class:"form-control" %>
</div>
<div class="form-group">
<label class="control-label">CVV</label>
<%= f.text_field :cvv, autofocus: false, class:"form-control" %>
</div>
<div class="form-group">
<label class="control-label">Expiration Month</label>
<%= f.text_field :expmonth, autofocus: false, class:"form-control" %>
</div>
<div class="form-group">
<label class="control-label">Expiration Year</label>
<%= f.text_field :expyear, autofocus: false, class:"form-control" %>
</div>
<div class="margin-top-10">
<% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
<div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
<% end %>
<div class="actions">
<%= f.submit "Update", class:"btn green" %>
<% end %>
</div>
</form>
</div>
I'm trying to get this partial to show up in a different page that's not under users_controller.rb, but I keep getting an error.
NameError in Orders#new
Showing /home/nitrous/code/uvesty/app/views/devise/registrations/_credit.html.erb where line #2 raised:
undefined local variable or method `resource' for #<#<Class:0x00563138834928>:0x00563138821670>
In app/models/order.rb:
class Order < ActiveRecord::Base
belongs_to :user
# only use this if user instance is created before order,
# and alway associated with order
validates :user, presence: true
end
In app/models/user.rb:
class User < ActiveRecord::Base
has_many :orders
# --OR--
# has_one :order
end
In app/controllers/orders_controller.rb:
class OrdersController < ApplicationController
# 1)assuming `current_user` is populated
#
def new
#order = Order.new
#user = current_user
end
# --OR--
# 2) assuming the following line is in `config/routes.rb`:
# get '/orders/new/:id' => 'orders#new'
#
def new
if #order = Order.find_by_id(new_params[:id])
#user = #order.user
else
# do whatever here; replace the next line
raise "order not found"
end
end
private
# only needed for 2) above
def new_params
params.permit(:id)
end
end
see: http://guides.rubyonrails.org/action_controller_overview.html#strong-parameters
In app/view/orders/new.html.erb:
<% raise('#user not defined') unless #user %><%# <- Remove this line after partial is working %>
<%= render partial: "devise/registrations/credit", locals: {resource: #user, resource_name: :user} %>
see: http://guides.rubyonrails.org/layouts_and_rendering.html#passing-local-variables

Can't save nested model on polymorphic association

Forced to ask for help with saving nested models on polymorphic association. I'm missing something but can't figure out what.
Everithing pretty straightforward. There is Address which can have multiple Phones.
So models are
class Address < ActiveRecord::Base
has_many :phones, as: :phoneable
accepts_nested_attributes_for :phones, allow_destroy: true
validates :city, :street, :building, :name, presence: true
end
and
class Phone < ActiveRecord::Base
belongs_to :phoneable, polymorphic: true
validates :number, :extension, presence: true
end
addresses_controller.rb
def new
#address = Address.new
#phone = #address.phones.build
authorize #address
end
def create
#address = Address.create(address_params)
authorize #address
if #address.save
binding.pry
flash[:success] = "Address #{#address.name} created"
redirect_to address_path(#address)
else
flash.now[:danger] = 'Failed'
render :new
end
end
def address_params
params.require(:address).permit(:name, :street, :building, :city, phones_attributes: [:id, :number, :extension, :details] )
end
/app/views/address.html.erb
<div class="row">
<div class="col-md-12">
<%= form_for(#address, html: {class: 'form-horizontal', role: 'form'}) do |f| %>
<%= render 'shared/errors', obj: #address, model_name: 'addresses' %>
<div id="create-form">
<div class="form-group">
<div class="control-label col-md-4">
<%= f.label :city, 'Город' %>
</div>
<div class="col-md-4">
<%= f.select(:city, options_for_select(['Moscow', 'Samara']), {}, {class: "form-control"}) %>
</div>
</div>
<div class="form-group">
<div class="control-label col-md-4">
<%= f.label :street, 'Street' %>
</div>
<div class="col-md-4">
<%= f.text_field :street, class: 'form-control' %>
</div>
</div>
<div class="form-group">
<div class="control-label col-md-4">
<%= f.label :building, 'Building' %>
</div>
<div class="col-md-4">
<%= f.text_field :building, class: 'form-control' %>
</div>
</div>
<div class="form-group">
<div class="control-label col-md-4">
<%= f.label :name, 'Place name' %>
</div>
<div class="col-md-4">
<%= f.text_field :name, class: 'form-control' %>
</div>
</div>
<div class="form-group">
<div class="control-label col-md-4">
<%= f.label :phones, 'Phone' %>
</div>
<div class="col-md-4">
<%= f.fields_for :phone do |phone_form| %>
<%= phone_form.text_field :number, class: 'form-control' %>
</div>
</div>
<div class="form-group">
<div class="control-label col-md-4">
<%= phone_form.label :extension, 'Ext.' %>
</div>
<div class="col-md-4">
<%= phone_form.text_field :extension, class: 'form-control' %>
</div>
<% end %>
</div>
<div class="form-group">
<div class='col-md-offset-4 col-md-6'>
<%= f.submit #address.new_record? ? 'Add' : 'Update', class: 'btn btn-primary btn-md' %>
</div>
</div>
</div>
<% end %>
</div>
</div>
First issue I encountered with is if I set key :phones instead :phone into the following line <%= f.fields_for :phone do |phone_form| %> my phone text fields don't render in view but they should. One user emphasized this moment here https://stackoverflow.com/a/3328041/2049476
if I use :phone everything somehow works fine but seems like it's wrong.
And the second one.
Phone object doesn't save in DB, when I create new address or edit current I succeed but phone doesn't show any validation errors if I leave all it fields blank.
Here what I have in params hash
{"utf8"=>"✓",
"authenticity_token"=>"inwXr3Ev/Aj/hZRY2IadizDHDgdSFo2zFhY9DAvysfFu3jjD9AS66esKVsTzEuKo2WC46YQt6HnOKTgInvfUEg==",
"address"=>{"city"=>"Moscow", "street"=>"ul. Tsentralnaya d. 4 kv. 220", "building"=>"1212", "name"=>"Astoria", "phone"=>{"number"=>"9215555555", "extension"=>"111"}},
"commit"=>"Add",
"controller"=>"addresses",
"action"=>"create"}
What am I missing?
Try answer for 2 issues:
The correct way is to pass :phones, and then phone as variable to field_for, like is was done here:
<%- #address.phones.each.with_index do |phone, index| %>
<%- f.fields_for :phones, phone do |phone_form| %>
<%- end %>
<%- end %>
Should be resolved as of the 1-st question, since the fields shoudl sent ot server via params not a phone hash, but phones_attributes array of hashes, in order to accepts_nested_attributes_for could accept phones:
phones_attributes: [{ ... },{ ... }]

Passing two parameters to input type file upload

In my scenerio, i'm using CarrierWave to upload images. But my "Image" model is polymorphic, because more than one model use "Image" model. And for each model, there are 4 different image types (Icon, Title, Regular, Slider). So i will make "ENUM" for them in "Image" model. But the problem is that, in "new" or "edit" views, how will i know, which file upload for which "Image Type"?
So here is my Image Model:
class Image < ActiveRecord::Base
belongs_to :imageable, polymorphic: true
mount_uploader :image, ImageUploader
enum image_type: {icon: 1, title: 2, regular: 3, slider: 4}
end
Here is the Service Model (This model is one of the models which will have images):
class Service < ActiveRecord::Base
has_many :images, as: :imageable, :dependent => :destroy
accepts_nested_attributes_for :images
end
And here is my _form.html.erb:
<div class="control-group">
<!-- <label class="control-label" for="input1">Adı</label> -->
<div class="controls">
<%= f.input :service_name, input_html: { class: "span6", id: "input1"}, label: "Hizmet Adı" %>
</div>
</div>
<div class="control-group">
<!-- <label class="control-label" for="inputRemarks">Adres</label> -->
<div class="controls">
<%= f.input :service_description, input_html: { class: "span12 wysihtml5", rows: "6"}, as: :text, label: "Hizmet Açıklaması" %>
</div>
</div>
<div class="control-group">
<----- THIS PART IS FOR FILE UPLOAD ----------->
<div class="controls">
<label class="control-label">Hizmet Büyük Resimi</label>
<div class="fileupload fileupload-new" data-provides="fileupload">
<div class="fileupload-new thumbnail" style="width: 200px; height: 150px;"><img src="http://www.placehold.it/200x150/EFEFEF/AAAAAA&text=no+image" alt=""/></div>
<div class="fileupload-preview fileupload-exists thumbnail" style="max-width: 200px; max-height: 150px; line-height: 20px;"></div>
<div>
<span class="btn btn-file"><span class="fileupload-new">Resim Seç</span><span class="fileupload-exists">Değiştir</span>
<%= f.input :images, input_html: { type: :file, class: "default"}, wrapper: false, label: false %></span>
Sil
</div>
</div>
<----- THIS PART IS FOR FILE UPLOAD ----------->
</div>
</div>
<div class="control-group">
<!-- <label class="control-label" for="input2">Soyadı</label> -->
<div class="controls">
<%= f.input :meta_keywords, input_html: { class: "span6", id: "input2"}, label: "Anahtar Kelimeler" %>
</div>
</div>
<div class="form-actions">
<%= button_tag(type: 'submit', class: "btn btn-primary") do %>
Kaydet
<% end %>
<!-- <button type="button" class="btn">Cancel</button> -->
</div>
I'm new to rails, so is it difficult or not, i don't know yet. But this type of solution looks good to me. But i'm stuck in this question and i need your help.
I've found the solution, as i said i'm so new to Rails and there is a "fields_for" form helper in Rails. So for nested attributes, you can use "fields_for" form helpers to do it. In my scenerio, i added hidden fields for that.

How to implement upload photo feature in rails?

My repo: https://github.com/Gtar69/artstore_hw2
I want to implement upload more than 1 photo in certain product.
The basic idea is that administrator enter edit page of the product and can "upload new photos" for certain product.Im struggling in "how to create a photo object in edit page and add this photo object to product. !!! I can not successfully create a photo object in edit page .....
Very Thanks
in view/admin/products/edit.html.erb
<h2> 編輯 <%= #product.title %> </h2>
<%= simple_form_for [:admin, #product ] , :html => { :class => "form form-horizontal" } do |f| %>
<div class="form-group">
<div class="col-sm-10">
<%= f.input :title %>
</div>
</div>
<div class="form-group">
<div class="col-sm-10">
<%= f.input :description, :as => :text %>
</div>
</div>
<div class="form-group">
<div class="col-sm-1">
<%= f.input :quantity %>
</div>
</div>
<div class="form-group">
<div class="col-sm-1">
<%= f.input :price %>
</div>
</div>
<!-- 20140623 upload more than one photo feature added -->
<div>
<%= simple_form_for [:admin, #product, #product.photos.build] do |f| %>
<%= f.input :image , :as => :file %>
<div class="form-group">
<%= f.submit "upload new photos", :class => "btn btn-default" , :disable_with => 'Submiting...' %>
</div>
<%end%>
</div>
<div class="form-group">
<%= f.submit "Submit", :class => "btn btn-default" , :disable_with => 'Submiting...' %>
</div>
<% end %>
in controllers/admin/products_controller.rb
def edit
#product = Product.find(params[:id])
##photo = #product.photos.build
##product.add_photo_to_product(#photo)
end
in controllers/admin/photos_controller.rb
def create
#binding.pry
#product = Product.find(params[:product_id])
#photo = #product.photos.create(photo_params)
#product.add_photo_to_product(#photo)
redirect_to admin_products_path
end
private
def photo_params
params.require(:photo).permit(:image)
end
in model/product.rb
class Product < ActiveRecord::Base
has_many :photos
accepts_nested_attributes_for :photos
validates :title , :presence => true
validates :quantity , :presence => true
def default_photo
photos.first
end
def add_photo_to_product(photo)
photos << photo
end
end
in photos.rb
class Photo < ActiveRecord::Base
belongs_to :product
mount_uploader :image, ImageUploader
end

Resources