Rails: Create a no-id model using generator - ruby-on-rails

Is there a way to use the $ rails generate model to explicitly not using the id since I want to create a joined table with foreign keys instead.
I am fully aware that we can do that by going to the migration table, as follow:
# Just an example to demonstrate <id: false>
create_table :my_table, id: false do |t|
t.string :key_column
t.string :value_column
end
StackOverflow Source
But is there a way to do so using the generator? And yes, I've checked the help option:
$ rails g model -h
but maybe I'm missing something.

Related

Add Rows to a SQL Table in Ruby on Rails

How do I add data to a table in Rails?
So far I have created a Rails app that pulls in data from an API. Next, I have ran the command
rails generate model order
and I have /db/migrate/timestamp_create_orders.rb
class CreateOrders < ActiveRecord::Migration
def change
create_table :orders do |t|
t.string :email, null: false
t.string :order_date, null: false
t.string :total_price, null: false
t.string :order_number, null: false
t.string :cust_name, null: false
t.string :zip_code, null: false
t.timestamps null: false
end
end
end
Next, I believe I need to run db:migrate and that will create the table.
My question is, how do I add data to this table? I want the user to visit the app, it pulls in the data, and stores it in this table.
I have found conflicting advice..
Should I just use the advice from here
Order.create(:email=>'fake#fake.com',:order_data=>"...
But other advise seems to say not to do this here and here. Though they are all pretty old
You do not create database entries in migrations, you usually create schema or specify changes in the schema in migration files. You use seeds for creating seed data in the database.
To create new data in database through rails you can use either create or new method but you need to save the data as mentioned in other posts in your links when you are using new method.
While creating or migrating a new database table, table row is not automatically added. You need to add them manually. One way to populate the newly created database table is using seeds.rb file which is located in your application db folder. You can add Faker gem to your application for creating fake table attribute elements. An example using faker:
(1..3).each do # it'll create 3 new order
Order.create(email: Faker::Internet.email, order_date: Faker::Date.between(2.days.ago, Date.today))
end
Then run rake db:seed in your project folder console.
If you have some validation in your order.rb file, then you can create new instance of that order and then save it like:
order = Order.new(....)
order.save(validate: false)

What is the correct way to generate multi-word reference fields in rails

rails generate model product name:string twoWord:string twoWordRef:references
produces the following migration file
class CreateProducts < ActiveRecord::Migration
def change
create_table :products do |t|
t.string :name
t.string :twoWord
t.references :twoWordRef, index: true
t.timestamps null: false
end
add_foreign_key :products, :twoWordRefs
end
end
I thought that field names were supposed to be snake case, not camel case, yet rails generate model is producing camel case field names in the migration file. I think I am following the examples on my generate command.
I am also finding issues later on when I try to update via reference where rails assumes the foreign key is in snake case and can't find my foreign key in the table.
What am I doing wrong? Thanks
Rails automatically generate the database tables in snake case. For example if your model is TestModel corresponding table in database will be test_models. However same is not the case for attributes.
Do this instead:
rails generate model product name:string two_word:string two_word_ref:references
Update
This is completely out of scope for what OP asked, but I thought sharing this might be helpful if you are starting Rails. Rails uses a few conventions when naming models and tables (like singular name for model and plural for table). For this it uses ActiveSupport::Inflector module.
The Inflector transforms words from singular to plural, class names to
table names, modularized class names to ones without, and class names
to foreign keys. The default inflections for pluralization,
singularization, and uncountable words are kept in inflections.rb.
You can use its classify and tableize methods to validate corresponding names. Eg:
"test_models".classify
# => "TestModel"
"TestModel".tableize
# => "test_models"

Rails - using `rails generate model` to specify non-nullable field type

According to the rails documentation
http://guides.rubyonrails.org/migrations.html
2.3 Supported Type Modifiers says it should be possible to modify fields to allow or disallow NULL in the column, and that it's possible to do this on the terminal
This is what I want to appear in the migration file
class CreateTestModels < ActiveRecord::Migration
def change
create_table :test_models do |t|
t.string:non_nullable, :null => false
t.timestamps
end
end
end
On the terminal, I've tried
rails generate model TestModel non_nullable:string{null}
rails generate model TestModel 'non_nullable:string{null: false}'
I can't think of any other way to express it
Note: I already know you can go into the migration file and manually add it. That's not what I'm looking for.
The docs mention that
Some commonly used type modifiers can be passed directly on the command line. They are enclosed by curly braces and follow the field type
but they don't give details about which "commonly used" modifiers willl work.
As pointed out by mr rogers
there are only three supported options:
length for string/text/binary/integer (name:string{255})
precision,scale for decimal (dollar_fragment:decimal{3,2})
polymorphic for references/belongs_to (agent:references{polymorphic})
As mentioned by user2903934
it may be possible to make this work from the command line as a hack.
NOTE: this is a hack. i wouldn't recommend doing this but it does answer your question.
rails generate model TestModel 'non_nullable, null => false:string'
It looks like it splits on the first colon, so we can use a hashrocket syntax to sneak options in there. This yields:
class CreateTestModels < ActiveRecord::Migration
def change
create_table :test_models do |t|
t.string :non_nullable, null => false
t.timestamps
end
end
end
That obviously isn't officially supported, it just happens to work.
The closest I can get to your solution is something like this:
rails generate model TestModel non_nullable,null:string
I couldn't work out what comes after the , but that should give you a start
You can open editor by utilising https://github.com/rails/rails/pull/38870 (available for Rails versions > 6.1.0)
To create migration with null: false from command line, first you need to enable EDITOR_FOR_GENERATOR
# config/application.rb
# https://github.com/rails/rails/pull/38870#issuecomment-609018444
config.generators.after_generate do |files|
if ENV["EDITOR_FOR_GENERATOR"]
files.each do |file|
system("#{ENV["EDITOR_FOR_GENERATOR"]} #{file}")
end
end
end
Than use sed to append to specific columns.
For example that you want to create a model with jti and exp columns with not null constrains and add index to them (index is supported on command line using :index).
We need to match line t.string :jti and append to it so end result is t.string :jti, null: false
Here is command I use:
# rails g model CreateJwtDenylist jti:index exp:datetime:index
# replace jti and exp with your column names
EDITOR_FOR_GENERATOR='sed -i "" -r -e "/^[[:space:]]*t.*(jti|exp)$/ s/$/, null: false/"' rails g model CreateJwtDenylist jti:index exp:datetime:index
This works both for rails g migration and rails g model.
Resulting migration is
# db/migrate/20230121091319_create_jwt_denylist.rb
class CreateJwtDenylist < ActiveRecord::Migration[7.0]
def change
create_table :jwt_denylists do |t|
t.string :jti, null: false
t.datetime :exp, null: false
t.timestamps
end
add_index :jwt_denylists, :jti
add_index :jwt_denylists, :exp
end
end
You can do it in your model class like this-
class TestModel < ActiveRecord::Base
validates_presence_of :non_nullable
end

How to use default sql expression with ActiveRecord?

I'm trying to use default values for a model in rails. The best documented way of doing this that I have seen is using the :default parameter in a migration. However, it looks like this only works for values. I am trying to set a default expression, specifically currval('customers_id_seq'::regclass). I can add the default expression by hand, but I can't figure out how to tell rails to use a default value. How can I use this default when creating model objects?
EDIT
Here is a more specific example:
class CreateTestmodels < ActiveRecord::Migration
def up
create_table :testmodels do |t|
t.integer :idxfield, :null=>false
t.string :datafield
t.timestamps
end
execute("ALTER TABLE testmodels ALTER COLUMN idxfield SET DEFAULT currval('testmodels_id_seq'::regclass)")
end
def down
drop_table :testmodels
end
end
With this migration, I can run insert into testmodels (datafield) VALUES ('sometestdata');, which will add a row to the table where idxfield defaults to id.
However, if I run Testmodel.create(:datafield=>"testdata") from the rails console, it substitutes null for idxfield and throws an exception.
For anyone coming across this now, you can use an expression in a default value migration by wrapping it in a lambda: https://github.com/rails/rails/pull/20005
e.g.
create_table :posts do |t|
t.datetime :published_at, default: -> { 'NOW()' }
end
Sounds like you're trying to use Postgres sequences, which rails will use in all primary key definitions in migrations.
If you need to add it to non-primary key column then you will need to run an ALTER TABLE outside of the create_table and add it yourself, like
execute("ALTER TABLE users ADD COLUMN foo INTEGER default nextval('users_id_seq'::regclass)")

Scaffolding in Rails while defining nullable fields and foreign keys

I'm just figuring out my way around rails but I need a little help with the rails generate scaffold command.
Here's the command that I'd like to use
rails generate scaffold Expense user:??? name:string description:text
I'd like the description field to be nullable and the users field to be linked to another Model — in this case I'd like to create a foreign key to the Users. I'm using the devise authentication framework.
I've read that many RoR developers try and avoid the scaffolding method and opt for the manual approach instead but my web-app is quite simple and I've thought of going the scaffolding way.
Scaffolding only generates the migration that you then run. Once the file is generated simply crack open the generated migration and adjust any of the values you need specific constraints on. By default columns are set to null unless you specify otherwise e.g.:
create_table "slugs", :force => true do |t|
t.integer "sequence", :default => 1, :null => false
t.string "sluggable_type", :limit => 40
t.string "scope", :limit => 40
t.datetime "created_at"
end
This is the code generated by the friendly_id plugin as you can see they have specified that the sequence column cannot be null while the other fields have other constraints.

Resources