Hello Stackoverflow programmer's,
I'm currently running a small side-project next to my school's project but i'm quite stuck for a while now.
Here's my problem: I have a user with some standerd attributes and that user has_one :spec. A spec is sort of an extended version for a profile.
Now this spec has as i mentioned some extra attributes, but i can't seem to assign it correctly to the current user. The strange thing is i am able to make a spec in the console.
Here's my Spec_controller:
class SpecController < ApplicationController
def index
# redirect_to :controller => "user", :action => "index"
# #user.spec = #spec.new
end
def update
#user = User.find(current_user.id)
#spec.user_id = current_user.id
#spec = current_user.spec(spec_params)
#spec = Spec.find(params[:id])
if #spec.update(params.require(:spec).permit(:first_name, :last_name, :gender, :birthdate, :occupation, :city, :state))
flash[:notice] = "Changes saved."
redirect_to users_path
else
flash[:notice] = "er ging iets mis!"
render 'edit'
end
end
def edit
#user = User.find(session[:user_id])
#spec = Spec.new
end
def new
#user = User.find(session[:user_id])
#spec = Spec.new
end
private
def spec_params
params.require(:spec).permit(:first_name, :last_name, :gender, :birthdate, :occupation, :city, :state)
end
def user_params
params.require(:user).permit(:screen_name, :email, :password)
end
end
The Spec model:
class Spec < ActiveRecord::Base
belongs_to :user
ALL_FIELDS = %w(first_name last_name occupation gender birthdate city state zip_code)
STRING_FIELDS = %w(first_name last_name occupation city state)
VALID_GENDERS = ["Male", "Female"]
START_YEAR = 1900
VALID_DATES = DateTime.new(START_YEAR)..DateTime.now
validates_inclusion_of :gender, in: VALID_GENDERS, allow_nill: false, message: "Must be male or female"
validates_inclusion_of :birthdate, in: VALID_DATES, allow_nill: false, message: "Is invalid"
end
The user model:
class User < ActiveRecord::Base
has_one :spec
validates_presence_of :email, :screen_name
validates_uniqueness_of :email, :screen_name
has_secure_password
extend FriendlyId
friendly_id :screen_name, use: :slugged
end
And the edit view in case if it's needed:
<%= form_for :spec do |form| %>
<fieldset>
<legend><%= #title %></legend>
<%= text_field_tag form, "first_name" %>
<%= text_field_tag form, "last_name" %>
<div class="form_row">
<label for="gender">Gender:</label><br>
<%= radio_button :spec, :gender, "Male" %> Male
<%= radio_button :spec, :gender, "Female" %> Female
</div>
<div class="form_row">
<label for="birthdate">Birthdate:</label><br>
<%= date_select :spec, :birthdate, :start_year => Spec::START_YEAR, :end_year => Time.now.year, :include_blank => true, :order => [:month, :day, :year] %>
</div>
<%= text_field_tag form, "occupation" %>
<%= text_field_tag form, "city" %>
<%= text_field_tag form, "state" %>
</br>
<%= submit_tag "Update", :class => "submit", :controller => :spec, :action => :update %>
</fieldset>
<% end %>
Routes file:
Rails.application.routes.draw do
resources 'spec'
#post ':controller(/:spec(/:id(.:edit)))'
get 'spec/index'
get 'spec/edit'
get 'profile/index'
get 'profile/show'
get 'profile/:screen_name', controller: 'profile', action: 'show'
resources :users, only: [:index, :new, :create, :login, :destroy, :edit, :show, :update]
controller :sessions do
get :login, to: 'sessions#new'
delete :logout, to: 'sessions#destroy'
post :authenticate, to: 'sessions#create'
end
root 'site#index'
get 'site/index'
get 'site/about'
get 'site/help'
end
I hope my code is readable, still trying to figure out alot of stuff inside of rails (first programming language).
Thanks!
Finnaly got it to work! Ive made a new def in Spec controller instead of update i made a create function. Kinda strange in the first place why i made it a update function when you want to create a complete new Spec!
Thanks alot for your help :) much appreciated.
try
#spec = current_user.spec
if #spec.update(spec_params)
...
Update:
I think the link to update spec will be something like this /specs/1/edit, getting and updating spec should be
#spec = Spec.find(params[:id])
if #spec.update(spec_params)
...
Related
I got a problem which I can't solve. I made two models; one model called Film is the parent model and another model called Review is the child model. I got validation conditions on the child model but it does not display on the view.
Film model
class Film < ApplicationRecord
has_many :reviews
validates_presence_of :filmtitle, presence: true
validates_presence_of :filmdescription, presence: true
validates_presence_of :filmdirector, presence: true
validates_presence_of :filmrating, presence: true
validates_presence_of :filmstarname, presence: true
end
Review model
class Review < ApplicationRecord
validates :rating, presence: true
validates :commenter, presence: true
validates :body, presence: true
belongs_to :film
end
Review Controller
class ReviewsController < ApplicationController
def create
#film = Film.find(params[:film_id])
#review = #film.reviews.create(review_params)
redirect_to film_path(#film)
end
private
def review_params
params.require(:review).permit(:commenter, :body, :rating)
end
end
Film show.html.erb
<% if #film.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#film.errors.count, "error") %></h2>
<ul>
<% #film.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= form_for([#film, Review.new]) do |f| %>
<p>
<%= f.label :commenter %><br>
<%= f.text_field :commenter, :placeholder => 'Your name' %>
</p>
<p>
<%= f.label :body %><br>
<%= f.text_area :body, :placeholder => 'Your comment' %>
</p>
<p>
<%= f.label :rating %><br>
<%= f.select :rating, ['1 Star', '2 Stars', '3 Stars', '4 Stars', '5 Stars'] %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
Film Controller
class FilmsController < ApplicationController
before_action :set_film, only: [:show]
# GET /films
# GET /films.json
def index
#films = Film.all.paginate(page: params[:page], per_page: 30)
#reviews = Review.new
end
# GET /films/1
# GET /films/1.json
def show
end
# GET /films/new
def new
end
# GET /films/1/edit
def edit
end
# POST /films
# POST /films.json
def create
end
# PATCH/PUT /films/1
# PATCH/PUT /films/1.json
def update
end
# DELETE /films/1
# DELETE /films/1.json
def destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_film
#film = Film.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def film_params
params.require(:film).permit(:filmtitle, :filmdescription)
end
end
route.rb
Rails.application.routes.draw do
resources :films do
resources :reviews
end
resources :rentals
resources :buys
resources :admin
resources :adminrentals
resources :adminfilms
resources :logins
resources :admins_login
resources :games
get '/adminCool' => 'admins_login#index'
get '/adminlogin' => 'admins_sessions#new'
post '/adminlogin' => 'admins_sessions#create'
get '/adminlogout' => 'admins_sessions#destroy'
get '/adminsignup' => 'admins#new'
post '/admins' => 'admins#create'
get '/login' => 'sessions#new'
post '/login' => 'sessions#create'
get '/logout' => 'sessions#destroy'
get '/signup' => 'users#new'
post '/users' => 'users#create'
get '/cool' => 'logins#index'
end
Please try
def create
#film = Film.find(params[:film_id])
#review = #film.reviews.new(review_params)
if #review.save
redirect_to film_path(#film)
else
render "films/#{#film.id}"
end
end
I have a recipient and category model. It's a simple association of 1 category has many recipients. When I try to update the recipient form and assign a category, it won't save to the record. If I use the console and update a record manually, e.g. Recipient.update(9, category_id: 13), I see the correct category assigned to the recipient but when I try to edit/update the record, it won't save to the new chosen category.
Here is my recipient model
class Recipient < ActiveRecord::Base
belongs_to :category
accepts_nested_attributes_for :category
end
Here is my category model
class Category < ActiveRecord::Base
has_many :recipients
validates :category, presence: true
default_scope { order('category')}
end
here is the recipient controller
class RecipientsController < ApplicationController
def index
#recipients = Recipient.order(:recipient_name).page(params[:page])
end
def new
#recipient = Recipient.new
end
def show
#recipient = Recipient.find(params[:id])
end
def create
#recipient = Recipient.new(recipient_params)
if #recipient.save
redirect_to recipients_path
else
render :new
end
end
def edit
#recipient = Recipient.find(params[:id])
end
def update
#recipient = Recipient.find(params[:id])
recipient_params = params.require(:recipient).permit(:recipient_name, :alternate_name, :description, :city, :state, :country, category_attributes: [:category, :id])
#recipient.update_attributes(recipient_params)
redirect_to recipient_path(id: #recipient.id)
end
def destroy
#recipient = Recipient.find(params[:id])
#recipient.destroy
redirect_to recipients_path
end
private
def recipient_params
params.require(:recipient).permit(:recipient_name, :alternate_name, :description, :city, :state, :country, product_attributes: [:product_name, recipient_products: [:recipient_id, :product_id]], channel_attributes: [:channel_name, recipient_channels: [:recipient_id, :channel_id]], category_attributes: [:id, :category])
end
end
here is the edit view
<div class="row">
<div class="col-sm-6">
<h2>Edit <%= #recipient.recipient_name %></h2>
<%= simple_form_for #recipient do |form| %>
<%= form.error_notification %>
<%= form.input :recipient_name, placeholder: 'Recipient', label: 'Recipient Name' %>
<%= form.input :alternate_name, placeholder: 'Alternate Name' %>
<%= form.association :category, label_method: :category, value_method: :id %>
<%= form.input :description, placeholder: 'Description'%>
<%= form.input :city, placeholder: 'City'%>
<%= form.input :state, placeholder: 'State' %>
<%= form.input :country, as: :country, priority: ['US', 'CA'] %>
<%= form.button :submit, 'Update Recipient', {:class=>"btn btn-secondary"} %>
<%= link_to "Cancel", :back, {:class=>"btn btn-default"} %>
<% end %>
</div>
</div>
and here is my routes.rb file
Rails.application.routes.draw do
root to: 'home#index'
resources :media_points
resources :products
resources :channels
resources :recipients
resources :media_point_products
resources :distributions
resources :categories do
resources :recipients
end
get '/listing' => "listing#index"
devise_for :admins
devise_for :users
resources :users
end
I wrote it as an answer since the format is a pain in comments:
Change your recipient_params private method from:
category_attributes: [:category, :id]
to
category_id: param_that_has_category_id_here
Also, you have two routes for recipients and it is going to utilize the first matching route which is not the nested recipients in categories route you have further down. If the first change in your private method didn't fix it I would specify the nested situation in simpleform doing as follows since you are probably looking to use the nested route:
simple_form_for [#category, #recipient], url: 'nested_route_path_here' do |f|
Just add #category = Category.new to your new action in the recipients controller and then in your create action you'll have the category param sent via params[:category_id]
/What does the .1 mean in ../users/1/profile.1? In editing an associated model in a one to one relationship, such as a user has one profile; it updates and redirected to ..users/user_id/profile.# instead of ../users/user_d/profile.
In the form_for, i used form_for [:user, #profile] to cover for the namespace through nested resources, but i don't understand why the .#. In an attempt to see if the link will cause my program to break, i clicked home (to take me back to my root page, basically reloading the profile as i had programmed for a logged in user), it reverts back to ../users/user_d/profile.
Using a debugging gem i get:
--- !ruby/hash:ActionController::Parameters
action: show
controller: profiles
user_id: '1'
format: '1'
What is format: '1'? Any explanation appreciated.
Adding my Code
USER.RB
class User < ActiveRecord::Base
attr_accessor :remember_token
before_save {self.email = email.downcase }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format:{with: VALID_EMAIL_REGEX},
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
has_one :profile, dependent: :destroy
accepts_nested_attributes_for :profile
end
PROFILE.RB
class Profile < ActiveRecord::Base
validates :name, presence: true, length: { maximum: 50 }
validates :street, :city, :state, :zipcode, presence: true
belongs_to :user
end
Their controllers
USER CONTROLLER
class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
def new
#user = User.new
#profile = #user.build_profile
end
def create
#user = User.new(user_params)
if #user.save
log_in #user
flash[:success] = "Welcome to the Mini Olympics"
redirect_to user_profile_path(current_user, #profile)
else
render 'new'
end
end
def show
#user = User.find(params[:id])
end
def edit
# Commented out the code, as its redundant due to the line 'before_action :correct_user'
# #user = User.find(params[:id])
end
def update
# Commented out first line of the code, as its redundant due to the line 'before_action :correct_user'
# #user = User.find(params[:id])
if #user.update_attributes(user_params)
flash[:success] = "profile updated"
#redirect_to #user
redirect_to user_profile_path(current_user, #profile)
else
render 'edit'
end
end
def index
#users = User.paginate(page: params[:page], per_page: 15)
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to users_url
end
private
def user_params
params.require(:user).permit(:id, :email, :password, :password_confirmation, profile_attributes: [:name,
:street, :city, :state, :zipcode] )
end
# Before filters
# Confirms a logged-in user.
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
# Confirms the correct user.
def correct_user
#user = User.find(params[:id])
redirect_to(root_url) unless current_user?(#user) # '#user == current_user' = 'current_user?(#user)'
end
# Confirms an admin user.
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
PROFILE CONTROLLER
class ProfilesController < ApplicationController
def edit
#profile = User.find(params[:user_id]).profile
end
def show
#profile = User.find(params[:user_id]).profile
end
def update
#profile = User.find(params[:user_id]).profile
if #profile.update_attributes(profile_params)
flash[:success] = "profile updated"
redirect_to user_profile_path(current_user, #profile)
else
render 'edit'
end
end
private
def profile_params
params.require(:profile).permit(:id, :name, :street, :city, :state, :zipcode)
end
end
Profile edit form
<% provide(:title, "Edit Profile") %>
<h1>Update your profile</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for [:user, #profile] do |f| %>
<%= render 'fields', f: f %>
<%= f.submit "Save changes", class: "btn btn-primary" %>
<% end %>
</div>
</div>
APP/VIEWS/PROFILES/_FIELDS.HTML.ERB
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :street %>
<%= f.text_field :street, class: 'form-control' %>
<%= f.label :city %>
<%= f.text_field :city, class: 'form-control' %>
<%= f.label :state %>
<%= f.text_field :state, class: 'form-control' %>
<%= f.label :zipcode %>
<%= f.text_field :zipcode, class: 'form-control' %>
ROUTES FOLDER
Rails.application.routes.draw do
root 'static_pages#home'
get 'help' => 'static_pages#help'
get 'about' => 'static_pages#about'
get 'contact' => 'static_pages#contact'
get 'signup' => 'users#new'
get 'login' => 'sessions#new'
post 'login' => 'sessions#create'
delete 'logout' => 'sessions#destroy'
resources :users do
resource :profile, only: [:show, :edit, :update ]
end
end
Usually the point following a dot at the end of a url is the format, for instance.
/users/12.html
/users/12.js
/users/12/profiles.xml
It looks like you've got a mal-formed url being generated somewhere which is passing the ID in as the format, as well as the id parameter.
That's the explanation, I'm not sure how to get rid of it without a little more information.
What does the users and profiles controllers look like in your routes file?
What does the link_to or url_for or *_url or *_path which generated this link look like?
Although my best guess is that you could just do form_for(#profile) to tidy this up. Then redirect in your create or update method to users_profiles_path(#user, #profile)
Update:
I put part of your routes file into a new rails app and got these routes
edit_user_profile GET /users/:user_id/profile/edit(.:format) profiles#edit
user_profile GET /users/:user_id/profile(.:format) profiles#show
PATCH /users/:user_id/profile(.:format) profiles#update
PUT /users/:user_id/profile(.:format) profiles#update
I missed the fact that you used resource instead of resources, so that each user has only one profile.
In the redirect, use user_profile_path(#user), you don't need to pass in the profile, the path only has one id in it, and that's the user_id.
the "dot something" at the end of a route indicates the format you want to get.
So if you type profile.json, rails will know you want json answer and will render accordingly in the controller (if this one is supported).
Other have answered about the format.
I am currently using Rails 5.1.5 and experienced similar situation. However, once I removed the instance variables that I was passing, the ids did not append to the url and you can still access them in the views.
user_profile_path(current_user, #profile)
To
user_profile_path
I am very new to rails and am attempting to get some practice by building a profile page. I have implemented devise and once a user has signed in they can click on a link that displays a profile with information about them. Right now it is just displaying their name, date of birth, and gender. I would like for it to also display an image of them. So I have bundled the paperclip gem and read through some tutorials to try and get it configured. I have gotten the code in place, so that a user can click on 'choose file' and select an image. However when I click 'update' it provides me with an error. I'd like for the image to display on the same show page of their profile.
profiles_controller.rb
class Users::ProfilesController < ApplicationController
def edit
#profile = Profile.create(params[:photo])
end
def create
end
private
def profile_params
params.require(:profile).permit(:photo)
end
end
profiles/edit.html.erb
<h2>Edit Account Profile </h2>
<%= form_for(#profile, url: profiles_edit_path(#profile), html: { :multipart => true }) do |f| %>
<li>
<%= f.label :photo, "Photo" %>
<%= f.file_field :photo %>
</li>
<div class="actions">
<%= f.submit "Update" %>
</div>
<% end %>
<%= link_to "Back", :back %>
registrations/show.html.erb
<%= image_tag #profile.photo.url %>
<br>
<%= #user.email %>
<br>
<%= #user.first_name %>
<%= #user.last_name %>
<br>
<%= #user.date_of_birth %>
<br>
<%= #user.is_female %>
registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
def show
#user = User.find(params[:id])
end
private
def sign_up_params
params.require(:user).permit(:first_name, :last_name, :is_female, :date_of_birth, :email, :password, :password_confirmation)
end
def account_update_params
params.require(:user).permit(:first_name, :last_name, :is_female, :date_of_birth, :email, :password, :password_confirmation, :current_password)
end
end
routes.rb
Rails.application.routes.draw do
devise_for :users, :controllers => { :registrations => 'users/registrations', :profiles => 'users/profiles' }
devise_scope :user do
get 'profiles/edit' => 'users/profiles#edit'
post 'profiles/edit' => 'users/profiles#edit'
get 'users/:id' => 'users/registrations#show', as: :user
end
root to: "home#index"
end
Error after clicking update:
Routing Error
No route matches [PATCH] "/profiles/edit.21"
I appreciate any assistance, and please let me know if any further information is needed.
Try:
patch 'profiles/:id' => 'users/profiles#update'
and form helper should like this:
<%= form_for(#profile, html: { :multipart => true }) do |f| %>
Hope it is helpful for you!!
try adding this route to devise_scope to pass that error
patch 'profiles/edit' => 'users/profiles#edit'
btw, in ProfilesController
creating new Profile should be in create action
in edit action, you should load existed profile and render edit view
Update:
form in edit view will submit data to action update, we need get profile again and update data for it
your routes is not correct, when edit a profile, we should submit to a url look like users/profiles/4/update
you can declare routes as below
devise_for :users, :controllers => { :registrations => 'users/registrations' }
namespace :users do
resources :profiles
end
In update action, you retrieve that profile p = Profile.find(params[:id]) and update it p.photo = params[:photo]; p.save or anything you want
I know that you should use "attr_accessible" to allow mass-assign protection, and that's actually what I do in my user model. How come it still complains?
Can't mass-assign protected attributes: email, password
user controller:
class UsersController < ApplicationController
def index
#users = User.new
end
def show
#user = User.find(2)
end
def login
#user = Project.new(params[:user])
...
end
end
user model:
class User < ActiveRecord::Base
has_and_belongs_to_many :projects
belongs_to :project
belongs_to :ticket
# Setup accessible (or protected) attributes for your model
attr_accessible :email, :password
end
the view, _navigation.html.erb:
<%= form_for("user", :url => login_users_path, :html => { :method => :post }) do |f| %>
<%= f.label :email%>
<%= f.text_field(:email, :size => 30, :class => 'login_field', :placeholder => 'Användarnamn')%>
<%= f.label :password%>
<%= f.text_field(:password, :size => 30, :class => 'login_field', :placeholder => 'Lösenord')%>
<%= f.submit "Logga in", :class => 'login_submit btn btn-primary' %>
<% end %>
from config/routes.rb:
resources :users do
post :login, on: :collection, as: :login
end
Because I'm using the collection above it should (as far as I understand) be the correct control / model (user) that I'm using?
I think it should be User not Project
def login
#user = User.new(params[:user])
...
end
You should also change your index action to:
def index
#users = Users.all
end
And show action to:
def show
#user = User.find(params[:id])
end