I'm trying to make a create product page in rails. This includes adding multiple images and text fields. I have one model for products and one for photos. I'm using the paperclip gem for photo upload. But I get this error when I try to create a new product. P.S. I use HAML.
NoMethodError in Products#new
Showing /some_app/app/views/products/new.html.haml where line #33 raised:
undefined method `photo' for :product:Symbol
Extracted source (around line #33):
33: = f.file_field :product.photo, multiple: 'multiple'
products/new.html.haml
%h1
create item
= form_for #product, :html => { :multipart => true } do |f|
- if #product.errors.any?
.error_messages
%h2 Form is invalid
%ul
- for message in #product.errors.full_messages
%li
= message
%p
= f.label :name
= f.text_field :name
%p
= f.file_field :product.photo, multiple: 'multiple'
%p.button
Products controller
class ProductsController < ApplicationController
def new
#product = Product.new
#photo = Photo.new
end
def create
#photo = current_user.photos.build(params[:photo])
5.times { #product.photos.build }
#product = current_user.products.build(params[:product])
if #product.save
render "show", :notice => "Sale created!"
else
render "new", :notice => "Somehting went wrong!"
end
end
Product model
class Product < ActiveRecord::Base
attr_accessible :description, :name, :price, :condition, :ship_method, :ship_price, :quantity, :photo
has_attached_file :photo
belongs_to :user
validates :user_id, presence: true
validates :name, presence: true, length: { minimum: 5 }
end
Photo model
class Photo < ActiveRecord::Base
attr_accessible :product_id
belongs_to :product
has_attached_file :image,
:styles => {
:thumb=> "100x100#",
:small => "300x300>",
:large => "600x600>"
}
end
The syntax isn't correct - change:
= f.file_field :product.photo, multiple: 'multiple'
To:
= f.file_field :photo, multiple: 'multiple'
Related
I'm trying to implement a multiple select and save with simple form and nested forms.
My view shows all the branches with name and description in a card format (Bootstrap). Next to te name I would like to have a checkbox. I would like to save only the selected ones
= simple_form_for(#my_branch, html: { class: 'form-horizontal' }) do |f|
= f.error_notification
= f.input :user_id, input_html: { value: current_user.id}, as: :hidden
- #branches.each do |branch|
= f.simple_fields_for :my_branch_items do |b|
.col-md-4
.card{:style => "width: 18rem;"}
.card-header
= branch.name
= b.input :branch_id, input_html: { value: branch.id }, as: :radio_buttons
%ul.list-group.list-group-flush
= branch.description
.form-group
= f.submit 'Save', class: 'btn btn-primary'
Here are the associations
class Branch < ApplicationRecord
has_many :my_branch_items
end
class MyBranch < ApplicationRecord
belongs_to :user
has_many :my_branch_items
accepts_nested_attributes_for :my_branch_items, allow_destroy: true, reject_if: proc { |att| att['name'].blank? }
end
class MyBranchItem < ApplicationRecord
belongs_to :my_branch
belongs_to :branch
end
And the Controller
class MyBranchesController < BaseController
before_action :set_my_branch, only: [:show]
def new
#branches = Branch.all
#my_branch = MyBranch.new
end
def create
#my_branch = Quiz.new(my_branch_params)
if #my_branch.save
redirect_to my_branch_path(#my_branch), notice: 'Thanks for taking the Branch Quiz'
else
render :new
end
end
def show
end
private
def set_my_branch
#my_branch = MyBranch.find(params[:id])
end
def my_branch_params
params.require(:my_branch).permit(:name, :note, :user_id, my_branch_items_attributes: MyBranchItem.attribute_names.map(&:to_sym).push(:_destroy))
end
end
Fixed!
Form
= simple_form_for(#my_branch, html: { class: 'form-horizontal' }) do |f|
= f.error_notification
= f.input :user_id, input_html: { value: current_user.id}, as: :hidden
- #branches.each do |branch|
= f.simple_fields_for :my_branch_items do |b|
.col-md-4
.card{:style => "width: 18rem;"}
.card-header
= branch.name
= b.input_field :branch_id, checked_value: branch.id, as: :boolean, boolean_style: :inline, include_hidden: false
%ul.list-group.list-group-flush
= branch.description
.form-group
= f.submit 'Save', class: 'btn btn-primary'
Model was using a validation for blanks field that did not exist in the table.
class MyBranch < ApplicationRecord
belongs_to :user
has_many :my_branch_items, dependent: :destroy
accepts_nested_attributes_for :my_branch_items, allow_destroy: true, reject_if: proc { |att| att['branch_id'].blank? }
end
I'm really new to the ruby on rails framework and i try to build a recipe website to learn more about this framework.
I have a entity Recipe.rb which has many RecipeIngredient. A RecipeIngredient is a entity in relation with a Ingredient, a Unit and have a quantity.
Here are the files :
Recipe.rb
class Recipe < ActiveRecord::Base
extend Enumerize, FriendlyId
belongs_to :user
has_many :directions
has_many :recipe_ingredients
has_many :ingredients, through: :recipe_ingredients
accepts_nested_attributes_for :directions, reject_if: :all_blank, allow_destroy: true
accepts_nested_attributes_for :recipe_ingredients, reject_if: :all_blank, allow_destroy: true
friendly_id :title, use: :slugged
has_attached_file :image, styles: { thumb: "270x200#", main: "570x420#" }, default_url: "/images/:style/missing.png"
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
enumerize :difficulty, in: [:easy, :medium, :hard], default: :easy
end
RecipeIngredient.rb
class RecipeIngredient < ActiveRecord::Base
belongs_to :ingredient
belongs_to :unit
belongs_to :recipe
end
Ingredient.rb
class Ingredient < ActiveRecord::Base
belongs_to :recipe_ingredient
end
Unit.rb
class Unit < ActiveRecord::Base
belongs_to :recipe_ingredient
end
As you can see, I use the gem Cocoon to handle nested fields. So, when the user wants to create a recipe, he has to provide the ingredients with the recipe.
My _form looks like this : (I have removed all html tag (the indentation is not good here)
= simple_form_for #recipe, html: {multipart: true} do |f|
- if #recipe.errors.any?
.alert.alert-danger
- #recipe.errors.full_messages.each do |msg|
li= msg
= f.input_field :title, label: false, placeholder: 'Nom de la recette', required: true
= f.input_field :prepareTime, label: false, placeholder: 'Temps de préparation (en mn)', required: true
= f.input_field :cookingTime, label: false, placeholder: 'Temps de cuisson (en mn)', required: true
= f.input_field :difficulty, label: false, required: true, placeholder: 'Difficulté'
= f.input_field :serves, label: false, placeholder: 'Nombre de personnes', required: true
= f.input_field :description, label: false, placeholder: 'Description', required: true
= f.simple_fields_for :recipe_ingredients do |recipe_ingredient|
= render 'recipe_ingredient_field', f: task
= link_to_add_association 'Ajouter un ingrédient', f, :recipe_ingredients, class: 'add'
= f.simple_fields_for :directions do |direction|
= render 'direction_field', f: task
= link_to_add_association 'Ajouter une étape', f, :directions, class: 'add'
= f.input_field :image, as: :file, label: false
= f.button :submit, value: 'Ajouter la recette', class: 'button'
_recipe_ingredient_fields.html.slim
.f-row.ingredient
.nested_fields
.large
= f.input_field :ingredient, label: false, placeholder: "Nom de l'ingrédient"
.small
= f.input_field :quantity, label: false, placeholder: 'Quantité'
.third.mb
= f.association :unit, label: false
= link_to_remove_association '-', f, class: 'remove'
And last but not least, my recipe controller
class RecipesController < ApplicationController
before_action :authenticate_user!, only: [:new, :create]
before_action :find_recipe, only: [:show]
def index
#recipes = Recipe.all
end
def new
#recipe = current_user.recipes.build
end
def create
#recipe = current_user.recipes.build(recipe_params)
if #recipe.save
redirect_to #recipe, notice: 'La recette a été ajoutée'
else
render 'new'
end
end
def show
end
private
def recipe_params
params.require(:recipe).permit(:title, :prepareTime, :cookingTime, :difficulty, :serves, :description, :image, directions_attributes: [:id, :step, :done, :_destroy], recipe_ingredients_attributes: [:id, :ingredient, :unit, :quantity, :done, :_destroy])
end
def find_recipe
#recipe = Recipe.friendly.find(params[:id])
end
end
So now, when I tr to create a new recipe, I have an error "Ingredient excepted, String given".
I know it's because my new ingredient is not created in my create method but I really don't know how to do this. If someone can help me :)
If you want to save the ingredient in recipe_ingredients as a string then I think your issue is caused by a name clash between your model and the input field so rename your input field to something else and update your strong parameters.
However...If instead you intend to build a new ingredient record when you add a recipe_ingredient record then you need to have a fields_for clause in order to build the ingredient record and have it associated to your recipe_ingredient field. Also make sure than your recipe_ingredient includes accepts_nested_attributes_for :ingredients
I have two models #product and #photo with #photo nested in #product. I am only allowed to use one form to create both. I am using this JQuery plugin to handle photo upload being how it gives me a nice preview.
However the plugin has certain restrictions in my create action so I cannot use the product create action to handle both creating the photo and product.
Is it possible to have one nested for_for use two different controllers?
And how would i do it ?
my form (haml)
= form_for #product,:url => products_path, :html => { id: "fileupload", multipart: true } do |f|
%p
= f.text_field :name, placeholder: "Name"
%p
= f.text_field :price, class: "auto", data: { a_sign: "$ " }, placeholder: "Price"
%p
= f.text_field :description, placeholder: "Description"
%p
= f.fields_for :photos do |fp|
=fp.file_field :image
%br
.files{"data-target" => "#modal-gallery", "data-toggle" => "modal-gallery"}
%p.button.start
= f.submit
You can use accept_nested_attributes for to save associated data with only one create action.
Eg:-
class AlbumsController < ApplicationController
def new
#album = Album.new
#album.photos.build
end
def create
#album = Albums.new(params[:album])
#album.photos.build unless #album.photos.present?
if #album.save
flash[:notice] = "Successfully created albumn"
respond_with(#album, :location => albums_path())
else
flash[:error] = #album.errors.full_messages
render :new
end
end
end
Model:-
class Album < ActiveRecord::Base
has_many :photos, dependent: :destroy
accepts_nested_attributes_for :photos, allow_destroy: true, reject_if: proc {|attr| attr['image'].blank? }
end
class Photo < ActiveRecord::Base
belongs_to :album
end
View:-
= form_for #album,:url => albums_path, :html => {multipart: true } do |f|
%p
= f.text_field :name, placeholder: "Name"
%p
= f.text_field :price, class: "auto", data: { a_sign: "$ " }, placeholder: "Price"
%p
= f.text_field :description, placeholder: "Description"
%p
= f.fields_for :photos do |photo|
= photo.file_field :image
%br
.files
%p.button.start
= f.submit
Use ajax to firstly upload photo and after successful response continue with product.
New to rails and I'm trying to reject an empty nested file_field but it just keeps going though. Here is the setup.
EDIT: It does actually save the image properly if one is included, just doesn't reject the empty ones.
Fihish Model
class Finish < ActiveRecord::Base
default_scope order('finishes.id ASC')
attr_accessible :name,
:title,
##belongs_to##
:sku_id,
##has_many##
:image_attributes
belongs_to :skus
has_one :image, as: :imageable, :dependent => :destroy
accepts_nested_attributes_for :image, :reject_if => lambda { |a| a[:asset].blank? }, :allow_destroy => true
validates_presence_of :title
before_save :create_name
private
def create_name
self.name = title.parameterize
end
end
Finishes Controller
def new
#finish = Finish.new
#finish.build_image
respond_to do |format|
format.html # new.html.erb
# format.json { render json: #finish }
end
end
Image Model
class Image < ActiveRecord::Base
attr_accessible :content, #remove if no longer necessary
:asset
belongs_to :imageable, polymorphic: true
mount_uploader :asset, ImageUploader
end
Form
= form_for #finish, :html => { :multipart => true } do |f|
- if #finish.errors.any?
#error_explanation
%h1= "#{pluralize(#finish.errors.count, "error")} prohibited this finish from being saved:"
%ul
- #finish.errors.full_messages.each do |msg|
%li= msg
%fieldset{id: "finishes"}
.field
= f.label :title
= f.text_field :title
#finish-image.images
= f.fields_for :image do |image_builder|
= render 'images/image_fields', f: image_builder
.actions
= f.submit 'Save'
image_fields partial
.field
= f.label :asset, "Image"
= f.file_field :asset
- if f.object.asset
.image-box
= image_tag f.object.asset_url(:thumb).to_s
.remove-fields
= link_to_remove_fields f
I have model with virtual attributes, for use in simple_form:
class Sms < ActiveRecord::Base
attr_accessor :delayed_send, :send_time_date, :send_time_time
I have form for /smses/new:
= simple_form_for([:admin, resource]) do |f|
...
.clear
.field.grid_3
= f.input :delayed_send, :as => :boolean, :label => "Отложенная отправка на:"
.clear
.field.grid_3
= f.input :send_time_date, :as => :string, :input_html => { :class => 'date_picker' }, :disabled => true, :label => "Дату:"
.clear
.field.grid_1
= f.input :send_time_time, :as => :string, :disabled => true, :label => "Время:", :input_html => { :value => (Time.now + 1.minute).strftime("%H:%M") }
.clear
.actions.grid_3
= f.submit "Отправить"
And i wanna validate all that virtual attributes inside my SmsesController, in create action, and if it invalid - show error. But that doesn't work:
class Admin::SmsesController < Admin::InheritedResources
def create
#sms.errors.add(:send_time, "Incorrect") if composed_send_time_invalid?
super
end
How should i add my custom errors, if i using inherited_resources?
If there's not a specific reason you're validating in the controller, the validation should be in the model:
class Sms < ActiveRecord::Base
#two ways you can validate:
#1.use a custom validation routine
validate :my_validation
def my_validation
errors.add(:send_time, "Incorrect") if composed_send_time_invalid?
end
#OR 2. validate the attribute with the condition tested in a proc.
validates :send_time, :message=>"Incorrect", :if=>Proc.new{|s| s.composed_send_time_invalid?}
end
In the controller, a save (or call to object.valid?) will trigger these validations to run. You can then handle the response in your controller to re-render the action if warranted.