I have an issue with associating two models in my rails application: Users & Profiles. An individual user profile should be created after a new user signs up. After signing up the user, saving data into the actual profile model is not successful. I can not get it to work. Please find a detailed description below.
Here is my setup:
I use Rails 4.0.0.rc2 and ruby 2.0.0p195.
Both models are associated like this:
profile.rb
class Profile < ActiveRecord::Base
belongs_to :user
end
user.rb
has_one :profile, dependent: :destroy
accepts_nested_attributes_for :profile
before_create :build_profile
As I use the devise gemI have created a registrationscontrollerto change the after_sign_up_path:
class RegistrationsController < Devise::RegistrationsController
protected
def after_sign_up_path_for(resource)
new_user_profile_path(:user_id => #user)
end
end
Whenever I sign up a new user the actual sign up works fine, the user is subsequently being directed to http://localhost:3000/users/42/profile/new for example. However, when I then enter the data into the profile form fields and click on submit I get the following error:
No route matches [POST] "/users/profile"
Although one could expect a routing error, you will notice a different error when looking at the actual domain:
http://localhost:3000/users//profile
In case you still want to have a look at my routes.rb please do (relevant excerpt):
devise_for :users, :controllers => { :registrations => "registrations" }
devise_scope :user do
get 'signup', :to => "devise/registrations#new", as: :signup
get 'login', :to => "devise/sessions#new", as: :login
get 'logout', :to => "devise/sessions#destroy", as: :logout
end
resources :users do
resource :profile
end
However, as noted above I don't really have a routing issue. It rather seems like I have an issue with the current user_id not being properly shown in the domain, which can be related to either my actual profile form or the new action on the profiles controller.
I start my profile form on new.html.erb like this:
<%= form_for #profile, url: user_profile_path(#user) do |f| %>
My profiles_controller.rblooks like this:
class ProfilesController < ApplicationController
before_action :set_profile, only: [:show, :edit, :update, :destroy]
# GET /profiles
# GET /profiles.json
def index
#profiles = Profile.all
end
# GET /profiles/1
# GET /profiles/1.json
def show
end
# GET /profiles/new
def new
#profile = Profile.new
end
# GET /profiles/1/edit
def edit
end
# POST /profiles
# POST /profiles.json
def create
#profile = Profile.new(profile_params)
respond_to do |format|
if #profile.save
format.html { redirect_to #profile, notice: 'Profile was successfully created.' }
format.json { render action: 'show', status: :created, location: #profile }
else
format.html { render action: 'new' }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /profiles/1
# PATCH/PUT /profiles/1.json
def update
respond_to do |format|
if #profile.update(profile_params)
format.html { redirect_to #profile, notice: 'Profile was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
# DELETE /profiles/1
# DELETE /profiles/1.json
def destroy
#profile.destroy
respond_to do |format|
format.html { redirect_to profiles_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_profile
##profile = Profile.find(params[:id])
#profile = current_user.profile
end
# Never trust parameters from the scary internet, only allow the white list through.
def profile_params
params.require(:profile).permit(:photo, :address, :zip, :city, :state, :country, :telephone, :experience, :levels, :ages, :travel, :teachinglocation, :onlineteaching, :quotation, :aboutme, :subjects, :specialties, :lessondetails, :equipment)
end
end
What do I do wrong? How can I properly ensure that a newly signed up user can properly save his profile data?
It would be so great, if you could help me out.
It appears the Profile controller is not configured with the correct url - as can be seen by the missing User ID information.
You can see all currently defined paths by running the command rake routes from the command line.
I am a beginner at RESTful design and Rails, so do not consider the following expert advice.
Try changing the location of your redirect_to in the Profile create method:
# POST /profiles
# POST /profiles.json
def create
...
format.html { redirect_to user_profile_path(#profile.user, #profile), notice: 'Profile was successfully created.' }
...
If this works, the path should also be updated in the update and delete methods. As well as the format.json sections.
For additional info about this topic see:
2.9 Creating Paths and URLs From Objects
http://guides.rubyonrails.org/routing.html
Related
I'm hoping someone can help. I am using the Devise gem for registering and signing in users. I have a Profile controller. When an existing user logs in, I want them to be diverted to the Profile's show.html.erb page in order to view their profile. I would expect this would be done under the Sessions controller but it doesn't seem to do anything
The Sessions controller code is:
class Registrations::SessionsController < Devise::SessionsController
# before_action :configure_sign_in_params, only: [:create]
protected
def after_sign_in_path_for(resource)
profile_path(resource)
end
However, when a user registers, the redirect works successfully under the Registrations controller below:
class RegistrationsController < Devise::RegistrationsController
# before_action :configure_sign_up_params, only: [:create]
# before_action :configure_account_update_params, only: [:update]
protected
def after_sign_up_path_for(resource)
new_profile_path(resource)
end.
I also want to have a link to the users Profile page when they are logged in but when I do it throws up the following error
application.html.erb code for the link is below (I have tried a number of different variables in place of the '#profile' but with no success)
<li><%= link_to 'Show Profile', profile_path(#profile), :class => 'navbar-link' %></li>
The error I receive is:
ActionController::UrlGenerationError in Profiles#index
No route matches {:action=>"show", :controller=>"profiles", :id=>nil} missing required keys: [:id]
My routes (which I'm not sure are setup correctly:
Rails.application.routes.draw do
resources :profiles
get 'profiles/:id', to: 'profiles#show'
get '/profiles/new' => 'profiles#new'
get '/profiles/edit' => 'profiles#edit'
get '/profiles/index' => 'profiles#index'
root to: 'pages#index'
devise_for :users, :controllers => { :registrations => "registrations" }
Lastly, my Profile controller:
class ProfilesController < ApplicationController
before_action :set_profile, only: [:show, :edit, :update, :destroy]
def index
#search = Profile.search(params[:q])
#profiles = #search.result(distinct: true)
end
def show
#profile = Profile.find(params[:id])
end
def new
#profile = Profile.new
end
def create
#profile = Profile.new(profile_params)
respond_to do |format|
if #profile.save
format.html { redirect_to #profile, notice: 'Your Profile was successfully created' }
format.json { render :show, status: :created, location: #profile }
else
format.html { render :new }
format.json { render json: #profile.errors, status: :unprocessable_entry }
end
end
end
def edit
#profile = Profile.find(params[:id])
end
def update
respond_to do |format|
if #profile.update(profile_params)
format.html { redirect_to #profile, notice: 'Profile was successfully updated.' }
format.json { render :show, status: :ok, location: #profile }
else
format.html { render :edit }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
def destroy
#profile.destroy
respond_to do |format|
format.html { redirect_to profile_url, notice: 'Profile was successfully destroyed.' }
format.json { head :no_content }
end
end
def set_profile
#profile = Profile.find(params[:id])
end
private
def profile_params
params[:profile][:user_id] = current_user.id
params.require(:profile).permit(:full_name, :contact_number, :location, :makeup_type, :bio, :user_id, :image)
end
end
Any help is most appreciated.
Ok so there are two problems:
Redirecting after sign in
Url generation error in the application layout
Redirecting after sign in
You need to add the controller to your routes definition (like you have registrations.
devise_for :users, controllers: { registrations: "registrations", sessions: 'registrations/sessions' }
Url generation error in the application layout
I assume that the profile model is associated with the user (e.g. profile belongs_to user, or maybe user has_one profile). I also assume that you want to have a link for the current user's profile.
If that is the case then you could most likely do something like this:
<%= if current_user %>
<li>
<%= link_to 'Show Profile', profile_path(current_user.profile), :class => 'navbar-link' %>
</li>
<% end %>
Otherwise, you should set #profile in some before_action in the application controller or in any controller that uses the application layout.
In your application controller you want something like this
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
def after_sign_in_path_for(user)
profile_path(current_user)
end
#user is the model name for example that you created with devise
end
I searched about this question, but had no success.
I´m trying to learn ruby on rails, came from php. I generated a webapp with the generation tool, second I generated a News controller with scaffold. The devise and pundit are installed too with gems.
The program works perfectly, the problem is related to the News module, I generated it with scaffold.
The routes where created with the command: resources :news
My idea is to create one _form.html.erb and it be called to create a new record or to updated an existing record. Some tutorials teach to create a new.html.erb and an update.html.erb file and duplicate the code, but I know that is possible to have partials as the main form part.
I´m using simple_form_for and the code to do the new is:
# GET /news/new
def new
#news = New.new
authorize New
end
The _form.html.erb
<%= simple_form_for(#news) do |f| %>
<%= f.input :titulo %>
<%= f.input :resumo %>
<%= f.button :submit %>
<% end %>
When I enter to edit, it works, but to add a new it throws.
ActionController::UrlGenerationError at /news/new
No route matches {:action=>"show", :controller=>"news", :locale=>:en} missing required keys: [:id]
Sorry for my bad english, I´m without direction here, is there any way that I can solve it?
Thanks.
====== UPDATED =======
routes.rb
Rails.application.routes.draw do
root to: 'visitors#index'
devise_for :users
resources :users
resources :news
end
New.rb (Model)
class New < ActiveRecord::Base
belongs_to :user
end
application_controller.rb
class ApplicationController < ActionController::Base
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_filter :set_locale
def default_url_options(options={})
{ locale: I18n.locale }
end
private
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
end
news_controller.rb (Complete)
class NewsController < ApplicationController
before_filter :authenticate_user!
after_action :verify_authorized
#before_action :set_news, only: [:show, :edit, :update, :destroy]
# GET /news
# GET /news.json
def index
#news = New.all
authorize New
end
# GET /news/1
# GET /news/1.json
def show
#news = New.find(params[:id])
authorize New
end
# GET /news/new
def new
#news = New.new
authorize New
end
# GET /news/1/edit
def edit
#news = New.find(params[:id])
authorize New
end
# POST /news
# POST /news.json
def create
#news = New.new(news_params)
respond_to do |format|
if #news.save
format.html { redirect_to #news, notice: 'New was successfully created.' }
format.json { render :show, status: :created, location: #news }
else
format.html { render :new }
format.json { render json: #news.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /news/1
# PATCH/PUT /news/1.json
def update
respond_to do |format|
if #news.update(news_params)
format.html { redirect_to #news, notice: 'New was successfully updated.' }
format.json { render :show, status: :ok, location: #news }
else
format.html { render :edit }
format.json { render json: #news.errors, status: :unprocessable_entity }
end
end
end
# DELETE /news/1
# DELETE /news/1.json
def destroy
#news.destroy
respond_to do |format|
format.html { redirect_to news_url, notice: 'New was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_news
#news = New.find(params[:id])
end
private
def news_params
params.require(:news).permit(:titulo, :resumo, :texto, :published_at, :user_id)
end
end
Command rake routes
news_index GET /news(.:format) news#index
POST /news(.:format) news#create
new_news GET /news/new(.:format) news#new
edit_news GET /news/:id/edit(.:format) news#edit
news GET /news/:id(.:format) news#show
PATCH /news/:id(.:format) news#update
PUT /news/:id(.:format) news#update
DELETE /news/:id(.:format) news#destroy
Thanks in advance.
======= UPDATE 2 ===========
Changing my New action to this:
def new
#news = New.create(params[:id])
end
It solved, but everytime I enter, it creates an empty record...
Use news_index_path for GET /news and POST /news. Rails doesn't figure out the pluralization correctly for the "news" term.
Check the output of rake routes, it will be obvious.
I get the following error, Couldn't find User with 'id'=
I have this in my Users_Controller,
def edit
#user = #signed_in_user
end
This is in my routes.rb,
root 'welcome#welcome'
get 'login' => 'sessions#login', :as => :login
get 'profile' => 'users#profile', :as => :profile
post 'logging/user' => 'sessions#create'
get 'logout' => 'sessions#destroy', :as => :logout
get 'about' => 'about'
resources :users
get 'register' => 'users#new', :as => :register
get 'edit' => 'users#edit', :as => :edit
This is in my application_controller.rb,
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :set_user
protected
def set_user
unless session[:user_id] == nil
#signed_in_user = User.find(session[:user_id])
end
end
end
This is in my Users_Controller
Here is my code from my User_Controller on creating the account
class UsersController < ApplicationController
before_action :set_user, only: [:show, :edit, :update, :destroy]
# GET /users
# GET /users.json
def index
#users = User.all
end
def profile
#user = User.find(session[:user_id]) unless session[:user_id] == ""
redirect_to login_path, notice: "You're not logged in" unless #user
end
# GET /users/1
# GET /users/1.json
def show
end
# GET /users/new
def new
#user = User.new
end
# GET /users/1/edit
def edit
#user = #signed_in_user
end
# POST /users
# POST /users.json
def create
#user = User.new(user_params)
respond_to do |format|
if #user.save
format.html { redirect_to #user, notice: 'User was successfully created.' }
format.json { render :show, status: :created, location: #user }
else
format.html { render :new }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /users/1
# PATCH/PUT /users/1.json
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User 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
# DELETE /users/1
# DELETE /users/1.json
def destroy
#user.destroy
respond_to do |format|
format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_user
#user = User.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def user_params
params.require(:user).permit(:name, :password, :password_confirmation, :email, :age)
end
end
And this is the link that I use for my HTML,
<li role="presentation"><%= link_to 'Edit', edit_path %></li>
So, to start, a good practice when you get an 'Couldn't find' message is to check what instance variables are in your view.
So in a view, just type: <%= #user_id %> and see if anything shows up on your page, thus indicating if any user is even present! The other problem is that your instance variable might be <%= user.id %> but I am not sure as I can't see your code and how the user is stored in the database.
Second if you run rake routes, you generally find that the edit path will have a URI pattern like: "/edit(.:format)", meaning the route need "edit_path(#user.id)" rather than just "edit_path".
Let me know if this leads you anywhere or you have further questions and I hope I can answer them!
===========
Additional info:
Well without more code to look at, I would provide a few more suggestions...The goal is to have the <%= #user.id %> (or user_id) show up on the page somehow, thus telling you it is available.
The set_user method is an instance method, not a class method. To make it a class method, try def self.set_user. This invokes the method of the instance on the controller, thus making it a class method.
Make sure you have a session object to use. In the routes, it looks like post logging/user might be creating the session, but I am not sure.
Keep the edit_path(#user.id) or however the id is stored for the user as the route rather than just edit_path. I am pretty sure if you run 'rake routes', it will tell you that an additional variable needs to be passed for the link to work
Use the gem byebug Here is the link: https://github.com/deivid-rodriguez/byebug. You get this error while the edit page or where ever you are getting the error write in the action byebug. As you have mentioned in the console it shows a arrow pointing at a specific line in the application, the last line should appear as this (byebug), here write the variable in which you are getting the user id. If we take an example of your application controller in the set_user method:
def set_user
byebug
unless session[:user_id] == nil
#signed_in_user = User.find(session[:user_id])
end
end
In the console after (byebug) write session[:user_id] so this will give you the value of the session[:user_id]. So if this is null then you have a problem here or just follow the same procedure to check anywhere else.
Also there is one more thing you can do to learn is just create a new project or use the existing one and generate a scaffold which will give you options of show, edit, index. It will generate all the views, controller code, migration and everything. You can do that like this:
rails generate scaffold User email:string password:string
You can add more fields if you want. And then in your application just visit http://localhost:[port_no]/users which will by default take you to index page where you can add new users, edit existing ones. This will teach you about everything. It would be like a reference code for you. Read more at: http://guides.rubyonrails.org/command_line.html#rails-generate
And being more specific to users only there is a gem named Devise which will give you all the required things like sign_in, sign_up, session_management for users. Hope these things help you with your issue.
Edit:
Here is a very good tutorial link which will help you: https://www.railstutorial.org/book/updating_and_deleting_users#sec-updating_users
I have two models post and topic in my rails app
class Post < ActiveRecord::Base
#relation between topics and post
belongs_to :topic
#post is valid only if it's associated with a topic:
validates :topic_id, :presence => true
#can also require that the referenced topic itself be valid
#in order for the post to be valid:
validates_associated :topic
end
And
class Topic < ActiveRecord::Base
#relation between topics and post
has_many :posts
end
I am trying to create association between both of them.
I want multiple post corresponding to each topic
I have used nested routes
Rails.application.routes.draw do
# nested routes
resources :topics do
resources :posts
end
resources :userdetails
devise_for :users, :controllers => { :registrations => "registrations" }
My Post controller looks like
class PostsController < ApplicationController
# before_action :set_post, only: [:show, :edit, :update, :destroy]
before_filter :has_userdetail_and_topic, :only =>[:new, :create]
# GET /posts
# GET /posts.json
#for new association SAAS book
protected
def has_userdetail_and_topic
unless(#topic =Topic.find_by_id(params[:topic_id]))
flash[:warning] = 'post must be for an existing topic'
end
end
public
def new
#post = #topic.posts.build
###topic = Topic.find(params[:topic_id1])
end
def index
#posts = Post.all
end
# GET /posts/1
# GET /posts/1.json
def show
end
# GET /posts/new
# GET /posts/1/edit
def edit
end
# POST /posts
# POST /posts.json
def create
##topic.posts << #post
##current_user = current_user.id
#current_user.posts << #topic.posts.build(params[:post])
##post = Post.new(post_params )
##post.userdetail_id = current_user.id
#Association functional between topic and post
#Class variable used
###topic.posts << #post
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render :show, status: :created, location: #post }
else
format.html { render :new }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /posts/1
# PATCH/PUT /posts/1.json
def update
respond_to do |format|
if #post.update(post_params)
format.html { redirect_to #post, notice: 'Post was successfully updated.' }
format.json { render :show, status: :ok, location: #post }
else
format.html { render :edit }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# DELETE /posts/1
# DELETE /posts/1.json
def destroy
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url, notice: 'Post was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_post
#post = Post.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def post_params
params.require(:post).permit(:topic_id,:issue, :description, :rating, :userdetail_id)
end
end
I am trying to navigate from topics/index via code <td><%= link_to 'Write', new_topic_post_path(#topic) %> </td>
but when i try to go at localhost:3000/topics]
I am getting error
No route matches {:action=>"new", :controller=>"posts", :topic_id=>nil} missing required keys: [:topic_id]
Can any body tell me about this error, as i am new to rails please clearly specify answer.
And I have one more doubt, please tell me if i am doing association between topic and post incorrectly.I have confusion about this line of code -
#topic.posts << #post
What the error missing required keys: [:topic_id] is telling you is that you need to provide a hash with the key topic_id:
<%= link_to 'Write', new_topic_post_path(topic_id: #topic) %>
Passing a resource as to a route helper only works for the id param:
<%= link_to #topic, topic_path(#topic) %>
Is a kind of shorthand for:
<%= link_to #topic, topic_path(id: #topic.to_param) %>
Addition:
#prcu is also correct. The #topic record needs to be saved to the database. Records which are not saved do not have an id since the database assigns the id column when the record is inserted.
You also need to set the #topic instance variable in PostsController:
#topic = Topic.find(params[:id])
This is commonly done with a before filter:
before_filter :set_topic, only: [:new]
def set_topic
#topic = Topic.find(params[:id])
end
The same also need to be done in TopicsController#index.
#topic is not set or it's not persisted. You can not use topic not saved to db in this helper.
Forgive my ignorance but I am quite new to RoR. I am working on a project where users are able to duplicate a post in order to edit this "cloned version" and to save it (with a new post id, of course).
First I tried to use the Amoeba gem described like here, but I failed.
Then I thought I found a better solution - Duplicating a record in Rails 3 - but when I am integrating the suggested code, I am receiving the following error:
NoMethodError in Posts#show
undefined method `clone_post_path' for #<#:0x0000010267b8c8>
Researching and tinkering for hours now, I would really appreciate any help!
I am using Rails 3.2.13.
In my posts_controller I have the following code:
def show
#post = Post.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #post }
end
end
def new
#post = current_user.posts.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #post }
end
end
def clone
#post = current_user.posts.find(params[:id]) # find original object
#post = current_user.posts.new(#post.attributes) # initialize duplicate (not saved)
render :new # render same view as "new", but with #post attributes already filled in
end
def create
#post = current_user.posts.new(params[:post])
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render json: #post, status: :created, location: #post }
else
format.html { render action: "new" }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
This is the post.rb model:
class Post < ActiveRecord::Base
attr_accessible :content, :title, :videos, :link, :description
validates :title, presence: true
belongs_to :user
end
In the show view I call this:
<%= link_to 'Create a clone', clone_post_path(#post) %>
What am I doing wrong?
Thank you so much in advance for any help!
UPDATE:
Adding
resources :posts do
get 'clone', on: :member
end
to the routes file worked.
Here is the routes file:
Tt::Application.routes.draw do
devise_for :users
get 'about' => 'pages#about'
resources :posts
root :to => 'pages#home'
post 'attachments' => 'images#create'
resources :posts do
get 'clone', on: :member
end
end
Unfortunately afterwards a new error occurred:
ActiveModel::MassAssignmentSecurity::Error in PostsController#clone
Can't mass-assign protected attributes: id, created_at, updated_at, image_file_name, image_content_type, image_file_size, image_updated_at, file, user_id
Make sure that your routes file includes the following:
resources :posts do
get 'clone', on: :member
end
Since the clone action is not a standard action you must account for it in your routes file so it knows what to do.