Rails 6, how can I create custom primary key with migration? - ruby-on-rails

I am trying to create custom primary key in rails 6, but didn't created primary key,
it's created normal id.
migration i have given
def change
create_table(:m_user_status, :id => false) do |t|
t.integer :id, :options => 'PRIMARY KEY'
t.string :status_name, limit: 20
t.integer :status_check
t.timestamps
end
from above just created id with int4 type, but i want create id with type int4 and primary key without auto-increment.
how i give id without auto-incremented and type int4(integer normal)

You can combine the :id and :primary_key options according to the docs here: https://apidock.com/rails/ActiveRecord/ConnectionAdapters/SchemaStatements/create_table
For example:
class CreatedummyTable < ActiveRecord::Migration[6.0]
def change
create_table :dummy_table, id: :int4, primary_key: :foo do |t|
t.timestamps
end
end
end
Creates this schema in postgres:
create_table "dummy_table", primary_key: "foo", id: :integer, default: nil, force: :cascade do |t|
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end

Related

How to establish an association in Rails between two models with the same column name

I have a problem, usually I run a migration and add a column called employee_id to Attendace model, then establish the relation and its done, but for company's rules I cannot change the db. So to connect a model with other I must work with a column with same name and both models, But I can't accomplish the relation. I leave the code.
class Attendace < ApplicationRecord
belongs_to :employee, :class_name => "Employee", :foreign_key => "private_number"
end
class Employee < ApplicationRecord
has_many :attendaces, :class_name => "Attendace", :foreign_key => "private_number"
end
The common column for both models is a field called "private_number" which is a string.
The error that arises is when I try to get the employee and their attendances is:
2.7.2: 001> Employee.joins (: attendaces)
Traceback (most recent call last):
ActiveRecord :: StatementInvalid (PG :: UndefinedFunction: ERROR: operator does not exist: character varying = bigint)
LINE 1: ... OIN "attendaces" ON "attendaces". "Private_number" = "Empleo ...
^
HINT: No operator matches the name and type of the arguments. It may be necessary to add explicit type casts.
The tables
create_table "attendaces", force: :cascade do |t|
t.string "private_number"
t.date "date"
t.time "time"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "employees", force: :cascade do |t|
t.string "email"
t.string "name"
t.string "lastname"
t.string "position"
t.string "private_number"
t.boolean "active"
t.bigint "company_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["company_id"], name: "index_employees_on_company_id"
end
Here is what I think you need to do, based on the info you shared:
Even when you added the :foreign_key directive within your model, you need to generate a new migration in order to apply those changes at the DB level, after add that foreign key at the DB level, your schema should have also a new index. With that said here is how you can do it:
rails g migration <name_of_your_migration>, this command will generate a file where you can add the foreign key to the DB by adding this line to the change method: add_foreign_key :attendaces, :employees, column: :private_number.
Here are the docs for add_foreign_key.
Hope this helps! 👍

"joins" with two tables does not work because of "ActiveModel::MissingAttributeError"

