Profile Model with Devise Users in Rails - ruby-on-rails

I'm trying to create a separate Profile model for Devise Users with things such as location, biography, blah, blah, blah. The problem is that I can't get it to save to the database.
My users are called "artists".
### /routes.rb ###
get 'artists/:id/new_profile' => 'artists/profiles#new', as: :profile
post 'artists/:id/new_profile' => 'artists/profiles#create'
### artists/profiles_controller.rb ###
class Artists::ProfilesController < ApplicationController
before_action :authenticate_artist!
def new
#artist = current_artist
#profile = ArtistProfile.new
end
def create
#artist = current_artist
#profile = ArtistProfile.new(profile_params)
if #profile.save
redirect_to current_artist
else
render 'new'
end
end
end
### /artist.rb ###
class Artist < ActiveRecord::Base
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable, :confirmable, :lockable, :timeoutable
has_one :artist_profile, dependent: :destroy
### /artist_profile.rb ###
class ArtistProfile < ActiveRecord::Base
belongs_to :artist
validates :artist_id, presence: true
end
### /views/artists/profiles/new.html.erb ###
<%= form_for(#profile, url: profile_path) do |f| %>
<div class="field">
<%= f.label :biography, "biography", class: "label" %>
<%= f.text_area :biography, autofocus: true , class: "text-field" %>
</div>
<div class="field">
<%= f.label :location, "location", class: "label" %>
<%= f.text_field :location, class: "text-field" %>
</div>
...
...
...
<div class="actions">
<%= f.submit "create profile", class: "submit-button" %>
</div>
<% end %>
What am I doing wrong here?

You need to initialise the profile using the current_artist object.
class Artists::ProfilesController < ApplicationController
before_action :authenticate_artist!
def new
#artist = current_artist
#profile = #artist.build_profile
end
def create
#artist = current_artist
#profile = #artist.build_profile(profile_params)
if #profile.save
redirect_to current_artist
else
render 'new'
end
end
end
Update:
To use this example your association should be like
class Artist < ActiveRecord::Base
has_one :profile, class_name: ArtistProfile
end

Make sure you set the artist_id for the profile before attempting to save.
#profile = ArtistProfile.new(profile_params, artist_id: #artist.id)
or
#profile = ArtistProfile.new(profile_params)
#profile.artist_id = #artist.id
should work.

In your controller you are missing the profile_params method.
private
def profile_params
params.require(:profile).permit(:biography, :location)
end

Related

Rails 6: Only one profile per user should be created

I'm currently working on a Rails 6 application. I have the following association. A User has a Profile and a Profile belongs to a User. When editing a profile for a user I ended up having two profiles for the user. I would like to have only one profile per user.
Edit form: profile/edit.html.erb
<%= form_for #profile do |f| %>
<div class="form-group">
<%= f.label :avatar %>
<%= f.file_field :avatar, as: :file, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :full_name, 'Full Name' %>
<%= f.text_field :full_name, autofocus: true, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :city, 'City' %>
<%= f.text_field :city, class: "form-control" %>
</div>
<div class="form-group">
<%= f.label :bio, 'Bio'%>
<p> Why did you join ArtsySpace?
What should other people here know about you?
</p>
<%= f.text_field :bio, class: "form-control"%>
</div>
<div class="form-group">
<%= f.submit "Edit profile", class: "btn btn-primary" %>
</div>
<% end %>
I see from the console that user 1 has 2 profiles. I'm not sure how a profile was created maybe I hit the create method from profile controller but mistake but I would like for this not to happen. Is there a validation for only one profile to belong to user?
class ProfilesController < ApplicationController
def new
#profile = current_user.build_profile
end
def create
#profile = current_user.create_profile(profile_params)
#profile.avatar.attach(params[:profile][:avatar])
if #profile.save
redirect_to #post
else
render 'new'
end
end
def show
#profile = Profile.find(params[:id])
end
def edit
#profile = current_user.profile
end
def update
#profile = current_user.profile
if #profile.update!(profile_params)
redirect_to #profile, notice: 'Profile was successfully updated.'
else
render :edit
end
end
def delete
#profile = current_user.profile.find(params[:id])
#profile.destroy
end
private
def profile_params
params.require(:profile).permit(:full_name, :city, :bio, :avatar)
end
end
I'm not sure if the issue comes from the way the routes are configured?
Rails.application.routes.draw do
devise_for :users
devise_scope :users do
resources :profiles, only: [:edit, :update]
end
resources :profiles, only: [:show]
resources :posts do
resource :comments, only: %i[show new create edit update]
end
end
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :posts
has_one :profile
accepts_nested_attributes_for :profile
end
From the below snippet you can see that a user has 2 profiles for user_id: 1
[#<Profile id: 3, user_id: 1, full_name: "steven ", city: "diego ", bio: "Because im ", created_at: "2019-06-12 23:11:49", updated_at: "2019-06-16 18:49:22">, #<Profile id: 4, user_id: 1, full_name: "andrew", city: "Tony", bio: "because i know ", created_at: "2019-06-12 23:12:35", updated_at: "2019-06-16 18:51:22">]
Not sure where the issue came from.
I once solved this by doing (I know it's strange, I'm just putting it out here as an idea)
has_one :profile
has_many :profiles
validates :profiles, length: { in: 0..1 }
https://guides.rubyonrails.org/active_record_validations.html#length
I decided to not to use a profile controller and just have one model User that can be used as the profile.

How to debug why an update won't work with Rails associations?

I'm trying to setup a simple rails app with job board functionality. I was able to add jobs to the database, until I added an association between my Job model and devise User model. Now it won't update the database when I fill out the form.
jobs_controller
class JobsController < ApplicationController
def index
#jobs = Job.all
end
def new
#job = Job.new
end
def listing
end
def listings
end
def create
#job = Job.new(params.require(:job).permit(:title, :description, :url, :user_id))
if #job.save
redirect_to root_path
else
render "new"
end
end
end
new.html.erb
<%= simple_form_for #job do |form| %>
<%= form.input :title, label: "Job title" %>
<%= form.input :description, label: "Description" %>
<%= form.input :url, label: "URL" %>
<%= form.button :submit %>
<% end %>
index.html.erb
<% #jobs.each do |job| %>
<div class="job">
<h2><%= link_to job.title, job.url %></h2>
<p><%= job.description %></p>
</div>
<% end %>
<p><%= link_to "Add a job", new_job_path %></p>
user.rb
class User < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :validatable
has_many :jobs
end
job.rb
class Job < ApplicationRecord
belongs_to :user
end
There isn't an error in the console, but the database doesn't seem to be updated or it's not updating the view.
I also ran a migration:
class AddUserToJob < ActiveRecord::Migration[5.2]
def change
add_reference :jobs, :user, foreign_key: true
end
end
You can get the user with current_user in Devise.
class JobsController < ApplicationController
# This restricts the actions to authenticated users and prevents a nil error
before_action :authenticate_user, except: [:show, :index]
# ...
def create
# this sets the user_id column
#job = current_user.jobs.new(job_params)
if #job.save
# you really should set a flash message or something to notify the user
# and possibly redirect to the show or index action instead
redirect_to root_path
else
render "new"
end
end
private
def job_params
params.require(:job)
.permit(:title, :description, :url, :user_id)
end
end
If you don't want to associate the job immediately to a user, you need to change the association to be optional, like:
class Job < ApplicationRecord
belongs_to :user, optional: true
end
Else you need to supply user_id in your form or set it in the controller action.
You should also delegate this part to a separate method
def job_params
params.require(:job).permit(:title, :description, :url, :user_id)
end
Job.new(job_params)

How can I make my polymorphic model work?

I've got two models User and Image as polymorphic association because I want my image model to reuse in other models.
class User < ApplicationRecord
has_one :cart
has_many :images, as: :imageable, dependent: :destroy
accepts_nested_attributes_for :images
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
before_validation :set_name, on: :create
validates :name, presence: true
private
def set_name
self.name = "person#{rand(1000)}" if self.name.blank?
end
end
class Image < ApplicationRecord
mount_uploader :image, ImageUploader
belongs_to :imageable, polymorphic: true
end
And I made Image polymorphic: true and use carrierwave gem for creating uploader `mount_uploader mount_uploader :image, ImageUploader in Image model:image
class ImageUploader < CarrierWave::Uploader::Base
end
and I permit :image parameters to each model: User and Good,
module Admin
class UsersController < BaseController
before_action :set_admin_user, only: [:show, :edit, :update, :destroy]
def users_list
#admin_users = User.all.preload(:images).where(admin: true)
end
def show
end
def edit
end
def update
if #user.images.update(admin_user_params)
redirect_to admin_users_list_path, notice: 'User was successfully updated'
else
flash[:alert] = 'User was not updated'
end
end
def destroy
end
private
def set_admin_user
#user = User.find(params[:id])
end
def admin_user_params
params.require(:user).permit(:name, :email, images_attributes: [:image])
end
end
end
In my view form I've got the next code:
<%= form_for [:admin, #user], html: { multipart: true } do |f| %>
<%= f.label 'Name', class: 'form-group' %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.fields_for :images_attributes do |i| %>
<%= i.label :image %>
<%= i.file_field :image %>
<% end %>
<%= f.label 'Email', class: 'form-group' %>
<%= f.text_field :email, class: 'form-control' %>
<%= f.submit class: 'btn btn-oultline-primary' %>
<% end %>
but when I want to update user for exampletry to upload the image I've got the next:
Here is what I have as response
I can't saveupload my image. Why is that? I expect to have an insert into db but it doesn't happen and in db I've got no attached images.
Since you are adding multiple images, change your form to:
<%= i.file_field :image, multiple: true, name: "images_attributes[image][]" %>
And in the controller:
def edit
#image = #user.images.build
end
def update
if #user.images.update(admin_user_params)
create_user_images
redirect_to admin_users_list_path, notice: 'User was successfully updated'
else
flash[:alert] = 'User was not updated'
end
end
private
def admin_user_params
params.require(:user).permit(:name, :email, images_attributes: [:id, :user_id, :image])
end
def create_user_images
if params[:images_attributes]
params[:images_attributes]['image'].each do |i|
#image = #user.images.create!(:image => i)
end
end
end
Let me know if you still have problems after the edits :)

#comments nil while #event.comments works in show partial

I cannot figure out why #comments is returning nil when I am attempting to loop through it. If I use #event.comments.each do instead it works just fine. My current structure is User / Events / Comments.
Comments Controller:
class CommentsController < ApplicationController
before_action :authenticate_user!, only: [:create, :destroy]
def create
#event = Event.find(params[:event_id])
#comment = #event.comments.create(comment_params)
#comment.user = current_user
if #comment.save
flash[:notice] = "Comment Added"
redirect_to #event
else
flash[:alert] = "Comment Not Added"
redirect_to #event
end
end
def show
#event = Event.find(params[:id])
#comments = #event.comments
end
def destroy
end
private
def comment_params
params.require(:comment).permit(:body)
end
end
Events Controller Show Action:
class EventsController < ApplicationController
before_action :authenticate_user!, only: [:new, :create,:edit, :update, :show,
:index, :destroy]
def show
#event = Event.find(params[:id])
end
private
def event_params
params.require(:event).permit(:start_date, :start_time, :location, :title, :description, :size, :difficulty,
:activity, :duration)
end
end
Comment Model:
class Comment < ActiveRecord::Base
belongs_to :event
belongs_to :user
validates :body, presence: true
scope :newest, -> { order("created_at DESC") }
end
User Model:
class User < ActiveRecord::Base
has_many :created_events, class_name: 'Event', :foreign_key => "creator_id",
dependent: :destroy
has_many :registers, :foreign_key => "attendee_id", dependent: :destroy
has_many :attended_events, through: :registers, dependent: :destroy
has_many :comments, through: :events
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable, :confirmable, :lockable
validates :name, presence: true, uniqueness: true, length: { maximum: 50 }
validates :email, presence: true, uniqueness: { case_sensitive: true }
validate :validate_name
def validate_name
if User.where(email: name).exists?
errors.add(:name, :invalid)
end
end
end
Event Model:
class Event < ActiveRecord::Base
scope :latest, -> { order(date: :asc, time: :asc)}
belongs_to :creator, class_name: 'User'
has_many :registers, :foreign_key => 'attended_event_id', dependent: :destroy
has_many :attendees, through: :registers, dependent: :destroy
has_many :comments, dependent: :destroy
validates :title, presence: true, length: { maximum: 50 }
validates :description, presence: true, length: { maximum: 500 }
validates :location, presence: true
validates :start_time, presence: true
validates :start_date, presence: true
validates :activity, presence: true
validates :difficulty, presence: true
end
and lastly, the comments/_show.html.erb partial:
<% if #comments %>
<span class="results-number color-aqua-show">Comments</span>
<% #comments.each do |comment| %>
<p class="comments">
<i class="color-green fa fa-user ride-i"></i>
<%= comment.user.name %>: <%= time_ago_in_words(comment.created_at).capitalize %> ago
</p>
<p>
<i class="color-aqua fa fa-comment ride-i"></i>
<%= comment.body %>
</p>
<div class="bottom-breaker"></div>
<% end %>
<% else %>
<span class="results-number color-aqua-show">Be the first to comment!</span>
<% end %>
Show form from events:
<div class="container s-results margin-bottom-50">
<div class="row">
<div class="col-md-9">
<%= render partial: 'comments/show' %>
<%= render partial: 'comments/form' %>
</div>
</div>
</div>
Again, if I change #comments in the partial to #events.comments it will recognize that there are comments for the particular event and loop through them. This has been driving me insane for the better part of 5 hours now. Thanks.
As Pardeep Saini said, you need to add #comments to events#show:
def show
#event = Event.find params[:id]
#comments = #event.comments
end
The problem is that #comments is a variable, which needs to be defined. If it isn't defined, then you're going to receive the equivalent of an undefined error.
Thus, to fix it, you need to make sure that you're calling a defined variable; either #comments (if you've defined it), or #event.comments.
I think there is a much deeper issue with your structure (from looking at your code).
You'd be better setting it up like this:
#config/routes.rb
resources :events do
resources :comments, only: [:create, :destroy] #-> url.com/events/:event_id/comments...
end
#app/controllers/comments_controller.rb
class EventsController < ApplicationController
def show
#event = Event.find params[:id]
#comments = #event.comments
end
end
This will allow you to use the following:
#app/views/events/show.html.erb
<%= #event.title %>
<%= render #comments %>
<%= render "new_comment" %>
#app/views/events/_comment.html.erb
<%= comment.user.name %>: <%= time_ago_in_words(comment.created_at).capitalize %> ago
<%= comment.body %>
#app/views/events/_new_comment.html.erb
<%= form_for #event.comments.build do |f| %>
<%= f.text_field :body %>
<%= f.submit %>
<% end %>
This will make it so that if you browse to url.com/events/1, it will output all the event's comments.
The added benefit of this setup is the ability to create / destroy comments:
#app/controllers/comments_controller.rb
class CommentsController < ApplicationController
before_action :set_event
def create
#comment = #event.comments.new comment_params
#comment.user = current_user
#comment.save
end
def destroy
#comment = #event.comments.find params[:id]
#comment.destroy
end
private
def comment_params
params.require(:comment).permit(:body, :user)
end
def set_event
#event = Event.find params[:event_id]
end
end
Solved the problem. It was a very dumb error where I had show listed twice in my events controller. The bottom one was over riding the top.

Rails - nested model input data not appearing in "show" page

I am having trouble figuring out how to make some data collected through a nested model appear on the "show" page. I have a rails app with 3 models, a User model, a Project model, and a Team model. The model associations are as follows:
Project:-
class Project < ActiveRecord::Base
has_many :users, :through => :team
has_one :team, :dependent => :destroy
accepts_nested_attributes_for :team, allow_destroy: true
end
Team:-
class Team < ActiveRecord::Base
belongs_to :project
has_many :users
end
User:-
class User < ActiveRecord::Base
# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
has_and_belongs_to_many :teams
end
Every project has one team, and every team consists of many users who are already saved in the database. What I would like to do exactly is to make it possible to select multiple existing users within the project form (through a nested form) and save them to a model called team. I managed to get the form working correctly, but im not sure how to go about saving the data collected to the team model, and then to make the group of users that were selected (the team) to appear in project's show page. Please help!
P.S I used the nested form gem to add multiple team members within the project's form.
Projects Show page:-
<%= bootstrap_nested_form_for(#project, :html => {:multipart => true}, layout: :horizontal) do |f| %>
<% f.fields_for :teams do |builder| %>
<% if builder.object.new_record? %>
<%= builder.collection_select :user, User.all, :id, :email, { prompt: "Please select", :selected => params[:user], label: "Employee" } %>
<% else %>
<%= builder.hidden_field :_destroy %>
<%= builder.link_to_remove "Remove" %>
<% end %>
<%= f.link_to_add "Add Team Member", :teams %>
<%= f.submit %>
<% end %>
projects controller:-
class ProjectsController < ApplicationController
before_action :set_project, only: [:show, :edit, :update, :destroy]
respond_to :html
def index
#projects = Project.all
respond_with(#projects)
end
def show
respond_with(#project)
end
def new
#project = Project.new
#project.pictures.build
#project.teams.build
respond_with(#project)
end
def edit
#project = Project.find(params[:id])
#project.pictures.build
#project.teams.build
end
def create
#project = Project.new(project_params)
if #project.save
flash[:notice] = "Successfully created project."
redirect_to #project
else
render :action => 'new'
end
end
def update
#project.update(project_params)
respond_with(#project)
end
def destroy
#project.destroy
respond_with(#project)
end
private
def set_project
#project = Project.find(params[:id])
end
def project_params
params.require(:project).permit(:id, :title, :description, :status, :phase, :location, :image, pictures_attributes: [:id, :image], teams_attributes: [:project_id, :user_id])
end
end

Resources