I have few multi-selects in my app that validation always fail (getting form errors: genres can't be blank, languages can't be blank) during every form submit (even if the multi-select options were selected). Code:
models/dvd.rb
class Dvd < ActiveRecord::Base
has_and_belongs_to_many :genres
has_and_belongs_to_many :languages
has_many :rentals, dependent: :destroy
has_many :users, through: :rentals
validates :title, presence: true
validates :year, inclusion: {in: 1900..Time.now.year.to_i}, :presence => {:message => 'Year must be from 1900 till current year.'}
validates :length, inclusion: {in: 1..999}, :presence => {:message => 'DVD length must be in minutes in range 1..999.'}
validates :genres, presence: true
validates :languages, presence: true
end
models/language.rb
class Language < ActiveRecord::Base
has_and_belongs_to_many :dvds
validates :title, presence: true, uniqueness: { case_sensitive: false }
end
models/genre.rb
class Genre < ActiveRecord::Base
has_and_belongs_to_many :dvds
validates :title, presence: true, uniqueness: { case_sensitive: false }
end
dvds_controller.rb
class DvdsController < ApplicationController
before_action :set_dvd, only: [:show, :edit, :update, :destroy]
before_action :set_genres, :set_languages, only: [:new, :edit]
before_action :delete_genres, :delete_languages, only: [:update]
after_action :add_genres, :add_languages, only: [:create, :update]
# GET /dvds
# GET /dvds.json
def index
#dvds = Dvd.all
end
# GET /dvds/1
# GET /dvds/1.json
def show
end
# GET /dvds/new
def new
#dvd = Dvd.new
end
# GET /dvds/1/edit
def edit
end
# POST /dvds
# POST /dvds.json
def create
#dvd = Dvd.new(dvd_params)
respond_to do |format|
if #dvd.save
format.html { redirect_to #dvd, notice: 'Dvd was successfully created.' }
format.json { render :show, status: :created, location: #dvd }
else
format.html { render :new }
format.json { render json: #dvd.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /dvds/1
# PATCH/PUT /dvds/1.json
def update
respond_to do |format|
if #dvd.update(dvd_params)
format.html { redirect_to #dvd, notice: 'Dvd was successfully updated.' }
format.json { render :show, status: :ok, location: #dvd }
else
format.html { render :edit }
format.json { render json: #dvd.errors, status: :unprocessable_entity }
end
end
end
# DELETE /dvds/1
# DELETE /dvds/1.json
def destroy
#dvd.destroy
respond_to do |format|
format.html { redirect_to dvds_url, notice: 'Dvd was successfully deleted.' }
format.json { head :no_content }
end
end
private
def set_dvd
if params[:id]
#dvd = Dvd.find(params[:id])
else
#dvd = Dvd.find(params[:dvd][:id])
end
end
def dvd_params
params.require(:dvd).permit(:title, :description, :year, :genres, :languages, :length)
end
def add_languages
params[:dvd][:languages].each do |l|
if !l.empty?
#dvd.languages << Language.find(l)
end
end
end
def add_genres
params[:dvd][:genres].each do |g|
if !g.empty?
#dvd.genres << Genre.find(g)
end
end
end
def set_genres
#genres = Genre.all
end
def set_languages
#languages = Language.all
end
def delete_genres
# Delete all dvd genre relations
#dvd.genres.delete_all
end
def delete_languages
# Delete all dvd language relations
#dvd.languages.delete_all
end
end
routes.rb
Rails.application.routes.draw do
resources :dvds do
resources :rentals
end
resources :rentals
resources :languages
resources :genres
resources :dvds
resources :users, :path => 'clients'
root to: "index#index"
end
form
<div class="field">
<%= f.label :genres %><br>
<%= f.collection_select(:genres, Genre.all, :id, :title, {:selected => #dvd.genres.map {|dl| dl.id}, :include_blank => false}, {:multiple => true}) %>
</div>
<div class="field">
<%= f.label :languages %><br>
<%= f.select :languages, options_for_select(Language.all.map {|l| [l.title,l.id]}, #dvd.languages.map {|dl| dl.id}), {:include_blank=> false}, {:multiple => true} %>
</div>
Any ideas what is wrong with that?
The field names for the form should be genre_ids and language_ids, not genres and languages.
Related
I am trying simple_form nested attributes as suggested in https://github.com/plataformatec/simple_form/wiki/Nested-Models
The thing is, when I render the form I just can see the submit button, but not the input field. What am I doing wrong?
_form.html.erb
<%= simple_form_for [:admin, #incident] do |f| %>
<%= f.error_notification %>
<%= f.simple_fields_for :comments do |builder| %>
<%= builder.input :info, label: "Informe de seguimiento" %>
<% end %>
<div class="form-actions">
<%= f.submit "Enviar", class: "btn btn-primary" %>
</div>
<% end %>
incidents_controller.rb
class Admin::IncidentsController < ApplicationController
before_action :set_incident, only: [:show, :edit, :update]
def index
#incidents = Incident.all
end
def show
end
def new
#incident = Incident.new
#incident.comments.build
end
def edit
end
def update
respond_to do |format|
if #incident.update(incident_params)
format.html { redirect_to #incident, notice: 'Incidencia actualizada actualizada con éxito.' }
format.json { render :show, status: :ok, location: #incident }
else
format.html { render :edit }
format.json { render json: #incident.errors, status: :unprocessable_entity }
end
end
end
private
def set_incident
#incident = Incident.find(params[:id])
end
def incident_params
params.require(:incident).permit(:info, :subject, :status, comments_attributes: [:info])
end
end
incident.rb
class Incident < ApplicationRecord
belongs_to :user, optional: true
has_many :comments, dependent: :destroy
accepts_nested_attributes_for :comments, allow_destroy: true, reject_if: proc { |attributes| attributes['info'].blank? }
enum status: [:abierto, :tramite, :pendiente, :cerrado]
after_initialize :set_default_status, :if => :new_record?
def set_default_status
self.status ||= :abierto
end
end
comment.rb
class Comment < ApplicationRecord
belongs_to :user, optional: true
belongs_to :incident
end
You need to add #incident.comments.build to the show action of Admin::IncidentsController. Now it has no comments, I suppose, so the form is empty.
And you need to add :id to comments_attributes, without it comment can't be saved. If you planning to have some 'Delete' checkbox for existing comments you also need to add :_destroy to the attributes array
I have problem with the comments.
If I comment a status whatever it is, the comment will be displayed on the last status.
Hope you could help, I can edit with more code if needed, let me know what you need.
_form.html.erb
<%= simple_form_for([status, status.comments.new]) do |f|%>
<%= f.input :content %>
<%= f.button :submit, "Comment", class:"btn btn-primary"%>
<% end %>
index.html.erb
<% #statuses.each do |status| %>
<%= image_tag status.user.avatar.thumb if status.user.avatar?%>
<%= status.user.full_name%>
<%= simple_format(status.content) %>
<%= link_to time_ago_in_words(status.created_at) + " ago", status %>
<% if status.user == current_user %>
<span class="admin">
<%= link_to "Edit", edit_status_path(status) %> |
<%= link_to "Delete", status, method: :delete, data: {confirm: "Are you sure?"} %>
</span>
<% end %>
<% status.comments.each do |comment| %>
<%= comment.content %>
<% end %>
<%= render partial: "comments/form", locals: {status: status} %>
<% end %>
routes.rb
Rails.application.routes.draw do
get 'profiles/show'
devise_for :users
devise_scope :user do
get 'register', to: 'devise/registrations#new', as: :register
get 'login', to: 'devise/sessions#new', as: :login
get 'logout', to: 'devise/sessions#destroy', as: :logout
end
resources :statuses do
resources :comments
end
resources :comments
get 'feed', to: "statuses#index", as: :feed
root "statuses#index"
get '/:id', to: "profiles#show"
end
statuses_controller.rb
class StatusesController < ApplicationController
before_filter :authenticate_user!, only: [:new, :create, :edit, :update]
before_action :set_status, only: [:show, :edit, :update, :destroy]
def index
#users = User.all
#statuses = Status.all
#comments = Comment.all
end
def show
#status = Status.find(params[:id])
#comments = #status.comments.all
end
def new
#status = Status.new
#comment = #status.comments.build
end
def create
#status = Status.new(status_params)
#status.user = current_user
respond_to do |format|
if #status.save
format.html { redirect_to #status, notice: 'Status was successfully created.' }
format.json { render :show, status: :created, location: #status }
else
format.html { render :new }
format.json { render json: #status.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #status.update(status_params)
format.html { redirect_to #status, notice: 'Status was successfully updated.' }
format.json { render :show, status: :ok, location: #status }
else
format.html { render :edit }
format.json { render json: #status.errors, status: :unprocessable_entity }
end
end
end
def destroy
#status.destroy
respond_to do |format|
format.html { redirect_to statuses_url, notice: 'Status was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_status
#status = Status.find(params[:id])
end
def status_params
params.require(:status).permit( :content)
end
end
comments_controller.rb
class CommentsController < ApplicationController
def create
#status = Status.find_by(params[:status_id])
#comment = #status.comments.create(params_comment)
#comment.save
redirect_to statuses_path
end
def index
#statuses = Status.all
#comments = Comment.all
#comment = Comment.find_by(params[:id])
end
def new
#status = Status.find(params[:status_id])
#comment = Comment.new
end
private
def params_comment
params.require(:comment).permit(:content)
end
end
my models
class Comment < ActiveRecord::Base
belongs_to :status
belongs_to :user
end
class Status < ActiveRecord::Base
belongs_to :user
has_many :comments
default_scope -> { order(created_at: :DESC)}
validates :content, presence: true,
length: {minimum: 2}
validates :user_id, presence: true
end
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
mount_uploader :avatar, AvatarUploader
validates_presence_of :avatar
validates_integrity_of :avatar
validates_processing_of :avatar
has_many :statuses
has_many :users
validates :first_name, presence: true
validates :last_name, presence: true
validates :profile_name, presence: true,
uniqueness: true,
format: {
with: /\A[a-zA-Z0-9_-]+\z/,
message: "Must be formatted correctly." }
def full_name
first_name + " " + last_name
end
def gravatar_url
stripped_email = email.strip
end
end
Thank you
This is when I commen as "Nelly Johan" here on my own status...
And this my comment not where I expected:
I am trying to validate the "name" attribute in my Ingredient model. However, when I add validates :name, :uniqueness => true in my Ingredient model, it does not validate the name (can insert ingredient with same name). The ingredient model has a complex relationship with other model. Please see the code below.
My ultimate goal is to allows user to create ingredient if the ingredient does not exist in the Ingredient table. If the ingredient already existed, used the ingredient id for quantity. If anyone has any idea how to achieve that, please provide the solution to it. Thanks in advance.
ingredient.rb
class Ingredient < ActiveRecord::Base
has_many :quantities
has_many :recipes, through: :quantities
validates :name, :presence => true, :uniqueness => true
end
quantity.rb
class Quantity < ActiveRecord::Base
belongs_to :ingredient
belongs_to :recipe
accepts_nested_attributes_for :ingredient,
:reject_if => :all_blank
validates :ingredient, :uniqueness => true
end
recipe.rb
class Recipe < ActiveRecord::Base
has_many :quantities,
dependent: :destroy
has_many :ingredients,
:through => :quantities
accepts_nested_attributes_for :quantities,
reject_if: :all_blank,
allow_destroy: true
accepts_nested_attributes_for :ingredients
end
view section for creating Ingredient:
%strong Ingredients:
%fieldset#recipe-ingredients
%br
= f.fields_for :quantities do |builder|
= render 'recipe/quantity_fields', f: builder
.links
= link_to_add_association 'add ingredient', f, :quantities, 'data-association-insertion-node' => '#recipe-ingredients', 'data-assoication-insertion-moethod' => "append", :wrap_object => Proc.new{|quantity| quantity.ingredient.build ; quantity}
%br
Ingredient controller:
class IngredientController < ApplicationController
before_action :set_ingredient, only: [:show, :edit, :update, :destroy]
# GET /ingredients
# GET /ingredients.json
def index
#ingredients = Ingredient.all
end
# GET /ingredients/1
# GET /ingredients/1.json
def show
end
# GET /ingredients/new
def new
#ingredient = Ingredient.new
end
# GET /ingredients/1/edit
def edit
end
def create
#ingredient = Ingredient.new(ingredient_params)
respond_to do |format|
if #ingredient.save
format.html { redirect_to #ingredient, notice: 'Ingredient was successfully created.' }
format.json { render :show, status: :created, location: #ingredient }
else
format.html { render :new }
format.json { render json: #ingredient.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /ingredients/1
# PATCH/PUT /ingredients/1.json
def update
respond_to do |format|
if #ingredient.update(ingredient_params)
format.html { redirect_to #ingredient, notice: 'Ingredient was successfully updated.' }
format.json { render :show, status: :ok, location: #ingredient }
else
format.html { render :edit }
format.json { render json: #ingredient.errors, status: :unprocessable_entity }
end
end
end
# DELETE /ingredients/1
# DELETE /ingredients/1.json
def destroy
#ingredient.destroy
respond_to do |format|
format.html { redirect_to ingredients_url, notice: 'Ingredient was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_ingredient
#ingredient = Ingredient.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def ingredient_params
params.require(:ingredient).permit(:name)
end
end
It seems like a known issue probably a bug. Check this: https://github.com/rails/rails/issues/20676
I am creating a gallery where you can upload multiple images via form.
The form is nested inside another form and when that is submitted it should create the gallery with the attached images inside it. When I check the output I get this which means its not submitting the correct images to the gallery object at all:
Parameters: {
"utf8"=>"✓",
"authenticity_token"=>"BJhkDx7rCRMIlXQ6T9FskXZe7+fdYxj0qm+VnCaC51w=",
"book"=>{
"jacket_cover" =>#<ActionDispatch::Http::UploadedFile:0x000001025d3128
#tempfile=#<File:/var/folders/mf/srx7jt8s2rdg0mn5hr98cvz80000gn/T/RackMultipart20140918-47766-1g9kyas>, #original_filename="559a7a477253d58f891f8e852162dfac.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"book[jacket_cover]\"; filename=\"559a7a477253d58f891f8e852162dfac.jpg\"\r\nContent-Type: image/jpeg\r\n">,
"title"=>"zd szw wrwr",
"synopsis"=>"<p>cfdcrgcgrere</p>\r\n",
"body"=>"<p>rccgregrrgerereg</p>\r\n",
"age"=>"19",
"publisher"=>"Dove books",
"author_attributes"=>{
"name"=>"zsdxfrrwg",
"biography"=>"<p>exffwfwefewewf</p>\r\n"},
"gallery_attributes"=>{
"images_attributes"=>{
"0"=>{
"file"=>[#<ActionDispatch::Http::UploadedFile:0x000001025d20e8 #tempfile=#<File:/var/folders/mf/srx7jt8s2rdg0mn5hr98cvz80000gn/T/RackMultipart20140918-47766-tk1rdb>, #original_filename="23ebb202a3655c6d0947251cce8625b6.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"book[gallery_attributes][images_attributes][0][file][]\"; filename=\"23ebb202a3655c6d0947251cce8625b6.jpg\"\r\nContent-Type: image/jpeg\r\n">, #<ActionDispatch::Http::UploadedFile:0x000001025d1ff8 #tempfile=#<File:/var/folders/mf/srx7jt8s2rdg0mn5hr98cvz80000gn/T/RackMultipart20140918-47766-j3ji7c>, #original_filename="559a7a477253d58f891f8e852162dfac.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"book[gallery_attributes][images_attributes][0][file][]\"; filename=\"559a7a477253d58f891f8e852162dfac.jpg\"\r\nContent-Type: image/jpeg\r\n">]
}
}
}
}, "commit"=>"Create Book"
}
The books_controller handles the submission of the gallery with the images in it:
class BooksController < ApplicationController
before_action :set_book, only: [:show, :edit, :update, :destroy]
before_filter :authenticate_user!, only: [:new, :edit, :update, :destroy]
# GET /books
# GET /books.json
def index
#books = Book.order('created_at DESC').all
end
# GET /books/1
# GET /books/1.json
def show
# #book = Book.find(params[:id])
#book = Book.friendly.find(params[:id])
#gallery = #book.gallery
end
# GET /books/new
def new
#book = Book.new
#book.build_author
#gallery = #book.build_gallery
#gallery.images.build
end
# GET /books/1/edit
def edit
end
# POST /books
# POST /books.json
def create
#raise params.inspect
#book = Book.new(book_params)
#binding.pry
respond_to do |format|
if #book.save
format.html { redirect_to #book, notice: 'Book was successfully created.' }
format.json { render action: 'show', status: :created, location: #book }
else
format.html { render action: 'new' }
format.json { render json: #book.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /books/1
# PATCH/PUT /books/1.json
def update
respond_to do |format|
if #book.update(book_params)
format.html { redirect_to #book, notice: 'Book was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #book.errors, status: :unprocessable_entity }
end
end
end
# DELETE /books/1
# DELETE /books/1.json
def destroy
#book.destroy
respond_to do |format|
format.html { redirect_to books_url }
format.json { head :no_content }
end
end
# Get authors
# def reviews
# #book = Book.friendly.find(params[:id])
# #reviews = #movie.reviews
# respond_to do |format|
# format.html { render 'reviews/index' } # index.html.erb
# format.json { render json: #movies }
# end
# end
private
# Use callbacks to share common setup or constraints between actions.
def set_book
# #book = Book.find(params[:id])
#book = Book.friendly.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def book_params
params.require(:book).permit(:title, :synopsis, :body, :age, :publisher, :jacket_cover, author_attributes: [:name,:biography], gallery_attributes: [:name, :book_id ] )
end
end
book.rb
class Book < ActiveRecord::Base
has_attached_file :jacket_cover, :styles => { :medium => "300x300>", :thumb => "100x100>" }, :default_url => "/images/:style/missing.png"
validates_attachment_content_type :jacket_cover, :content_type => /\Aimage\/.*\Z/
validates :jacket_cover, :title, :slug, :synopsis, :body, :age, :publisher, presence: true
validates_uniqueness_of :title
extend FriendlyId
friendly_id :title, use: [:slugged, :finders]
belongs_to :author
has_one :gallery
has_many :stories
accepts_nested_attributes_for :author
accepts_nested_attributes_for :gallery
scope :available, ->{ where(available: true) }
scope :unavailable, ->{ where(available: [nil, false]) }
end
image.rb
class Image < ActiveRecord::Base
belongs_to :gallery
has_attached_file :file, :styles => { :medium => "300x300>", :thumb => "100x100>" }, :default_url => "/images/:style/missing.png"
validates_attachment_content_type :file, :content_type => /\Aimage\/.*\Z/
end
gallery.rb
class Gallery < ActiveRecord::Base
belongs_to :books
has_many :images
accepts_nested_attributes_for :books
accepts_nested_attributes_for :images, :allow_destroy => true
end
galleries_controller.rb
class GalleriesController < ApplicationController
before_action :set_gallery, only: [:show, :edit, :update, :destroy]
def index
#galleries = Gallery.all
end
def show
#gallery = Gallery.find(params[:id])
#images = #gallery.images
end
def new
#gallery = Gallery.new
##gallery.images.build
# #images = #gallery.build_images
# #gallery.images.build
end
def edit
#gallery.images.build
end
def create
#gallery = Gallery.new(gallery_params)
#image = Image.create()
##gallery.images.build
respond_to do |format|
if #gallery.save
format.html { redirect_to #gallery, notice: 'Gallery was successfully created.' }
format.json { render action: 'show', status: :created, location: #gallery }
else
format.html { render action: 'new' }
format.json { render json: #gallery.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #gallery.update(gallery_params)
format.html { redirect_to #gallery, notice: 'Gallery was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #gallery.errors, status: :unprocessable_entity }
end
end
end
def destroy
#gallery.destroy
respond_to do |format|
format.html { redirect_to galleries_url }
format.json { head :no_content }
end
end
private
def set_gallery
#gallery = Gallery.find(params[:id])
end
def gallery_params
params.require(:gallery).permit(:name, :book_id, :image)
#params.require(:gallery).permit(:name, :book_id, :images[], images_attributes: [:id, :file []])
#params.require(:gallery).permit(:name, :book_id, :images[images_attributes: [:file]])
# params.require(:gallery).permit(:name, :book_id, images_attributes: [:id, :image[:file]])
end
end
Added form in here that submits the gallery of images:
<%= simple_form_for(#book, :html => { :multipart => true } ) do |f| %>
<%= f.error_notification %>
<div class="inputs">
<div id="image" class="field">
<h3>Add the book image here</h3>
<div class="single-file">
<div class="file_upload">
<%= f.file_field :jacket_cover %>
</div>
</div>
</div>
<div id="title" class="field">
<h3>Add the title</h3>
<%= f.input :title %>
</div>
<div id="synopsis" class="field">
<h3>Add the book synopsis</h3>
<%= f.input :synopsis, :as => :ckeditor, :label => false, :input_html => { :ckeditor => { :toolbar => 'Full', :height => 400 } } %>
</div>
<div id="body" class="field">
<h3>Add the book body summary here</h3>
<%= f.input :body, :as => :ckeditor, :label => false, :input_html => { :ckeditor => { :toolbar => 'Full', :height => 400 } } %>
</div>
<div id="age" class="field">
<h3>Add the book age group</h3>
<%= f.input :age, collection: [['3-7', '3-7'],['7-11', '7-11'],['11-14', '11-14']], prompt: "Select age range" %>
</div>
<div id="publisher" class="field">
<h3>Who published the book</h3>
<%= f.input :publisher %>
</div>
<div id="school" class="field">
<h3>Schools reading this book (add the name and full address of the school)</h3>
<%= f.simple_fields_for :schools, :wrapper => 'inline' do |builder| %>
<%= render 'school_fields', :f => builder %>
<%= link_to_add_association 'add school', f, :schools, :render_options => {:wrapper => 'inline' }, :class => 'fa fa-plus' %>
<% end %>
</div>
</div>
<%#= f.select( :author_id, Author.all.map {|u| [u.name,u.id]}, {:include_blank => false, prompt: "No Author"} ) %>
<div id="author-inputs">
<h3>Add author</h3>
<%#= link_to 'New Author', new_author_path, :remote => true, :id => "new_author_link" %>
<%= f.simple_fields_for :author, :wrapper => 'inline' do |builder| %>
<%= render 'author_fields', :f => builder %>
<% end %>
</div>
<%= f.simple_fields_for :gallery do |builder| %>
<%= render 'galleries/form', :f => builder %>
<% end %>
<div class="actions">
<%= f.button :submit %>
</div>
<% end %>
When I check in the db console I cannot see any images inside the gallery when I search Gallery.all
I have created a gist file with all the necessary info in it.
Can anyone shed any light into this for me at all?
The main problem is with your parent form and model relationship.
In your book model you should have belongs_to :gallery instead of has_one :gallery. Also check your database. books Table should have gallery_id. As a database concept belongs_to should have parent table id. So model should be :
class Book < ActiveRecord::Base
has_attached_file :jacket_cover, :styles => { :medium => "300x300>", :thumb => "100x100>" }, :default_url => "/images/:style/missing.png"
validates_attachment_content_type :jacket_cover, :content_type => /\Aimage\/.*\Z/
validates :jacket_cover, :title, :slug, :synopsis, :body, :age, :publisher, presence: true
validates_uniqueness_of :title
extend FriendlyId
friendly_id :title, use: [:slugged, :finders]
belongs_to :author
belongs_to :gallery
has_many :stories
accepts_nested_attributes_for :author
accepts_nested_attributes_for :gallery
scope :available, ->{ where(available: true) }
scope :unavailable, ->{ where(available: [nil, false]) }
end
and your gallery model should look like :
class Gallery < ActiveRecord::Base
has_many :books
has_many :images
accepts_nested_attributes_for :books
accepts_nested_attributes_for :images, :allow_destroy => true
end
Your parent model in form should be gallery model instead of book. And also make required changes in your gallery controller.
Here is the error I receive:
Here is a Gist of the files (some of you may find this easier to read):
https://gist.github.com/drichards2013/7902811
Here is index.html.erb:
<%= render 'pages/home' if !user_signed_in? %>
<div id="things" class="transitions-enabled">
<% #things.each do |thing| %>
<div class='panel panel default'>
<div class="box">
<%= link_to image_tag(thing.image.url(:medium)), thing %>
<div class='panel-body'>
<% if thing.link.blank? %>
<strong><%= thing.title %></strong>
<% else %>
<strong><%= link_to thing.title, "http://#{thing.link}"%></strong>
<% end %>
<p><%= thing.description %></p>
By <%= link_to thing.user.username, user_path(thing.user) %>
<% if thing.user == current_user %>
<%= link_to edit_thing_path(thing) do %>
<span class='glyphicon glyphicon-edit'></span>
<% end %>
<%= link_to thing_path(thing), method: :delete, data: { confirm: 'Are you sure?' } do %>
<span class='glyphicon glyphicon-trash'></span>
<% end %>
</div>
<% end %>
</div>
</div>
<% end %>
</div>
<%= will_paginate #posts, renderer: BootstrapPagination::Rails, class: 'pull-left' %>
Here is thing.rb:
class Thing < ActiveRecord::Base
belongs_to :user
default_scope -> { order('created_at DESC') }
has_attached_file :image, :styles => { :large => '500x500>', :medium => '300x300>', :thumb => '100x100>' }
validates :image, presence: true
validates :title, presence: true, length: { minimum: 5, maximum: 50 }
# Returns microposts from the users being followed by the given user.
def self.from_users_followed_by(user)
followed_user_ids = "SELECT followed_id FROM relationships
WHERE follower_id = :user_id"
where("user_id IN (#{followed_user_ids}) OR user_id = :user_id",
user_id: user.id)
end
end
class ThingsController < ApplicationController
before_action :set_thing, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
# GET /things
# GET /things.json
def index
#things = Thing.all.order("created_at DESC").paginate(:page => params[:page], :per_page => 50)
end
# GET /things/1
# GET /things/1.json
def show
end
# GET /things/new
def new
#thing = current_user.things.build
end
# GET /things/1/edit
def edit
end
# POST /things
# POST /things.json
def create
#thing = current_user.things.build(thing_params)
respond_to do |format|
if #thing.save
format.html { redirect_to #thing, notice: 'Thing was successfully created.' }
format.json { render action: 'show', status: :created, location: #thing }
else
format.html { render action: 'new' }
format.json { render json: #thing.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /things/1
# PATCH/PUT /things/1.json
def update
respond_to do |format|
if #thing.update(thing_params)
format.html { redirect_to #thing, notice: 'Thing was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #thing.errors, status: :unprocessable_entity }
end
end
end
# DELETE /things/1
# DELETE /things/1.json
def destroy
#thing.destroy
respond_to do |format|
format.html { redirect_to things_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_thing
#thing = Thing.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def thing_params
params.require(:thing).permit(:title, :description, :image, :link)
end
end
Here is things_controller.rb:
class ThingsController < ApplicationController
before_action :set_thing, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
# GET /things
# GET /things.json
def index
#things = Thing.all.order("created_at DESC").paginate(:page => params[:page], :per_page => 50)
end
# GET /things/1
# GET /things/1.json
def show
end
# GET /things/new
def new
#thing = current_user.things.build
end
# GET /things/1/edit
def edit
end
# POST /things
# POST /things.json
def create
#thing = current_user.things.build(thing_params)
respond_to do |format|
if #thing.save
format.html { redirect_to #thing, notice: 'Thing was successfully created.' }
format.json { render action: 'show', status: :created, location: #thing }
else
format.html { render action: 'new' }
format.json { render json: #thing.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /things/1
# PATCH/PUT /things/1.json
def update
respond_to do |format|
if #thing.update(thing_params)
format.html { redirect_to #thing, notice: 'Thing was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #thing.errors, status: :unprocessable_entity }
end
end
end
# DELETE /things/1
# DELETE /things/1.json
def destroy
#thing.destroy
respond_to do |format|
format.html { redirect_to things_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_thing
#thing = Thing.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def thing_params
params.require(:thing).permit(:title, :description, :image, :link)
end
end
Here is user.rb:
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable, #:recoverable,
:rememberable, :trackable, :validatable
has_many :things
validates :name, presence: true, length: { minimum: 2, maximum: 20}
validates :username, presence: true, length: { minimum: 2, maximum: 20}
validates :username, uniqueness: true
validates :email, presence: true
validates :email, uniqueness: true
has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" , :nav => "25x25"}
extend FriendlyId
friendly_id :username
def show
end
#follow features
has_many :followed_users, through: :relationships, source: :followed
has_many :relationships, foreign_key: "follower_id", dependent: :destroy
has_many :followed_users, through: :relationships, source: :followed
def following?(other_user)
relationships.find_by(followed_id: other_user.id)
end
def follow!(other_user)
relationships.create!(followed_id: other_user.id)
end
def unfollow!(other_user)
relationships.find_by(followed_id: other_user.id).destroy!
end
def feed
Thing.from_users_followed_by(self)
end
has_many :reverse_relationships, foreign_key: "followed_id",
class_name: "Relationship",
dependent: :destroy
has_many :followers, through: :reverse_relationships, source: :follower
end
Here is users_controller.rb:
class UsersController < ApplicationController
def show
#user = User.find_by_username(params[:id])
end
def user_params
params.require(:user).permit(:avatar)
end
def following
#title = "Following"
#user = User.find_by_username(params[:id])
#users = #user.followed_users.paginate(page: params[:page])
render 'show_follow'
end
def followers
#title = "Followers"
#user = User.find_by_username(params[:id])
#users = #user.followers.paginate(page: params[:page])
render 'show_follow'
end
end
What I have tried
I went to the console, did thing = Thing.last, then tried to call thing.user, and that returned nil. So it appears the user_id isn't saving. How can I make that happen?
This is where the "try" method is your friend.
You're trying to get the "username" value from your user, which is missing.
If you were to write your line like:
thing.user.try(:username)
Then it would not crash, and it would fetch the username if user existed.
Your user is not saving because your one-to-many relationship is not set up. You might want to add a user_id to your things table.
Fixed by adding :user_id to the thing_params method in my things controller.
def thing_params
params.require(:thing).permit(:title, :description, :image, :link, :user_id)
end