I am trying to implement an advanced search on all microposts (by keyword and by purpose -dropdown options-) but I can not even write to the database searches. I guess my problems could be: models construction, collection_select set up, or controller (mostly everywhere).
routes.rb has all resources, but :searches is not nested
new.html.erb:
<%= form_for #search do |f| %>
<%= f.label :keywords %><br>
<%= f.text_field :keywords %>
<%= f.label :purpose_id %><
<%= f.collection_select :purpose_id, Micropost.order(:purpose), :id, :purpose_id, include_blank: true %>
<%= f.submit 'Buscar' %>
<% end %>
searches_controller.rb:
class SearchesController < ApplicationController
def new
#search = Search.new
end
def create
#search = Search.new(params[:search])
if #search.save
redirect_to #search
else
redirect_to root_url
end
end def show
#search = Search.find(params[:id])
end
private
def search_params
params.require(:search).permit(:keywords, :purpose_id)
end
schema.db:
create_table "microposts", force: :cascade do |t|
t.text "content"
t.bigint "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "picture"
t.string "purpose"
t.index ["user_id", "created_at"], name: "index_microposts_on_user_id_and_created_at"
t.index ["user_id"], name: "index_microposts_on_user_id"
end
create_table "searches", force: :cascade do |t|
t.string "keywords"
t.integer "purpose_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Error from console:
Processing by SearchesController#create as HTML Parameters: {"utf8"=>"✓", "authenticity_token"=>"wA3FRz....6Kg==", "search"=>{"keywords"=>"", "purpose_id"=>"18"}, "commit"=>"Buscar"}
Completed 500 Internal Server Error in 3ms (ActiveRecord: 0.0ms)
ActiveModel::ForbiddenAttributesError (ActiveModel::ForbiddenAttributesError):
app/controllers/searches_controller.rb:8:in `create'
You're passing the raw params directly into your model's constructor, rather than search_params where you require/permit specific parameters. search_param needs to be invoked to do anything.
The line
#search = Search.new(params[:search])
needs to be
#search = Search.new(search_params)
Related
I'm trying to build a very basic forum-like app, where Users can create Topics and reply to existing Topics.
The Topic creation works fine and I'm able to display Reply form, however, the Reply create action is not working properly. I don't have any errors, it just redirects_to topics_path.
This is following a tutorial, so the code is not mine. Is anyone able to spot the obvious cause for this? Any help much appreciated!
replies_controller.rb
def create
#topic = Topic.find(params[:topic_id])
#reply = #topic.replies.create(params[:reply].permit(:reply))
#reply.user_id = current_user.id if current_user
#reply.save
if #reply.save
redirect_to topic_path(#topic)
else
flash[:notice] = "Error."
redirect_to topics_path
end
end
reply.rb
class Reply < ApplicationRecord
belongs_to :post
belongs_to :user
end
replies/_form.html.erb
<%= form_for [#topic, #topic.replies.create] do |f| %>
<%= f.label :reply %>
<%= f.text_area :reply, class: "textarea", rows: "10" %>
<%= f.submit class: "button is-primary" %>
<% end %>
topic.rb
class Topic < ApplicationRecord
belongs_to :user
has_many :replies
end
schema.rb
create_table "topics", force: :cascade do |t|
t.string "title"
t.text "content"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
end
create_table "replies", force: :cascade do |t|
t.text "reply"
t.bigint "topic_id"
t.bigint "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["topic_id"], name: "index_replies_on_topic_id"
t.index ["user_id"], name: "index_replies_on_user_id"
end
In file replies/_form.html.erb
you should use build method build instead of create. Replace line:
<%= form_for [#topic, #topic.replies.create] do |f| %>
to
<%= form_for [#topic, #topic.replies.build] do |f| %>
There are some another problems with the code:
#reply = #topic.replies.create(params[:reply].permit(:reply))
In this line you call new + save, without user, which is required.
Change this to:
#reply = #topic.replies.new(params[:reply].permit(:reply))
Then, you call save twice:
#reply.save
if #reply.save
...
First line is unnecessary.
And finally, what is the cause of rollback, in your Reply model you have:
belongs_to :post
But in the schema.rb and in the params you have topic:
Schema.rb:
t.bigint "topic_id"
Params:
"reply"=>{"reply"=>"Test reply"}, "commit"=>"Create Reply", "topic_id"=>"4"}
I have this method in my micropost.rb, the error is souly when I run this method. When I submit my form I do not get the errors from the model but the rendering of the else condition in the controller. Something seems to be wrong in the model but don't see why I get rollback transaction from server?
The method just checks that a user has not entered and sent more than one field.
Micropost.rb
validate :return_media_field_errors
private
def return_media_field_errors
if :img_url? && :video_url?
errors.add(:img_url, "Can only submit one field at a time")
end
end
MicropostController
def create
#micropost = current_user.microposts.build(micropost_params)
if #micropost.save
flash[:success] = "Micropost created!"
#micropost = current_user.microposts.build
#feed_items = current_user.feed.paginate(page: params[:page])
render 'static_pages/home'
else
#feed_items = []
render 'shared/error_messages'
end
end
private
def micropost_params
params.require(:micropost).permit(:content, :picture, :picture_cache, :image_url, :video_url, :gif_url)
end
micropost schema.rb
create_table "microposts", force: :cascade do |t|
t.text "content"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "picture"
t.string "image_url"
t.string "video_url"
t.string "gif_url"
t.index ["user_id", "created_at"], name: "index_microposts_on_user_id_and_created_at"
t.index ["user_id"], name: "index_microposts_on_user_id"
end
View params
<div class="media_field_1">
Image <%= f.text_field :image_url, class: 'form-control' %>
</div>
<div class="media_field_2">
Video <%= f.text_field :video_url, class: 'form-control' %>
</div>
I think the problem is this line:
if :img_url? && :video_url?
here :img_url? is a symbol, not an attribute
What you want is self.image_url? or just image_url, same for video_url
Im creating a blog application in ruby on rails. I have "album" model and a "photo" model. In the album edit view I want to be able to delete the uploaded images which are associated to an album.
Unfortunately I'm getting an undefined method error for checkbox?!
Do you have any solution or hints to make this work?
How can I get to the bottom of why it does not work?
If you need further information just let me know.
albums/edit.html.erb
<%= form_for #album do |f| %>
<% if #album.errors.any? %>
<h2><%= pluralize(#album.errors.count, "error") %> prevent this post from saving:</h2>
<ul>
<% #album.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>k
</ul>
<% end %>
<div class="blog_edit_wrapper">
<%= f.label :title %><br>
<%= f.text_field :title, class: "blog_edit_title" %>
<br>
<br>
<%= f.submit 'Submit', class: "blog_edit_submit" %>
</div>
<% end %>
<hr>
<% #album.photos.each do |photo| %>
<%= image_tag(photo.gallery_image) %>
<%= photo.check_box :remove_gallery_image %>
<br>
<br>
<%= f.submit 'Submit', class: "blog_edit_submit" %>
<% end %>
photos_controller.rb
class PhotosController < ApplicationController
before_action :find_photo, only: [ :show, :edit, :update, :destroy]
before_action :set_album
def index
#photo = Photo.all
end
def new
#photo = #album.photos.new
end
def show
end
def create
#photo = #album.photos.new photo_params
#photo.album = #album
if #photo.save
redirect_to #album
else
render :new
end
end
def edit
end
def update
#photo = #album.photos.find params[:id]
if #photo.update photo_params
redirect_to #album, notice: "Your photo was successfully saved!"
else
render 'edit'
end
end
def destroy
#photo.destroy
redirect_to photos_path
end
private
def set_album
#album = Album.find params[:album_id]
end
def photo_params
params.require(:photo).permit(:gallery_image, :album_id, :title, :remove_gallery_image)
end
def find_photo
#photo = Photo.find(params[:id])
end
end
photo.rb
class Photo < ActiveRecord::Base
mount_uploader :gallery_image, ImageUploader
belongs_to :album
end
albums_controller.rb
class AlbumsController < ApplicationController
before_action :find_album, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [:index, :show]
def index
#albums = Album.all.order("created_at desc").paginate(page: params[:page], per_page: 12)
end
def new
#album = Album.new
end
def show
#photo = Photo.all
end
def create
#album = Album.new album_params
if #album.save
redirect_to #album
else
render :new
end
end
def edit
end
def update
if #album.update album_params
redirect_to #album, notice: "Your article was successfully saved!"
else
render 'edit'
end
end
def destroy
#album.destroy
redirect_to albums_path
end
private
def album_params
params.require(:album).permit(:title)
end
def find_album
#album = Album.find(params[:id])
end
end
album.rb
class Album < ActiveRecord::Base
has_many :photos
end
schema.rb
ActiveRecord::Schema.define(version: 20170424131600) do
create_table "albums", force: :cascade do |t|
t.string "title"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "friendly_id_slugs", force: :cascade do |t|
t.string "slug", null: false
t.integer "sluggable_id", null: false
t.string "sluggable_type", limit: 50
t.string "scope"
t.datetime "created_at"
end
add_index "friendly_id_slugs", ["slug", "sluggable_type", "scope"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type_and_scope", unique: true
add_index "friendly_id_slugs", ["slug", "sluggable_type"], name: "index_friendly_id_slugs_on_slug_and_sluggable_type"
add_index "friendly_id_slugs", ["sluggable_id"], name: "index_friendly_id_slugs_on_sluggable_id"
add_index "friendly_id_slugs", ["sluggable_type"], name: "index_friendly_id_slugs_on_sluggable_type"
create_table "photos", force: :cascade do |t|
t.string "gallery_image"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "album_id"
end
create_table "posts", force: :cascade do |t|
t.string "title"
t.text "content"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "slug"
t.string "post_main_image"
end
add_index "posts", ["slug"], name: "index_posts_on_slug", unique: true
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
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
I believe your issue is that you're not using a form but just iterating over the photos.
Also you can place the code for editing the photos inside the first form.
Place this code below the <br> tags and remove the <% #album.photos.each do |photo| %> interation.
<%= fields_for(#album, #album.photos.build) do |f| %>
<%= image_tag(f.gallery_image)%>
<%= f.check_box :remove_gallery_image %>
<% end %>
Edit:
To clarify the above; place the aforementioned code inside your original edit #album form and remove the iteration for the #album.photos.each
I've got a page where a User (using Devise) sets up multiple preferences through checkboxes and then radio buttons of predefined data. So far I have the user able to update a has_and_belongs_to_many association but I can't get my belongs_to one's working.
At the moment I've got this error with the following parameters shown:
PG::ForeignKeyViolation: ERROR: insert or update on table "users" violates foreign key constraint
{"utf8"=>"✓",
"_method"=>"patch",
"user"=>{"sport_ids"=>["4"], "goal_ids"=>["6"], "moment_id"=>"moment_id", "workout_id"=>"workout_id"},
"commit"=>"Save Changes",
"id"=>"1"}
It seems clear that I'm not passing an id number through but I don't know how to fix it. When I don't get an error nothing happens.
Here are my files
models/user.rb
class User < ApplicationRecord
belongs_to :city
belongs_to :workout
belongs_to :fitness_level
belongs_to :moment
has_and_belongs_to_many :sports
has_and_belongs_to_many :goals
has_and_belongs_to_many :gyms
end
controllers/users_controller.rb
class UsersController < ApplicationController
...
def edit
#user = User.find(params[:id])
#auth = current_user.id
# To make sure you can't edit someone elses profile
if #auth != #user.id
redirect_to #user
end
#sports = Sport.all.order(name: :asc)
#goals = Goal.all.order(name: :asc)
#workouts = Workout.all.order(:name)
#moments = Moment.all
end
def update
#user = User.find(params[:id])
if #user.update(user_params)
redirect_to #user
else
render 'edit'
end
end
def show
#user = User.find(params[:id])
end
private
def user_params
params.require(:user).permit(sport_ids: [], goal_ids: [])
params.require(:user).permit(:workout_id, :moment_id)
end
end
users/edit.html.erb
<%= simple_form_for #user do |f| %>
# The following two work
<% #sports.each do |sport| %>
<%= check_box_tag "user[sport_ids][]", sport.id, form.object.sports.include?(sport) %>
<%= sport.name %>
<% end %>
<% #goals.each do |goal| %>
<%= check_box_tag "user[goal_ids][]", goal.id, form.object.goal.include?(goal) %>
<%= sport.name %>
<% end %>
# the below doesn't work
<% #moments.each do |moment| %>
<%= radio_button_tag 'user[moment_id]', :moment_id %>
<h4><%= moment.name %></h4>
<% end %> <!-- end moments-->
<% #workouts.each do |workout| %>
<%= radio_button_tag 'user[workout_id]', :workout_id %>
<% end %> <!-- end workouts-->
<% end %> <! -- end form -->
I have some important styling with the forms using tags so that will need to stay.
EDIT: Adding Users table in schema
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.inet "current_sign_in_ip"
t.inet "last_sign_in_ip"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.jsonb "settings", default: {}, null: false
t.string "first_name"
t.string "last_name"
t.date "date_of_birth"
t.integer "city_id"
t.text "bio"
t.integer "workout_id"
t.integer "fitness_level_id"
t.integer "moment_id"
t.index ["city_id"], name: "index_users_on_city_id", using: :btree
t.index ["email"], name: "index_users_on_email", unique: true, using: :btree
t.index ["fitness_level_id"], name: "index_users_on_fitness_level_id", using: :btree
t.index ["moment_id"], name: "index_users_on_moment_id", using: :btree
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
t.index ["settings"], name: "index_users_on_settings", using: :gin
t.index ["workout_id"], name: "index_users_on_workout_id", using: :btree
end
Im pretty sure the problem in that line:
<%= radio_button_tag 'user[moment_id]', :moment_id %>
You dont pass moment_id and workout_id from the view to controller`s update action.
Try to change it to:
<% #moments.each do |moment| %>
<%= radio_button_tag 'user[moment_id]', moment.id %>
<h4><%= moment.name %></h4>
<% end %> <!-- end moments-->
The same is for workout:
<% #workouts.each do |workout| %>
<%= radio_button_tag 'user[workout_id]', workout.id %>
<% end %> <!-- end workouts-->
Also why dont you pass permitted params in one line? Like this:
def user_params
params.require(:user).permit(:moment_id, :workout_id, sport_ids: [], goal_ids: [])
end
I'm trying to create a posting form to post a url & some text. The nested form submits without an error but I can't display the subsequent content of that form in my show action controller.
Post Model
class Post < ActiveRecord::Base
has_many :texts
has_many :urls
accepts_nested_attributes_for :texts, :urls
end
Text Model
class Text < ActiveRecord::Base
belongs_to :post
end
Url Model
class Url < ActiveRecord::Base
belongs_to :post
end
Post Controller
class PostsController < ApplicationController
def index
#posts = Post.all
end
def show
#post = Post.find(params[:id])
#texts = #post.texts
#urls = #post.urls
end
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
private
def post_params
params.require(:post).permit(:texts_attributes => [:textattr], :urls_attributes => [:urlattr])
end
show.html.erb
<%= #text.textattr %>
<%= #url.urlattr %>
Database Schema
create_table "posts", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "texts", force: :cascade do |t|
t.text "textattr"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "post_id"
end
add_index "texts", ["post_id"], name: "index_texts_on_post_id"
create_table "urls", force: :cascade do |t|
t.string "urlattr"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "post_id"
end
add_index "urls", ["post_id"], name: "index_urls_on_post_id"
end
Error message after I press submit on the form
!(http://imgur.com/dzdss5z)
Your help would be amazing - thank you!
The #text and #url variables are never set in the controller's show action, so they are nil. Hence, there is an error when you try to call those attributes from them in the view.
You have set the #texts and #urls variables, so you can do something like this:
<% #texts.each do |text| %>
<%= text.textattr %>
<% end %>
<% #urls.each do |url| %>
<%= url.urlattr %>
<% end %>