Paperclip has_one association - ruby-on-rails

I have a User model with a has_one :image relationship to an Image model. I want each user to have a photo but I want the photos to be stored in a model separate from the User model, namely the Image model.
I am using paperclip to help with adding an image.
However when I try to create a new user through my view I get the following error:
ActiveModel::MassAssignmentSecurity::Error in UsersController#create
Can't mass-assign protected attributes: image_attributes
I don't have a image_attributes variable. Why is this happening? Is my implementation flawed? I want a separate table to hold the images, because in the future I wish users to have many images.
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>
<%= form_for #user do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :first_name %>
<%= f.text_field :first_name %>
<br>
<%= f.label :last_name %>
<%= f.text_field :last_name %>
<br>
<%= f.label :email %>
<%= f.text_field :email %>
<br>
<%= f.fields_for :image, :html => {:multipart => true} do |asset| %>
<%= asset.label :photo %>
<%= asset.file_field :photo %>
<% end %>
<br>
<%= f.label :password %>
<%= f.password_field :password %>
<br>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation %>
<br>
<%= f.submit "Create my account"%>
<% end %>
User Controller
class UsersController < ApplicationController
#calls method signed_in_users.
before_filter :signed_in_user, only: [:index, :edit, :update, :show]
#ensures only the correct user can modify their own data
before_filter :correct_user, only: [:edit, :update]
def new
#user = User.new
##user.image.build
#user.build_image
end
def show # personal profile page
#Method to make sure only the signed in user can edit their information
if User.id_equals_cookie(params[:id], cookies[:remember_token])
#user = User.find(params[:id])
else
redirect_to root_url
end
end
def create
#user = User.new(params[:user])
if #user.save
sign_in #user #method defined in sessions_helper
flash[:notice] = "Thank you for signing up." #the view template must be configured to be seen.
redirect_to #user #does user_path work???
else
render 'new' #flash[:warning]
end
end
def edit
end
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
flash[:success] = "Profile updated"
sign_in #user
redirect_to #user
else
render 'edit'
end
end
def index
end
private
#should this be in the users_helper.rb file
def signed_in_user
redirect_to signin_url, notice: "Please sign in." unless signed_in?
end
def correct_user
#user = User.find(params[:id])
redirect_to(root_path) unless current_user?(#user) #current_user is defined in the sessions_helper.rb
end
end
User Model
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# email :string(255)
# created_at :datetime not null
# updated_at :datetime not null
# password_digest :string(255)
# remember_token :string(255)
# first_name :string(255)
# last_name :string(255)
# full_name :string(255)
# birthdate :date
#
class User < ActiveRecord::Base
has_one :image, :dependent => :destroy #images?
accepts_nested_attributes_for :image #images?
attr_accessible :email, :first_name, :last_name, :full_name, :birthdate, :password, :password_confirmation
#magic to require a password, make sure passwords match, authenticate
has_secure_password
before_save { |user| user.email = email.downcase } #help ensure uniqueness
before_save :create_remember_token
#validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: {case_sensitive: false}
validates :password, presence: true, length: { minimum: 6 }
validates :password_confirmation, presence: true
def self.id_equals_cookie(id, cookie)
#user_of_cookie = find_by_remember_token(cookie)
#user_of_id = find(id)
if #user_of_cookie == nil
false
elsif #user_of_id == #user_of_cookie
true
else
false
end
end
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
Image Model
# == Schema Information
#
# Table name: images
#
# id :integer not null, primary key
# created_at :datetime not null
# updated_at :datetime not null
# photo_file_name :string(255)
# photo_content_type :string(255)
# photo_file_size :integer
# photo_updated_at :datetime
# user_id :integer
#
require 'paperclip'
class Image < ActiveRecord::Base
belongs_to :user
has_attached_file :photo
attr_accessible :photo, :photo_file_name, :photo_content_type, :photo_file_size, :photo_updated_at
end

write this in your model
attr_accessible :image_attributes

just do attr_accessible :photo_attributes

Related

Rails nested fields when associating User to Roles model

I'm having some issues with the nested field here. I've used nested fields in other views/controllers without issue.
I'm trying to associate a role to the user table from the roles table.
My role model looks like this:
class Role < ApplicationRecord
has_many :users
end
My user model has this:
class User < ApplicationRecord
belongs_to :role, optional: true
accepts_nested_attributes_for :role
...
The reason why it's set to optional is because current users don't yet have a role, and I need to apply it to those first (there are only two users in production at the moment so that's fine)
My user controller is like this for the permitted attributes and update
class Admin::UsersController < ApplicationController
before_action :set_user, only: [:edit, :update, :destroy]
...
def edit
end
def update
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to admin_users_url, notice: 'User Account was successfully updated.' }
else
format.html { render :edit }
end
end
end
private
def user_params
params.require(:user).permit(:first_name, :last_name, :email, :password, roles_attributes: [:name])
end
def set_user
#user = User.find(params[:id])
end
end
And the form to update the user roles:
.container.p-4
%h1 Edit User Information
= form_for([:admin, #user]) do |f|
- if #user.errors.any?
#error_explanation
%h2
= pluralize(#user.errors.count, "error")
prohibited this event from being saved:
%ul
- #user.errors.each do |error|
%li= error.full_message
.row.mb-4
.col
= f.label :first_name, "First Name"
= f.text_field :first_name, class: "form-control border border-dark"
.col
= f.label :last_name, "Last Name"
= f.text_field :last_name, class: "form-control border border-dark"
.form-group.mb-4
= f.label :email, "Email Address"
= f.email_field :email, class: "form-control border border-dark"
%h2 User Role
.form-group.mb-4
= f.fields_for :roles do |f|
= f.check_box :name, checked: false, value: "admin"
= f.label :name, "Admin"
.form-group.p-4.text-center
= f.submit
When I hit update after checking "Admin", the terminal readout is that :roles is unpermitted.
I have a seperate Role controller that allows me to define the roles to associate users to. the Roles table only has name:string and user:references.
So I'm not sure why it's not being permitted.
What you actually want here is a join table to avoid denormalization:
class User < ApplicationRecord
has_many :user_roles
has_many :roles, through: :user_roles
accepts_nested_attributes_for :roles
end
class UserRole < ApplicationRecord
belongs_to :user
belongs_to :role
end
class Role < ApplicationRecord
validates :name, uniqueness: true
has_many :user_roles
has_many :users, through: :user_roles
end
This will let you assign multiple users to a role without duplicating the string "admin" for example for each row and risking the denormalization and bugs that can occur if one row for example contains "Admin" instead. You would assign roles from an existing list to users with:
<% form_for([:admin, #user]) do |form| %>
<div class="field">
<%= form.label :role_ids, 'Roles' %>
<%= form.collection_select(:role_ids, Role.all, :id, :name, multiple: true) %>
</div>
# ...
<% end %>
def user_params
params.require(:user)
.permit(
:first_name, :last_name, :email, :password,
role_ids: []
)
end
If you REALLY want to be able to create new roles on the fly while creating users you can use nested attributes. I would really just use AJAX instead through as it lets you handle the authorization logic in a seperate controller. You might want to consider that you might want to let some users assign roles but not invent new role definitions.
sticking with role ids and using bootstrap selectpicker the following worked as well and even preselects already set role
= f.select :role_ids, options_for_select(Role.all.map{|role| [role.name, role.id]}, #user.role_ids), {}, {:multiple => true, inlcude_blank: false, class: "form-control input-sm selectpicker"}
and controller:
module Backend
class UsersController < ApplicationController
before_action :set_user, only: %i[edit update]
def index
#users = User.all
end
def edit
#user.roles.build unless #user.roles.any?
end
def update
if #user.update user_params
redirect_to backend_users_path(#user), notice: 'Rollen erfolgreich aktualisiert'
else
render :edit
end
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:id, role_ids: [])
end
end
end

Rails 5. Association one to one, updating record create new one

Model User:
class User < ApplicationRecord
has_one :address, foreign_key: :user_id
accepts_nested_attributes_for :address
end
Model Address
class Address < ApplicationRecord
belongs_to :user, optional: true
end
Controller User, everything happen here
class UsersController < ApplicationController
def home # method which I use to display form
#user = User.find_by :id => session[:id]
end
def update # method for updating data
#user = User.find(session[:id])
if #user.update(user_params)
flash[:notice] = "Update successfully"
redirect_to home_path
else
flash[:error] = "Can not update"
redirect_to home_path
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, images_attributes: [:image_link, :image_description], address_attributes: [:city, :street, :home_number, :post_code, :country])
end
end
Updating form:
<%= form_for #user, :html => { :id => "update-form", :class => "update-form"} do |f| %>
<%= f.text_field :name %>
<%= f.text_field :email %>
<%= f.fields_for :address do |a| %>
<%= a.text_field :city %>
<%= a.text_field :street %>
<%= a.number_field :home_number %>
<%= a.text_field :post_code %>
<%= a.text_field :country %>
<% end %>
<%= f.submit %>
<% end %>
When I submitting my form, it shows me everything is fine, I mean "Update successfully", but in database its looks like new record is added to address table, but user table is updated properly. Can someone give me explanation why? I am looking answers in google but nothing helps me.
When I submitting my form, it shows me everything is fine, I mean
"Update successfully", but in database its looks like new record is
added to address table, but user table is updated properly. Can
someone give me explanation why?
This is due to the nature of strong params. It expects :id to be permitted for the nested_attributes to update properly, else a new record is created instead. Permit the :id and you are good to go.
def user_params
params.require(:user).permit(:name, :email, :password, images_attributes: [:id, :image_link, :image_description], address_attributes: [:id, :city, :street, :home_number, :post_code, :country])
end
try below code in your controller:
class UsersController < ApplicationController
def home # method which I use to display form
#user = User.find_by :id => session[:id]
end
def update # method for updating data
#user = User.find(session[:id])
if #user.update(user_params)
flash[:notice] = "Update successfully"
redirect_to home_path
else
flash[:error] = "Can not update"
redirect_to home_path
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password, images_attributes: [:image_link, :image_description], address_attributes: [:id, :city, :street, :home_number, :post_code, :country])
end
end

Rails allowing user signup only if invite code entered is valid?

So I'm creating a simple app for myself and a small group of people. I would like to restrict access to people that I hand-generate codes for by typing them in myself.
User sign up, log in, logout works, but I don't just want anyone to be able to be register.
TL;DR
User can sign up but how do I go about setting up a hand-generated code
Should be some way to invalidate that code after sign up and see who
the code is associated with
How can I do this in rails?
I know the user_model would have to add some sort of field to it, the view/form for it as well, and it would have to be validated (by the controller?). Just stuck.
My thought process is as follows (what i've thought so far)
All the invite codes should be kept as an array in a file in the
rails app?
I will have to add a migration that adds invite_code to the model/db/view form
The controller should validate the presence of the invite code in the view form?
user/new.html.erb
<%= form_for(#user) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :email %>
<%= f.email_field :email %>
<%= f.label :password %>
<%= f.password_field :password %>
<%= f.label :password_confirmation, "Retype Password" %>
<%= f.password_field :password_confirmation %>
<%= f.submit "Create My Account" %>
<% end %>
users_controller.rb
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
end
def new
#user = User.new
end
def create
#user = User.new(user_params)
if #user.save
log_in #user
flash[:success] = "Welcome!"
redirect_to #user
# Handle a successful save.
else
render 'new'
end
end
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
end
user.rb model
class User < ActiveRecord::Base
# links this to the question.rb model
has_many :questions, dependent: :destroy
attr_accessor :remember_token
before_save { email.downcase! }
validates :name, presence: true, length: { maximum: 50 }
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, length: { minimum: 6 }
# Returns the hash digest of the given string.
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# returns a random token
def User.new_token
SecureRandom.urlsafe_base64
end
# remembers a user in the database for use in persistent sessions
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
# returns true if the given token matches the digest
def authenticated?(remember_token)
return false if remember_digest.nil?
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
# Forgets a user.
def forget
update_attribute(:remember_digest, nil)
end
end
Well, In that case, you can have a model say Token created as below:
def self.up
create_table :tokens do |t
t.string :code, null: false
t.boolean :used, default: false
t.timestamps
end
end
And a migration in your User table for token_id
You can generate new code like, Token.generate_new_code
def self.generate_new_code
token = Token.new(code: Digest::SHA1.hexdigest Time.now.to_s)
token.code if token.save
end
And you can understand the rest i.e. accept code from registration form, validate for unused code and set it true and token_id if the registration is successful.

Nested form update action producing duplicate results

During the update action of a nested form, instead of updating the current nested records, it seems to create new nested records.
This is what my controller looks like :
class ApplicationsController < ApplicationController
before_filter :set_user_and_job
def new
job = params[:job_id]
#application = Application.build(job)
end
def create
#application = Application.new(application_params)
#application.save
redirect_to root_url, :notice => "You have now applied!"
end
def edit
#application = Application.find(params[:id])
#answers = []
#job.questions.each do |question|
#application.answers.each do |answer|
#answers << answer if answer.question_id == question.id
end
end
end
def update
#application = Application.find(params[:id])
#application.update_attributes(application_params)
redirect_to root_url, :notice => "You have updated your application!"
end
def destroy
Application.find(params[:id]).destroy
flash[:success] = "Application Deleted."
redirect_to root_url
end
def show
#application = Application.find(params[:id])
#answers = []
#job.questions.each do |question|
#application.answers.each do |answer|
#answers << answer if answer.question_id == question.id
end
end
end
private
def set_user_and_job
#user = current_user
#job = Job.find(params[:job_id])
end
def application_params
params.require(:application).permit(:job_id, :user_id, answers_attributes:[:question_id, :content]).merge(user_id: current_user.id, job_id: params[:job_id])
end
end
This is what my edit view looks like :
<% provide(:title, " Edit this application") %>
<div class="row">
<div class="span6">
<h2> Job: <%= #job.job_title %></h2>
<p> <%= #job.job_summary %> </p>
</div>
<div class="span6">
<h2> Applicant: <%= #user.name %></h2>
</div>
<div class="span12">
<h3>Edit your job application below.</h3>
</div>
</div>
<%= form_for [#job, #application] do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<% #job.questions.each_with_index do |question| %>
<%= f.fields_for :answers, question do |question_field| %>
<%= question_field.label :content, question.content %>
<%= question_field.text_area :content, :value => "" %>
<%= question_field.hidden_field :question_id, :value => question.id %>
<% end %>
<% end %>
<%= f.submit "Submit the application", class: "button" %>
<% end %>
The Application Model itself:
# == Schema Information
#
# Table name: applications
#
# id :integer not null, primary key
# user_id :integer
# job_id :integer
# created_at :datetime
# updated_at :datetime
#
class Application < ActiveRecord::Base
belongs_to :job
belongs_to :user
validates :job_id, presence: true
validates :user_id, presence: true
has_many :answers
accepts_nested_attributes_for :answers, :reject_if => lambda { |a| a[:content].blank? }, :allow_destroy => true
def self.build(job_id)
application = self.new
job = Job.find(job_id)
job.questions.count.times do
application.answers.build
end
application
end
end
And the Answer Model :
# == Schema Information
#
# Table name: answers
#
# id :integer not null, primary key
# application_id :integer
# question_id :integer
# created_at :datetime
# updated_at :datetime
# content :string(255)
#
class Answer < ActiveRecord::Base
belongs_to :question
belongs_to :application
validates :content, presence: true
end
From searching, I found this link, RoR nested attributes produces duplicates when edit , which suggests that I add the :id to application_params, however when I do that, I get the error
ActiveRecord::RecordNotFound in ApplicationsController#update
Couldn't find Answer with ID=5 for Application with ID=17
(That's also a bit weird, because the actual id of the answer is 39. 5 is actually the ID of the question :S )
What are your thoughts on this? Mentors of SO, help much appreciated :)
update_only does not work for has_many relationships. You need to add the nested attribute :id field to your strong parameters:
def application_params
params.require(:application).permit(:job_id, :user_id,
answers_attributes:[:id, :question_id, :content]).merge(user_id: current_user.id,
job_id: params[:job_id])
end
Try adding update_only to your call to accepts_nested_attributes_for.
accepts_nested_attributes_for :nested_attribute, update_only: true

Ruby on Rails Tutorial by M. Hartl. Chapter 10.2.1 - undefined local variable or method `micropost'

I keep getting the following error in chapter 10.2.1 - undefined local variable or method `micropost'. I'm trying to get all the posts to render on the show page.
Here is my users controller:
class UsersController < ApplicationController
before_filter :signed_in_user, only: [:index, :edit, :update, :show, :destroy]
before_filter :correct_user, only: [:edit, :update]
before_filter :admin_user, [:destroy]
def index
##users = User.all
#users = User.paginate(page: params[:page])
end
def new
#user = User.new
end
def destroy
#user = User.find(params[:id]).destroy
flash[:success] = "User has been deleted!"
redirect_to users_path
end
def show
#user = User.find(params[:id])
#microposts = #user.microposts.paginate(page: params[:page])
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
#good!
sign_in #user
flash[:success] = "Profile"
redirect_to user_path(#user)
else
render 'edit'
end
end
def create
#user = User.new(params[:user])
if #user.save
sign_in #user
flash[:success] = "Welcome to the Ruby app!"
redirect_to user_path(#user)
else
render 'new'
end
end
private
def signed_in_user
if !signed_in?
store_location
flash[:error] = "Please sign in!"
redirect_to signin_url
end
end
def correct_user
#user = User.find(params[:id])
if !current_user?(#user)
redirect_to '/'
end
end
def admin_user
if current_user.admin?
#nothing
else
redirect_to '/'
end
end
end
Here is my view
<% provide(:title, #user.name) %>
<div class="row">
<aside class="span4">
<section>
<h1>
<%= gravatar_for #user %>
<%= #user.name %>
</h1>
</section>
</aside>
<div class="span8">
<% if #user.microposts.any? %>
<h3>Microposts (<%= #user.microposts.count %>)</h3>
<ol class="microposts">
<li>
<span class="content"><%= micropost.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
</li>
</ol>
<%= will_paginate #microposts %>
<% end %>
</div>
</div>
Here is the micropost model:
# == Schema Information
#
# Table name: microposts
#
# id :integer not null, primary key
# content :string(255)
# user_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
class Micropost < ActiveRecord::Base
attr_accessible :content
belongs_to :user
validates :content, presence: true, length: { maximum: 140 }
validates :user_id, presence: true
default_scope order: 'microposts.created_at DESC'
end
Lastly, here is the users model:
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# name :string(255)
# email :string(255)
# created_at :datetime not null
# updated_at :datetime not null
# password_digest :string(255)
# remember_token :string(255)
# admin :boolean default(FALSE)
#
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_secure_password
has_many :microposts, dependent: :destroy
before_save { |user| user.email = email.downcase }
before_save :create_remember_token
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 6 }
validates :password_confirmation, presence: true
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
Please let me know if you'd like me to post anymore code. Thanks!
In your view you are not looping the #user.microposts, try something like:
<% if #user.microposts.any? %>
<h3>Microposts (<%= #user.microposts.count %>)</h3>
<ol class="microposts">
<% #user.microposts.each do |micropost| %>
<li>
<span class="content"><%= micropost.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
</li>
<% end %>
</ol>
<%= will_paginate #microposts %>
<% end %>
In your view I'm seeing that sometimes you are refering to micropost as:
micropost.content
and in other ones as a class attribute:
#micropost
You should check on that.
It looks like your view is missing a line that loops through the users microposts to display them. Add the line:
<% #microposts.each do |micropost| %>
Now the local variable micropost in your line <%= micropost.content %> will exist.
if you want to use the local variable (micropost) directly like micropost.content, then you have to make a partial (html.erb) file and call that file in the view with render method

Resources