Basic database association - ruby-on-rails

I am attempting to associate 2 models - Athletes and Results
They have the following fields:
Athletes - :name :year :gender
Results - :name :event :performance
I have added belongs_to :athlete to results.rb & added has_many :results to athletes.rb
I would like to use the :name attribute to act as the primary key used to associate the two models as the intention is for all athletes to be uploaded initially and then just use the results input for the remainder of the season.
I have edited the results_controller to the following:
def create
#this was the initial code....
##result = Result.new(params[:result])
# This is the new code to try set up an association
#athlete = Athlete.where('name = ?', 'Peter')
#result = #athlete.results.create(params[:result])
respond_to do |format|
if #result.save
format.html { redirect_to #result, notice: 'Result was successfully created.' }
format.json { render json: #result, status: :created, location: #result }
else
format.html { render action: "new" }
format.json { render json: #result.errors, status: :unprocessable_entity }
end
end
end
This however produces the error undefined method 'results' for #<ActiveRecord::Relation:0x36a2b28>. I was also hoping to use the line #athlete = Athlete.where("name = ?", params[:name]) however it keeps yielding a NULL parameter value...
Is anyone able to point me in the correct direction please?
Extra information:
Results migration
class CreateResults < ActiveRecord::Migration def change
create_table :results do |t|
t.string :name
t.string :event
t.decimal :performance
t.timestamps
end
#add in new line here
add_index :results, :name
end end
Athletes migration
class CreateAthletes < ActiveRecord::Migration
def change
create_table :athletes do |t|
t.string :name
t.integer :year
t.string :gender
t.timestamps
end
end
end
Result.rb:
class Result < ActiveRecord::Base
belongs_to :athlete
end
Athlete.rb
class Athlete < ActiveRecord::Base
has_many :results
end

The problem is that Athlete.where('name = ?', 'Peter') returns ActiveRecord::Relation object. Either use
Athlete.where('name = ?', 'Peter').first
or the dynamic find method
Athlete.find_by_name('Peter')
Edit
Also make sure you add t.references :athlete to your results table.

Your results table needs to store the athlete_id.
create_table :results do |t|
t.references :athlete
t.string :name
t.string :event
t.decimal :performance
t.timestamps
end
The references will create the foreign key association using the rails conventions (which in this case is athlete_id)

Related

Rails PG::ForeignKeyViolation: ERROR: insert or update on table

I've got two models with simple belongs_to has_many relation, as follows:
class Property < ApplicationRecord
belongs_to :portfolio, optional: true
end
class Portfolio < ApplicationRecord
has_many :properties, dependent: :nullify
end
Which were created by migrations:
class CreateProperties < ActiveRecord::Migration[6.0]
def change
create_table :properties do |t|
t.string :name, null: false
t.string :status, null: false
t.references :portfolio, foreign_key: true
t.timestamps
end
end
end
class CreatePortfolios < ActiveRecord::Migration[6.0]
def change
create_table :portfolios do |t|
t.string :name, null: false
t.timestamps
end
end
end
When I want to create new Property with portfolio_id: 1 which doesn't exist I'm getting an error:
PG::ForeignKeyViolation: ERROR: insert or update on table "properties" violates foreign key constraint "fk_rails_760fb8258a" DETAIL: Key (portfolio_id)=(1) is not present in table "portfolios".
How to handle such error and display to user information such as "portfolio_id doesn't exist" ?
Create action is pretty standard:
property_controller.rb
def create
#property = Property.new(property_params)
respond_to do |format|
if #property.save
format.html { redirect_to #property, notice: 'Property was successfully created.' }
format.json { render :show, status: :created, location: #property }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #property.errors, status: :unprocessable_entity }
end
end
end
The error makes sense, since you're trying to create a property with a portfolio_id that doesn't exist. Since you added the optional: true to belongs_to :portfolio in the Property class, you removed the validation that normally comes with the belongs_to.
The best way of avoiding that would be to display a dropwdown in the form for a new portfolio that contains all the possible properties. That would also be better in terms of UX because the user would not have to remember the id of a property.

How can I set up a foriegn key field given an attribute rather than the foriegn key id?

I'm new to Rails and I'm having difficulties setting up my associations with my tables. I have a Team table that I want to have a foreign key to a TeamType table. The team type that I want to give each team is based on input from a form. Right now, my code is setup so that the user inputs the id of the team type. However, I want to make it so that the user inputs the name of the team type rather than the id in the form. With the name rather than the id, can I still populate the foreign id column? How could I do this?
Here are my migrations:
class CreateTeams < ActiveRecord::Migration[5.1]
def change
create_table :teams do |t|
t.references :team_type, foreign_key: true
t.string :team_name
t.integer :num_of_students
t.timestamps
end
end
end
class CreateTeamTypes < ActiveRecord::Migration[5.1]
def change
create_table :team_types do |t|
t.string :type_name
t.integer :max_size
t.timestamps
end
end
end
Here are my models:
class Team < ApplicationRecord
validates :team_name, length: {maximum: 40}, presence: true
belongs_to :team_type
# add_index :team_name
end
class TeamType < ApplicationRecord
validates :type_name, length: {maximum: 35}, presence: true
validates :max_size, numericality: { only_integer: true },
presence: true
end
Here is the create method of the team controller
def create
#team = Team.new(team_params)
respond_to do |format|
if #team.save
format.html { redirect_to #team, notice: 'Team was successfully created.' }
format.json { render :show, status: :created, location: #team }
else
format.html { render :new }
format.json { render json: #team.errors, status: :unprocessable_entity }
end
end
end
Any help would be appreciated. Thanks
Yes, you can set up your form fields on the front end so that the options display the name, but also pass along the ID so objects can be associated. I'd recommend referencing the Rails docs about form helpers, specifically section 3.1 on Select and Option Tags: https://guides.rubyonrails.org/form_helpers.html#the-select-and-option-tags

Rails, How to show all product by category search feature?

Now I want to add some add search feature to my project. I have 2 table about product and category, then i'm using one to many relation. I get stuck when i want to search my product by their categories. Here my code:
products_controller.rb:
def index
#product = Product.all
render json: #product, status: :ok
end
def search
#category = Category.find(params[:category_id])
#product = #category.products
render json: #product, status: :ok
end
def show
render json: #product, status: :ok
end
routes.rb
Rails.application.routes.draw do
namespace :v1 do
resources :products
resources :categories
post 'products/search', to: "products#search"
get 'products/search', to: "products#search"
end
end
model category.rb
class Category < ApplicationRecord
has_many :products
end
model product.rb
class Product < ApplicationRecord
belongs_to :category
end
my product table field
t.integer :category_id
t.string :product_name
t.string :company
t.text :description
t.string :logo_img
t.string :web_link
my table category field
t.string :category_name
t.string :status
How should it work for category show all their products?
Thanks for your help
add your categories_controller.rb like following
categories_controller.rb
def show
category = Category.find(params[:id])
render json: category.products, status: :ok
end
end
and visit /categories/:id url

Create a post with many-to-many relationship in Ruby on Rails

I have a structure very much for between category and yell. What I do is qeuro a call on POST type API with the following parameters:
{
"user_id":"1",
"title":"primeito",
"desciption":"de_novo",
"categories":[{"name":"eletro"},{"name":"domestic"},{"name":"new_category"}],
"yell_type":"novo",
"price":"10,00",
"payment_type":"boleto"
}
My structure is as follows:
My model yell:
#yell.rb
class Yell < ActiveRecord::Base
belongs_to :user, inverse_of: :yells
has_and_belongs_to_many :categories
end
model category:
#category.rb
class Category < ActiveRecord::Base
has_and_belongs_to_many :yells
end
method crete in controller yell:
#yells_controller.rb
def create
#yell = Yell.new(yell_params)
params[:categories].each do |rel|
#category = Category.find_by_name(rel[:name])
if #category
#only creates the relationship
else
#yell.categories.build(name: rel[:name]) #creates the relationship and category
end
end
if #yell.save
render json: #yell, status: :created, location: api_yell_path(#yell)
else
render json: #yell.errors, status: :unprocessable_entity
end
end
...
private:
def yell_params
params.require(:yell).permit(:title, :desciption, :price, :payment_type, :user_id, :yell_type, :categories)
end
So I created the table
class CreateCategoriesYellsJoinTable < ActiveRecord::Migration
def self.up
create_table :categories_yells, :id => false do |t|
t.integer :category_id
t.integer :yell_id
end
add_index :categories_yells, [:category_id, :yell_id]
end
def self.down
drop_table :categories_yells
end
end
I can make him create the categories, but does not know how to create only the relationship. Agluem can help me is the comment #only creates the relationship?
I need to do this check because the category name is unique
Also if someone know something more elegant way to do this, I accept suggestions
I am not quite sure I understood the last paragraph, but I think you need an intermediate table to join the two models first.
You would need to create a table like this:
class CreateCategoriesAndYells < ActiveRecord::Migration
def change
create_table :categories_yells, id: false do |t|
t.belongs_to :category, index: true
t.belongs_to :yell, index: true
end
end
end
Then you would need to update your controller to say something like this:
#yell.categories.build(category_params)
You would need also to pass the category parameters to the controller.
In order to do so I had to create a model to join my table:
model category_yell.rb
class CategoriesYell < ActiveRecord::Base
belongs_to :category
belongs_to :yell
end
and create my method was as follows:
def create
##yell = Yell.new(yell_params.except(:categories))
#yell = Yell.new({title: params[:title], desciption: params[:desciption], price: params[:price], user_id: params[:user_id], yell_type: params[:yell_type]})
if #yell.save
Array(params[:categories]).each do |rel|
#category = Category.find_by_name(rel[:name])
if #category
#categories_yells = CategoriesYell.new(category_id: #category.id, yell_id: #yell.id)
if #categories_yells.save
#yell.categories.build(id: #category.id, name: rel[:name])#only creates the relationship
else
render json: {status: 1, message:"relationship categoy not create", data: #yell.errors}, status: :unprocessable_entity
end
else
#yell.categories.create(name: rel[:name]) #creates the relationship and category
end
end
render json: {status: 0, message:"sucess", data: #yell}, status: :created
else
render json: {status: -1, message:"error", data: #yell.errors}, status: :unprocessable_entity
end
end

Form Creating nest objects

I have a nested object issue when creating a new item using forms. When I create a new Item within a form and view I created I get a nil on the Product object when trying to access it.
Here is my structure. I have a class called Item and a class called Product. There could be more than 1 item referring to the same Product.
class CreateItem < ActiveRecord::Migration
def change
create_table :items do |t|
t.integer :product_id
t.string :name
t.text :description
t.timestamps
end
end
end
class CreateProducts < ActiveRecord::Migration
def change
create_table :products do |t|
t.string :name
t.text :description
t.decimal :price
t.string :image_file_name
t.integer :inventory
t.timestamps
end
end
end
class Item< ActiveRecord::Base
belongs_to :product
belongs_to :itemstatus
end
class Product < ActiveRecord::Base
has_one :shop
accepts_nested_attributes_for :item
end
Controller Code:
def create
#item= Item.new(params[:item])
if #item.save
redirect_to #item, notice: "Item successfully created!"
else
render :new
end
end
The problem is happening when I go to show the created item. The Product object is nil. I'm not sure what I should be doing to get the Product Object correctly created and added in this process of creating a new Item.
Can someone help me?
Thanks!
params[:item] must be as the following:
#first example
params[:item] = {product_id: 1}
#second example
product = product.create(name: "", description: "", ..)
params[:item] = {product: product}
#third example
#item.product = product
#or
#item.product_id = params[:item][:product_id]
Try to do that:
#item.new(name: params[:item][:name], product_id: params[:item][:product_id], description: params[:item][:description])
help link

Resources