I have a nested form with the parent model built with devise and the child model without devise. I'm working on the edit form which has the user model fields and nested inside them expert model fields. The update/save works but logs me out and gives me an error saying "You need to sign in or sign up before continuing"
When I sign back in I see that the fields have been updated correctly. I would like it to not sign me out and show me the updated Edit page upon submitting the form.
User model - built with devise.
class User < ActiveRecord::Base
// some stuff
has_one :expert
accepts_nested_attributes_for :expert, :update_only => true
// more stuff
Expert model - built withOUT devise.
class Expert < ActiveRecord::Base
belongs_to :user
User controller:
before_action :set_user, only: [:show, :edit, :update, :destroy]
before_filter :authenticate_user!, :only => [:show, :edit]
def edit
#user = User.friendly.find(params[:id])
#title = "Edit Profile"
end
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.friendly.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:id, :name, :email, :phone, :password, :role, :expert_attributes => [:id, :Location])
end
end
Expert controller:
def edit
#expert = Expert.find(params[:id])
#title = "Edit Expert Profile"
end
Edit user view form partial:
<%= form_for(#user) do |f| %>
<% if #user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% #user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :Name %><br>
<%= f.text_field :name, :class => "form-control" %>
</div>
<div class="field">
<%= f.label :Email %><br>
<%= f.text_field :email, :class => "form-control" %>
</div>
<div class="field">
<%= f.label :Phone %><br>
<%= f.text_field :phone, :class => "form-control" %>
</div>
<div class="field">
<%= f.label :Password %><br>
<%= f.text_field :password, :class => "form-control" %>
</div>
<div class="field">
<%= f.fields_for :expert do |e| %>
<%= e.label :Location %><br />
<%= e.text_field :Location %>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
In your UsersController, you should add :update to the authenticate_user! filter:
before_filter :authenticate_user!, :only => [:show, :edit, :update]
Related
I currently have review model that will allow a user to create reviews for a tea model. The user who creates the review can edit or delete the review. I have a nested route within teas that allows you to create a new review for teas as you are viewing all reviews for that specific tea. Currently the nested new route does not allow creation as well as a google authenticated user can not create a review. Below is my controller action and view. I am not experiencing any error it just appears to rollback the database and follow the else logic and render the new page again.
Model
class Review < ApplicationRecord
belongs_to :user
belongs_to :tea
validates :title, presence: true
validates :rating, numericality: {only_integer: true, greater_than_or_equal_to: 0, less_than: 11}
validates :tea, uniqueness: {scope: :user, message: "has already been reviewed by you" }
scope :order_by_rating, ->{left_joins(:reviews).group(:id).order('avg(rating) desc')}
end
Controller Action
def create
#review = current_user.reviews.build(review_params)
if #review.valid?
#review.save
redirect_to new_review_path(#review)
else
render :new
end
end
View
<%= form_for Review.new do |f|%>
<% if params[:tea_id] %>
<%= f.hidden_field :tea_id %>
<% else %>
<div>
<%= f.label :tea_id, "Select a Tea Blend" %>
<%= f.collection_select :tea_id, Tea.alpha, :id, :flavor_and_brand, include_blank: true %>
</div>
<% end %>
<div>
<%= f.label :rating %>
<%= f.number_field :rating, min:0, max:10 %>
</div>
<br>
<div>
<%= f.label :title %>
<%= f.text_field :title %>
</div>
<br>
<div>
<%= f.label :content %>
<br>
<%= f.text_area :content, size: "60x25" %>
</div>
<br>
<%= f.submit %>
<% end %>
The simple answer was that I did not include create in my before action. This is what was causing my set_tea to not be automatically done as a before action.
The correct way to do this is by defining a nested route and setting up the form so that it posts to that route. So instead of creating a single form where the user has to select the tea you create a form on the show page or by each tea in a index page where the user can create reviews.
# config/routes.rb
resources :teas do
resources :reviews, shallow: true
end
shallow: true makes it so that the member actions (show, edit, update, destroy) are not nested.
Then setup a partial for the form so that you can reuse it:
# app/views/reviews/_form.html.erb
<%= form_for([local_assigns(:tea), review]) do |f| %>
<div class="field">
<%= f.label :rating %>
<%= f.number_field :rating, min:0, max:10 %>
</div>
<div class="field">
<%= f.label :title %>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :content %>
<%= f.text_area :content, size: "60x25" %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
By passing an array you get the nested route as the action attribute (/teas/1/reviews) and don't have to monkey around with a hidden input. local_assigns(:tea) avoids a NoMethodError if its not passed to the partial. The array is compacted so that this partial will work for both creating and updating.
# app/views/reviews/new.html.erb
<%= render partial: 'form', tea: #tea, review: #review >
# app/views/reviews/edit.html.erb
<%= render partial: 'form', review: #review >
# app/views/teas/show.html.erb
<h2>Review this tea</h2>
<%= render partial: 'reviews/form', tea: #tea, review: #tea.reviews.new >
In the controller you can just fetch the tea from params[:tea_id] since you passed it in the path.
class ReviewsController < ApplicationController
before_action :set_tea, only: [:new, :index, :create]
before_action :set_review, only: [:show, :edit, :update, :destroy]
# POST /teas/1/reviews
def create
# creating the review off the tea reveals intent better than doing
# it off the user
#review = #tea.reviews.new(review_params) do |r|
r.user = current_user
end
# Always check if the record is actually persisted
# - not just if the applications validations pass!
if #review.save
# you could also redirect to the review but this makes more
# sense from a ux perspective
redirect_to #tea, notice: 'Thank you for your review'
else
render :new
end
end
# GET /reviews/:id/edit
def edit
end
# PUT|PATCH /reviews/:id
def update
if #review.update(review_params)
redirect_to #review, notice: 'Review updated.'
else
render :edit
end
end
private
def set_tea
#tea = Tea.find(params[:tea_id])
end
def set_review
#review = Review.find(params[:id])
end
def review_params
params.require(:review).permit(:rating, :title)
end
end
I am working on a form for a editorial calendar app. I have two things going out that are pretty similar and not working.
Working with 3 models: Platforms, Posts and Calendars. They are join tables. Platform <=> Post, Post <=> Calendars
Post/new & Post/edit form:
<div class="container">
<div class="form-field">
<%= form_for #post do |f| %>
<%= f.label :title %>
<%= f.text_field :title, required: true %> <br>
Title is required.
</div>
<div class="form-field">
<%= f.label :content%>
<%= f.text_area :content%>
</div>
<div class="form-field">
<%= f.label :link %>
<%= f.text_field :link %>
</div>
<div class="file-field">
<%= f.label :picture%>
<%= f.file_field :picture, id: :post_picture%>
</div>
<div class="file-field">
<%= f.label :finalized %>
<%= f.radio_button :finalized , true%>
<%= f.label :finalized, "Yes" %>
<%= f.radio_button :finalized, false %>
<%= f.label :finalized, "No" %>
</div>
<%= f.hidden_field :user_id %> <br>
<div class="form-field">
<%= f.fields_for :platform_attributes do |platform| %>
<%= platform.label :platform, "Social Platforms"%>
<%= platform.collection_check_boxes :platform_ids, Platform.all, :id, :name %> <br> <br>
</div>
<div>
<h4> Or Create a new platform: </h4>
<%= platform.label :platform, 'New Platform'%>
<%= platform.text_field :name%> <br> <br>
</div>
<% end %>
<%= f.submit%>
<% end %>
</div>
My post controller is handling the checkboxes issue, and the "schedule post" issue. It will only allow me to schedule for one calendar, and it does not save the updates and add additional calendars.
Posts Controller:
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :schedule_post, :destroy]
def new
#posts = current_user.posts.select {|p| p.persisted?}
#post = current_user.posts.build
#platforms = Platform.all
end
def edit
#calendars = current_user.calendars
#platforms = Platform.all
end
def create
#post = current_user.posts.build(post_params)
if #post.save
redirect_to post_path(#post)
else
redirect_to new_post_path
end
end
def update
#post.update(post_params)
if #post.save
redirect_to post_path(#post), notice: 'Your post has been updated.'
else
redirect_to edit_post_path(#post)
end
end
def schedule_post
#calendar_post = CalendarPost.new(calendar_post_params)
if #calendar_post.save
binding.pry
redirect_to post_path(#post)
else
render 'show'
end
end
private
def set_post
#post = Post.find(params[:id])
end
def set_calendars
#calendars = current_user.calendars
end
def post_params
params.require(:post).permit(:title, :content, :link, :finalized, :picture, :user_id, :platform_attributes => [:platform_ids, :name])
end
def calendar_post_params
params.require(:calendar_post).permit(:post_id, :calendar_id, :date, :time)
end
end
I want the user to be able to add a post to multiple platforms and multiple calendars because of the versatility of what someone may need.
I also have my setter in my Post model.
class Post < ApplicationRecord
has_many :calendar_posts
has_many :calendars, through: :calendar_posts
has_many :platform_posts
has_many :platforms, through: :platform_posts
belongs_to :user
def platform_attributes=(platform_attributes)
if platform_attributes['platform_ids']
platform_attributes.platform_ids.each do |id|
platform = Platform.find(id: id)
self.platforms << platform
end
end
if platform_attributes['name'] != ""
platform = Platform.find_or_create_by(name: platform_attributes['name'])
self.platforms << platform
end
end
thoughts? why are they not saving to more than one calendar or more than one platform if they choose to have more than one?
Here is the updated code... and more of what I know about these changes and what is happening.
My submit button is not working for some odd reason on my form, so I'm trying to get the params submitted but it won't even route to give me params even if I raise them, nothing is happening.
On the form you can choose checkboxes or add in a platform. If you add in a platform it creates that one but it does not also save the other ones you selected. If you go to edit the post, and click submit with changes, no page loads at all and nothing is happening in log. It's just idle.
<%= f.fields_for :platform_attributes do |platform| %>
assumes you are creating one platform... it says "these are the fields for this platform"
but platform_ids is intended to be a selection of a set of platforms... and probably should be outside of the fields_for section (which should only surround the name field).
try something like the following:
<div class="form-field">
<%= f.label :platform_ids, "Social Platforms"%>
<%= f.collection_check_boxes :platform_ids, Platform.all, :id, :name %> <br> <br>
</div>
<div>
<%= f.fields_for :platform_attributes do |platform| %>
<h4> Or Create a new platform: </h4>
<%= platform.label :name, 'New Platform'%>
<%= platform.text_field :name%> <br> <br>
<% end %>
<%# end fields_for %>
</div>
Also you'll need to update permit/require appropriately eg
def post_params
params.require(:post).permit(:title, :content, :link, :finalized, :picture, :user_id, :platform_ids, :platform_attributes => [:name])
end
Note: not tested - bugs are left as an exercise for the reader ;)
i have the following situation:
I have a controller for admins , that i use to manage the users (model generated by Devise).
So, i use the actions of another controller to manage the resources "users".
Here my files:
admins_controller.rb
before_action :set_user, only: [:show, :edit, :update, :destroy]
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to admins_index_path, notice: 'Product was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
def set_user
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:id)
end
edit.html.erb
<h1>Edit user</h1>
<%= form_for #user, :as => :patch, :url => admins_update_path(id: #user) do |f| %>
<% if #user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#user.errors.count, "error") %> prohibited this line_item from being saved:</h2>
<ul>
<% #user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :id %><br>
<%= f.text_field :id %>
</div>
<div class="field">
<%= f.label :email %><br>
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :admin %><br>
<%= f.text_field :admin %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
The problem is that when i submit the edit , rails gives me an error:
Can you help me? i really don t knok how to fix
thank you
EDIT:
Change your view to drop the :as option, that will fix the params requirement error:
= form_for #user, :url => admins_update_path(id: #user) do |f|
The :as tells form_for to use a different key name than the default class-name based one that you're using in your params.require call.
You should also remove your f.text_field :id - you don't want someone editing that.
Then your permit(...) block will need to include anything else in that form that you want to allow mass-assignment for, most likely you'll want: permit(:email, :admin)
I am creating a customer service web app for our clients that allows them to submit new customer service tickets, once logged in. I have a login system that I created using the sorcery gem, and I have modified it to accept an additional field: client code.
We are assigning client codes to help prevent unauthorized users from creating accounts.
Upon login, it asks for the information like so:
Name:
Email:
Client Code: (we assign this)
Password:
My question is this, is there a way to only display customer service tickets to clients with the same client code? For example, The "Coca-Cola" clients would only see other "Coca-Cola" tickets and the "Pepsi" clients would only see other "Pepsi" tickets, etc.
Here is my Tickets Controller:
class TicketsController < ApplicationController
before_filter :require_login
def new
#ticket = Ticket.new
end
def create
#ticket = Ticket.new(ticket_params)
if
#ticket.save
redirect_to #ticket
flash[:notice] = "Your Ticket has been submitted. We will contact you very soon!"
else
flash[:notice] = "Something went wrong :("
render 'new'
end
end
def show
#ticket = Ticket.find(params[:id])
end
def index
#tickets = Ticket.all
end
def edit
#ticket = Ticket.find(params[:id])
end
def update
#ticket = Ticket.find(params[:id])
if #ticket.update(ticket_params)
redirect_to #ticket
else
render 'edit'
end
end
def destroy
#ticket = Ticket.find(params[:id])
#ticket.destroy
redirect_to tickets_path
end
private
def ticket_params
params.require(:ticket).permit(:name, :email, :phone, :help)
end
end
Here is the Ticket Index View:
<div class="panel panel-default">
<div class="panel-heading">Ticket Queue</div>
<div class="panel-body">
<table class="table">
<tr>
<th>Name</th>
<th>Phone</th>
<th></th>
<th></th>
</tr>
<% #tickets.each do |ticket| %>
<tr>
<td><%= ticket.name %></td>
<td><%= ticket.phone %></td>
<td><%= button_to "View or Edit", ticket_path(ticket), :class => "btn btn-primary btn-sm", :method => :get %></td>
<td><%= button_to "Delete", ticket_path(ticket), :class => "btn btn-primary btn- sm", :method => :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</table>
</div>
Here is the New Ticket View:
<div class="panel panel-info">
<div class="panel-heading">
<div id="wrapper">
<h1 class="panel-title">New Ticket
<div id="first"><%= button_to "Back", root_path, :class => "btn btn-primary btn-sm", :method => :get %></div>
</div>
</h1>
</div>
<div class="panel-body">
<%= form_for :ticket, url: tickets_path do |f| %>
<p>
<%= f.label "Name:" %>
<%= f.text_field :name %>
</p>
<p>
<%= f.label "Email:" %>
<%= f.text_field :email %>
</p>
<p>
<%= f.label :"Phone #:" %>
<%= f.text_field :phone %>
</p>
<p>
<%= f.label :"How can we help?" %>
<p><%= f.text_area :help, :cols=> 38, :rows => 8 %></p>
</p>
<p>
<button type="submit" class="btn btn-primary btn-sm"><span class="glyphicon glyphicon-envelope"></span> Submit Ticket</button>
</p>
<% end %>
</div>
</div>
Here is the User Model:
class User < ActiveRecord::Base
authenticates_with_sorcery!
validates :password, length: { minimum: 3 }
validates :password, confirmation: true
validates :password_confirmation, presence: true
validates :code, inclusion: { in: %w(Client1, Client2), message: "Please enter a valid Client Code", :allow_nil => false}
validates :email, uniqueness: true
validates_format_of :email, :with => /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
end
Here is the New User View:
<%= form_for(#user) do |f| %>
<% if #user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% #user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :email %><br>
<%= f.text_field :email %>
</div>
<div class="field">
<%= f.label :password %><br />
<%= f.password_field :password %>
</div>
<div class="field">
<%= f.label :password_confirmation %><br />
<%= f.password_field :password_confirmation %>
</div>
<div class="field">
<%= f.label "Client Code" %><br />
<%= f.text_field :code %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Here is the User Controller:
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
skip_before_filter :require_login, only: [:index, :new, :create]
# GET /users
def index
#users = User.all
end
# GET /users/1
def show
end
# GET /users/new
def new
#user = User.new
end
# GET /users/1/edit
def edit
end
# POST /users
def create
#user = User.new(user_params)
if #user.save
redirect_to(:users, notice: 'User was successfully created')
else
render :new
end
end
# PATCH/PUT /users/1
def update
if #user.update(user_params)
redirect_to #user, notice: 'User was successfully updated.'
else
render :edit
end
end
# DELETE /users/1
def destroy
#user.destroy
redirect_to users_url, notice: 'User was successfully destroyed.'
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def user_params
params.require(:user).permit(:email, :password, :password_confirmation, :code)
end
end
Let me know if you need to see anything else, thank you!
From your example, I would think Users would have an additional database layer:
class User < ActiveRecord::Base
belongs_to :company
has_many :tickets, through: :companies
end
class Company < ActiveRecord::Base
has_many :users
has_many :tickets
end
class Ticket < ActiveRecord::Base
belongs_to :company
end
Then you can easily display tickets associated with each company
I'm building a simple app (Ruby 2.0.0 and Rails 4) where a user can create a project and for each project create multiple screens. When creating a screen the user can upload a screenshot, that refer to a its own model (I do this to handle multiple versions of the same screen).
When creating the screen, the screenshot doesn't seem to be created because of a permission problem. Here's the server log:
Processing by ScreensController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"kezaGADsaLmY/+zozgbjEe5UfdeqRPg58FCf1qzfHxY=", "screen"=>{"project_id"=>"24", "name"=>"Billing", "description"=>"This is the page where a user will enter their credit card information", "screenshot"=>{"image"=># <ActionDispatch::Http::UploadedFile:0x007fcdce25b2c0 #tempfile=#<Tempfile:/var/folders/pv/srwrv0qj35b0hsxkt42l_z500000gn/T/RackMultipart20131007-91790-tewse9>, #original_filename="fb-banner.png", #content_type="image/png", #headers="Content-Disposition: form-data; name=\"screen[screenshot][image]\"; filename=\"fb-banner.png\"\r\nContent-Type: image/png\r\n">}}, "commit"=>"Create Screen"}
Unpermitted parameters: screenshot
These are my models:
Screen
class Screen < ActiveRecord::Base
belongs_to :project
has_many :screenshots
validates :name, presence: true
accepts_nested_attributes_for :screenshots
end
Screenshot
class Screenshot < ActiveRecord::Base
belongs_to :screen
end
This is my screens_controller:
class ScreensController < ApplicationController
before_action :set_screen, only: [:show, :edit, :update, :destroy]
def index
#screens = Screen.all
end
def show
end
def new
#screen = Screen.new(:project_id => params[:project_id])
#screen.screenshot.build
end
def edit
end
def create
#screen = Screen.create(screen_params)
if #screen.save
flash[:notice] = "A new screen has been added to this project"
redirect_to [#screen.project]
else
render :action => 'new'
end
end
def update
#screen = Screen.find(params[:id])
if #screen.update_attributes(screen_params)
flash[:notice] = "The screen has been successfully updated"
redirect_to [#screen.project]
else
render :action => 'edit'
end
end
def destroy
#screen = Screen.find(params[:id])
#screen.destroy
flash[:notice] = "Successfully destroyed screen"
redirect_to [#screen.project]
end
private
def set_screen
#screen = Screen.find(params[:id])
end
def screen_params
params.require(:screen).permit(:project_id, :name, :description, screenshot_attributes: [ :id, :screen_id, :image, :version ])
end
end
And finally this is the form:
<%= form_for #screen, :html => { :multipart => true } do |f| %>
<% if #screen.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#screen.errors.count, "error") %> prohibited this screen from being saved:</h2>
<ul>
<% #screen.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.hidden_field :project_id %>
</div>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %><br>
<%= f.text_field :description %>
</div>
<%= f.fields_for :screenshot do |s| %>
<%= s.hidden_field :screen_id, :value => #screen.id %>
<%= s.hidden_field :version, :value => "1" %>
<%= s.label :image %><br>
<%= s.file_field :image %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I hope this is enough to help me spot the problem. I'm a newbie when it comes to programming, so any help is more than welcome.
I recently worked through something similar, and this is what seemed to work...
Change your fields_for to plural:
<%= f.fields_for :screenshots do |s| %>
And also, make your params
def screen_params
params.require(:screen).permit(:project_id, :name, :description, screenshots_attributes: [ :id, :screen_id, :image, :version ])
end
Also, you need to update your new action to make screenshots plural, like so:
def new
#screen = Screen.new(:project_id => params[:project_id])
#screen.screenshots.build
end