Reputation:
UPDATE 2
What I have done so far was rails generate migration add_user_reference_to_products
Then I did
class AddUserReferenceToProducts < ActiveRecord::Migration[5.1] def change add_reference :products, :user, foreign_key: true end end
But I am obtaining the PG::DuplicateColumn: ERROR: column "user_id" of relation "request_items" already exists.
When I reference this (as @products.user_id) in the view, it's blank. I've tried creating a new product, but it isn't working. What should I be doing, as I just can't , for the life of me now, figure out what to do now.
UPDATE
I want to do something like this @products.users.username (If there is another way of doing this, please tell me) to display a user's username, for the product they have created.
Currently, I have not modified the code as outlined below, but if any more information is required please tell me. Also, if you are able to provide a step by step process, I would appreciate that.
The answer provided Clyde doesn't work for me (undefined method "username").
QUESTION: How can I link the user controller with the product controller, despite already defining this relationship in the model? (A user can create many products, and a product belongs to a user)
I have a website, where users can create products. Now, these users also have a profile page. I want to know if it's possible to have it so when the user creates the product ,and I click the product itself, it will show which user created it and when I click the user name, it directs to their profile page.
Currently, the only thing that isn't working is the user name is not appearing on the product they have created, the routes and everything else is working.
For users, relevant code:
class UsersController < ApplicationController
def index
@users = User.all
end
def show
@users = User.find(params[:id])
end
end
Schema
create_table "users", force: :cascade do |t|
t.string "email"
t.string "username"
t.string "encrypted_password"
t.string "description"
t.string "address"
t.string "phone_number"
end
For the products:
class ProductsController < ApplicationController
def index
@products = Product.all
end
def new
@products = Product.new
end
def create
params[:products][:user_id] = current_user.id
@products = products.new(products_params)
end
def edit
@products = Product.find(params[:id])
end
def update
@products = Products.find(params[:id])
@products.update_attributes!(products_params)
flash[:notice] = "#{@products.name} has been succesfully updated."
redirect_to show_my_products_path
end
def destroy
@products = Product.find(params[:id])
@products.destroy
flash[:notice] = "Your product '#{@products.name}' has been deleted."
redirect_to show_my_products_path
end
def show
@products = Product.find(params[:id])
end
end
Schema
create_table "products", force: :cascade do |t|
t.string "name", default: "", null: false
t.string "description"
t.string "quantity"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
end
For the show products page, it essentially displays the product information. One thing I want to add is this line:
<%= link_to "#{user.username}", users_show_path_url(user), class: 'link_to' %>
Where, if you click this link on the product page, that a user has created, it directs to their profile page. However, I do get the error, when in the show.html.erb page for products:
undefined method `username' for nil:NilClass
OR
Couldn't find User with 'id'= ....
I guess, I am not too sure how I can link the user who has created their product and display their username on that particular product. I've tried adding:
@users = User.all
@users = User.find(params[:id])
To the show product controller.
Also, this is the model:
class User < ActiveRecord::Base
has_many :products
end
class Product < ActiveRecord::Base
belongs_to :user
end
I've intentionally left out some code and selected the most relevant code, but if you require more code please tell me.
What I want is when someone clicks the product, they can see which user created it, and see their username and therefore click that to go to the user profile. The only link between the product and users is the "user_id" in the product table. So when a product is created, a user_id is tied to that item.
Upvotes: 3
Views: 841
Reputation: 876
First of all, I will create the devise User
rails generate devise User
Then I will create a Task
rails g scaffold Task name:string description:text user:references
Then I will modify the task controller
class TasksController < ApplicationController
before_action :set_task, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, except: [ :show, :index]
# GET /tasks
# GET /tasks.json
def index
@tasks = Task.all
end
# GET /tasks/1
# GET /tasks/1.json
def show
end
# GET /tasks/new
def new
@task = current_user.tasks.new
end
# GET /tasks/1/edit
def edit
end
# POST /tasks
# POST /tasks.json
def create
@task = current_user.tasks.new(task_params)
respond_to do |format|
if @task.save
format.html { redirect_to @task, notice: 'Task was successfully created.' }
format.json { render :show, status: :created, location: @task }
else
format.html { render :new }
format.json { render json: @task.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /tasks/1
# PATCH/PUT /tasks/1.json
def update
respond_to do |format|
if @task.update(task_params)
format.html { redirect_to @task, notice: 'Task was successfully updated.' }
format.json { render :show, status: :ok, location: @task }
else
format.html { render :edit }
format.json { render json: @task.errors, status: :unprocessable_entity }
end
end
end
# DELETE /tasks/1
# DELETE /tasks/1.json
def destroy
@task.destroy
respond_to do |format|
format.html { redirect_to tasks_url, notice: 'Task was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_task
@task = Task.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def task_params
params.require(:task).permit(:name, :description, :user_id)
end
end
Then I will add the new column in the User
table.
rails generate migration add_username_to_users username:string
Then I will run the migration command
rake db:migrate
Then I will add the username field in signup form #/views/devise/registerations/new.html.erb
<div class="field">
<%= f.label :username %><br />
<%= f.text_field :username, autofocus: true, autocomplete: "username" %>
</div>
Then I will allow the username in devise controller
class ApplicationController < ActionController::Base
before_action :configure_permitted_parameters, if: :devise_controller?
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up, keys: [:username])
end
end
And then I will add the validation for username in #user.rb
validates :username, uniqueness: true, presence: true
Then I will add the reference in user model
has_many :tasks
Now You can show Your username with the task In views everywhere
<%= task.user.username %>
Here is the working example Association link sample for username display
Upvotes: 2
Reputation: 48
I'm not sure your create function in the Products controller is working as intended.
First, when calling Product.new, Product needs a capital P. https://apidock.com/rails/v3.2.13/ActiveRecord/Base/new/class
Second, I don't think you are associating a user correctly.
Third, you need to save the new instance to the database.
Depending on your product_params, potentially you could do something like:
def create
attributes = product_params.merge(user_id: current_user.id)
@product = Product.new(attributes)
if @product.save
redirect_to show_my_products_path, notice: 'Product was successfully created.'
else
render :new
end
end
In this case, as you are not passing user_id through on params you won't need user_id permitted in your product_params.
Then in the correct view you should be able to call
<%= link_to @product.user.username, users_show_path_url(user), class: 'link_to' %> |
I also see you calling Products.find in other methods where they should be singular. (Potentially a separate problem) https://api.rubyonrails.org/classes/ActiveRecord/FinderMethods.html#method-i-find
It's also best practise to call your instance methods singular if there is only one so apart from in the index they would all be @product not @products but this is purely for readability.
General advice - It is best to try to scaffold your application as much as possible and rails will take care of a lot of work for you.
For example, from scratch I ran
rails g scaffold user username
rails g scaffold products name user:references
rails db:migrate
I don't have all the attributes as this was just an example but this generated me all the code I needed to get everything working as intended (except the has_many in my model). It's an extremely useful thing to learn about :)
Upvotes: 0
Reputation: 1323
In order to use the variable user
, you need to assign it and I don't see where this is being done in the show product action.
Instead try accessing the user through the product:
<%= link_to "#{@products.user.username}", users_show_path_url(@products.user), class: 'link_to' %>
You will also need to ensure that the product you are viewing actually has a user otherwise it will cause the same kinds of errors.
Upvotes: 0
Reputation: 141
Try :
<%= link_to user_path(@product.user), title: @product.user.username do %>
<%= @product.user.username %>
<% end %>
Upvotes: 0