I am writing my application on Ruby-on-Rails and I have a problem with "joins". There are 3 tables: todo_lists, users, statuses. Each todo has one user and one status and they are introduced as IDs. And I want to get the whole todo with status name and user name instead of their IDs. This is how I tried to do that:
TodoList.joins(:user, :status)
.select("
todo_lists.id,
todo_lists.title,
todo_lists.description,
statuses.name,
users.name,
todo_lists.deadline,
todo_lists.is_disabled")
.as_json
But this throws an MissingAttribute error: missing attribute: status_id. But I added user_id and status_id records to my todo_lists table before that. What I must to do to solve this?
user.rb, status.rb, todo_list.rb files:
class User < ApplicationRecord
has_many :todo_lists
end
class Status < ApplicationRecord
has_many :todo_lists
end
class TodoList < ApplicationRecord
belongs_to :status
belongs_to :user
end
schema.rb file:
ActiveRecord::Schema.define(version: 2020_03_17_071204) do
create_table "statuses", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "todo_lists", force: :cascade do |t|
t.string "description", null: false
t.integer "user_id", default: 1, null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.boolean "is_disabled"
t.datetime "deadline"
t.string "title", null: false
t.integer "status_id", default: 1, null: false
end
create_table "users", force: :cascade do |t|
t.string "name", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
add_foreign_key "todo_lists", "statuses"
add_foreign_key "todo_lists", "users"
end
The most interesting is that if I joins with only users table, everything works fine :)
The full stack trace:
ActiveModel::MissingAttributeError (missing attribute: status_id):
app/controllers/todos_controller.rb:75:in `get_all_todos'
app/controllers/todos_controller.rb:3:in `get'
And todos_controller.rb methods where I am using "joins":
def get
render :json => get_all_todos
end
def get_all_todos
TodoList.joins(:status, :user)
.select("
todo_lists.id,
todo_lists.title,
todo_lists.description,
statuses.status,
todo_lists.deadline,
todo_lists.is_disabled")
.as_json
end
Joins are performed on a related column between two or more tables.
In the configuration that you have given, you are trying to join todo_lists to users and statuses table, but there is no relationship column to join on.
At the same time, the reason todo_lists join with users works because the join is performed on user_id column of todo_lists.
ActiveRecord::MissingAttributeError is raised because it cannot find this related column to join these two tables with, I am not sure about your applications specific domain logic, but you can fix this by adding user_id foreign_key to todo_lists table.

Creating ActiveRecord Associations on Existing Columns

I have two models: User and Listing.
I am trying to set up a one-to-many relationship between them via existing db columns.
class User < ApplicationRecord
has_many :listings
class Listing < ApplicationRecord
belongs_to :user, foreign_key: "user_id"
This is my migration:
class AddFkToListing < ActiveRecord::Migration[5.1]
def change
add_foreign_key :listings, :users, column: :user_id, primary_key: :user_id
end
end
But it created the foreign key in table users on column id.
Any idea how to do this properly?
Here is the DB schema:
create_table "listings", force: :cascade do |t|
t.integer "listing_id"
t.string "state"
t.integer "user_id"
t.string "title"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["listing_id"], name: "index_listings_on_listing_id", unique: true
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_digest"
t.string "remember_digest"
t.boolean "admin", default: false
t.string "activation_digest"
t.boolean "activated", default: false
t.datetime "activated_at"
t.string "reset_digest"
t.datetime "reset_sent_at"
t.string "request_token"
t.string "request_secret"
t.string "oauth_verifier"
t.string "oauth_token"
t.string "login_name"
t.integer "user_id"
t.index ["email"], name: "index_users_on_email", unique: true
end
Thank you so much!
Since you have a conventional foreign key field name (user_id in listings table), I believe this should work just fine for you:
class AddFkToListing < ActiveRecord::Migration[5.1]
def change
add_foreign_key :listings, :users
end
end
The syntax of add_foreign_key is:
first argument (:listings) - table which should contain foreign key
second argument (:users) - table which should be used for constraint
column: :user_id - specifies to which field of the listings table constraint should be applied
primary_key: - specifies the field of the users table to build a constraint
(see https://apidock.com/rails/ActiveRecord/ConnectionAdapters/SchemaStatements/add_foreign_key)
The primary_key: :user_id part in your example actually refers (tries to) to non-existing user_id field in users table.

null false doesn't trigger error in rails and unique doesn't work

rails not null / unique in migrations doesn't trigger error :S
class CreateDeditProjects < ActiveRecord::Migration
def change
create_table :dedit_projects do |t|
t.string :name, :null => false
t.string :uid, :unique => true
t.boolean :status
t.timestamps null: false
end
end
end
empty name doesn't trigger error. Neither does duplication of uid.
This is what I see in schema.db
ActiveRecord::Schema.define(version: 20150410105216) do
create_table "dedit_projects", force: :cascade do |t|
t.string "name", null: false
t.string "uid"
t.boolean "status"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
hm, I need to add indexes somewhere I guess? Shouldn't that be automatic?
Not null problem is bogus though.
Rails automatically adds index on id and references and maybe on some other types. If You want to add new index, You can create a migration:
def change
add_index :dedit_projects, :uid, unique: true
end
You can also use validations validates_uniqueness_of and validates_presence_of in models. Although I don't understand why doesn't it work as it is :)
Uniqueness is a property of the index so you need either a separate call to add_index or write it like so
create_table :dedit_projects do |t|
t.string :uid, index: {unique: true}
...
end

Retroactively adding the primary key to a table

I wrote a migration for a join model which looks like:
create_table "project_memberships", :id => false, :force => true do |t|
t.integer "user_id"
t.integer "project_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "id"
end
I want to forcibly create ids now. Must I drop the table and recreate it or can I write a migration removing this constraint?
With a small amount of googling... http://thinkwhere.wordpress.com/2009/05/09/adding-a-primary-key-id-to-table-in-rails/
Generate a empty migration:
rails generate migration AddIdToProjectMemberships
and fill it in with:
def change
add_column :project_memberships, :id, :primary_key
end
Also there was a question like this before.. how to add a primary key to a table in rails

Resources