ActiveRecord is showing wrong column defaults - ruby

This is my first time using ActiveRecord in a non-rails application, and I'm running into a problem. ActiveRecord is able to figure out the columns I have in my sqlite3 database, but it can't figure out the default column values for some reason.
Here is the sql description for the table in question.
-- Describe ACCOUNTS
CREATE TABLE "accounts" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL DEFAULT (0),
"username" TEXT NOT NULL,
"password_hash" BLOB NOT NULL,
"creation_time" INTEGER NOT NULL DEFAULT(strftime('%s', 'now')),
"expiration_time" INTEGER NOT NULL DEFAULT(strftime('%s', 'now') + 2592000)
)
I used the following code for loading my database file.
require 'active_record'
require './config.rb'
ActiveRecord::Base.establish_connection(
:adapter => 'sqlite3',
:database => DB_FILE
)
class Account < ActiveRecord::Base
end
When I look at the column defaults for the Account table with a REPL, this is what I get:
[10] pry(main)> Account.column_defaults
=> {"id"=>0,
"username"=>nil,
"password_hash"=>nil,
"creation_time"=>0,
"expiration_time"=>0}
I worked with ActiveRecord for a rails app before and it was smart enough to figure out the default values. For some reason, it can't figure them out now.
Am I doing something wrong here? I read that I can manually specify the default value with default :id => bla, but shouldn't ActiveRecord be able to figure out the defaults?
Update: I think I figured out a workaround. The hash returned by Account.column_defaults is writeable, and changing those elements seems to work fine.

Try doing this:
class Account < ActiveRecord::Base
after_initialize :default_values
private
def default_values
self.username ||= "default value"
#etc...
end
end

Related

Cannot update record with ActiveRecord (no rails)

Hello I have the following table in my PostgreSQL database:
CREATE TABLE user_logins (token VARCHAR(512) NOT NULL, user_id UUID UNIQUE NOT NULL, create_date TIMESTAMPTZ DEFAULT clock_timestamp(), last_visit TIMESTAMPTZ DEFAULT clock_timestamp(), expire TIMESTAMPTZ DEFAULT clock_timestamp() + INTERVAL '30 DAYS');
For which I have the following ActiveRecord class
require "active_record"
class UserLogin < ActiveRecord::Base
end
I want to update the last_visit column, which I try to do like this:
def update_last_visited(user_token)
user_login = UserLogin.find_by(token: user_token)
if user_login == nil
return false
end
user_login.update(last_visit: Time.now.utc)
end
However, I get this error:
2023-02-19 11:55:21 - ActiveRecord::StatementInvalid - PG::SyntaxError: ERROR: zero-length delimited identifier at or near """"
LINE 1: ...logins" SET "last_visit" = $1 WHERE "user_logins"."" IS NULL
As I have understood ActiveRecord if I have the object ActiveRecord should be able to update the record, or have I miss understood something?
Should I instead do an update based on a where?
A minimal reproducible example:
require "active_record"
require "yaml"
require 'securerandom'
db_config_file = File.open("database.yml")
db_config = YAML::load(db_config_file)
ActiveRecord::Base.establish_connection(db_config)
class UserLogin < ActiveRecord::Base
end
def update_last_visited(user_token)
user_login = UserLogin.find_by(token: user_token)
if user_login == nil
return false
end
user_login.update(last_visit: Time.now.utc)
end
user_login = UserLogin.new
user_login.token = "wompas"
user_login.user_id = SecureRandom.uuid
user_login.save
update_last_visited(user_login.token)
The database.yml file:
adapter: 'postgresql',
host: 'localhost',
port: 5432
username: 'user',
password: 'password',
database: 'dbname'
Note: The project does not utilise Rails, we only use ActiveRecord for its ORM capabilities.
Your UserLogin doesn't have a primary key. But ActiveRecord expects tables to have a primary key column named id per default. See Active Record Basics / Schema Conventions in the official Rails Guides.
In your example, it feels like the user_id can be used as a primary key, because is cannot be NULL and it must be unique. You can try to configure user_id to be used by as primary key by ActiveRecord like this.
class UserLogin < ActiveRecord::Base
self.primary_key = :user_id
end
But in general, I suggest not fighting the default conventions and would just add a bigint id column to that table. While it is possible to configure custom primary key columns, it makes everything a bit more complex because you will need to remember those custom setting in other use cases – like, for example, when defining association.

Can I change SQLite column name with DataMapper?

Brand new to DataMapper and wondering if I can use DataMapper.auto_updgrade! to change a column name of an existing column in a SQLite database?
If I have the following in a song.rb
require 'date'
require 'dm-core'
require 'dm-migrations'
DataMapper.setup(:default, "sqlite3://#{Dir.pwd}/development.db")
class Song
include DataMapper::Resource
property :id, Serial
property :title, String
property :music_by, String
property :lryics_by, String
property :lyrics, Text
property :length, Integer
property :released_on, Date
def released_on=date
super Date.strptime(date, '%m/%d/%Y')
end
end
DataMapper.finalize
After a Song.auto_migrate!
2.0.0-p598 :004 > Song.new
=> #<Song #id=nil #title=nil #music_by=nil #lyrics_by=nil #lyrics=nil #length=nil #released_on=nil>
Is it possible to change the
property :lryics_by, String
to
property :words_by, String
and have the database column name change, but keep any existing data?
I've tried with Song.auto_upgrade! and it adds an empty new column and leaves the original column and data in place. On the other hand, my Song.new object looks right.
2.0.0-p598 :004 > Song.new
=> #<Song #id=nil #title=nil #music_by=nil #words_by=nil #lyrics=nil #length=nil #released_on=nil>
It seems like I need a migration in the way that ActiveRecord (I've played around a little with that ORM) handles migrations. Or I would need to change the column name with SQL or an app or the Firefox SQLlite plugin.
UPDATE:
I'm wondering now if this is more a SQLite thing than a DataMapper thing. When I went to delete a column in Firefox's SQLite Manager plugin I got this message:
This is a potentially dangerous operation. SQLite does not support statements that can alter a column in a table. Here, we attempt to reconstruct the new CREATE SQL statement by looking at the pragma table_info which does not contain complete information about the structure of the existing table.
Do you still want to proceed?
dm-migrations can do this but not for SQLite, mostly because SQLite doesn't support renaming columns, as it has a limited ALTER TABLE implementation (http://www.sqlite.org/lang_altertable.html)
There's a rename_column migration but as you can see from the dm-migrations TableModifier class (https://github.com/datamapper/dm-migrations/blob/master/lib/dm-migrations/sql/table_modifier.rb), it's not available for SQLite:
def rename_column(name, new_name, opts = {})
# raise NotImplemented for SQLite3
#statements << #adapter.rename_column_type_statement(table_name, name, new_name)
end

How to remove table column in Active Record

I would like to remove column from the table by using Active record. Please find the below snippet
require "active_record"
require 'sqlite3'
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => 'test_one')
class Account < ActiveRecord::Base
table_name = "AccountBean"
primary_key = "process_id"
remove_column "xxx" // I need this type of method to remove column "xxx" from accounts table
end
Is there any class method in ActiveRecord which satisfy this requirement ?
I guess ActiveRecord presumes that changes to structure shall be done using migrations.
If you really need to, you could use the same methods rails uses in migrations to e.g. remove a column - like here
I don't recommend this :)
ActiveRecord::Base.connection.remove_column("persons", "first_name")
Within a class that would look something like:
class Account < ActiveRecord::Base
table_name = "AccountBean"
primary_key = "process_id"
connection.remove_column(table_name, "xxx")
end

uninitialized constant ActiveRecord::RecordNotUnique

I am using rails 2.3.4, rubygems 1.3.6, activerecord 3.1.0, windows 7 home basic
Here's my code:
def items
#products = ShopifyAPI::Product.find(:all)
#products.each do |a|
begin
#shop = Product.new(:title => a.title , :shop_id => a.id, :product_type => a.product_type)
#shop.save
rescue ActiveRecord::RecordNotUnique
redirect_to :action => "display_items"
end
end
#shop_items =Product.find(:all)
if session[:user_id]
#log = "Welcome Administrator!"
#logout="logout"
else
#log = "Admin Log in"
#logout=""
end
end
I'm having the error "uninitialized constant ActiveRecord::RecordNotUnique" when trying to save the data fed by the API. Any help would be appreciated. thank you.
Any reason why you use ActiveRecord 3.1 with Rails 2.3.4. Though it's possible to use that, it is not recommended.
ActiveRecord::RecordNotUnique is only available with versions 3.0 or higher. I am not sure if activerecord modules are initialized correctly with your version of Rails.
That error is returned when ActiveRecord tries to save a duplicate when the index says values need to be unique. In your case, the value Baby Ruth-49696852 is a duplication, and violates the uni_products key, which is set to be unique.
Either make the column not unique, or stop trying to save duplicate records. Did you create your database indexes in your migrations? Is there a :unique => true on any of the columns in your migrations?

Articles/examples with test unit and active record

I'm looking to write some unit tests that will let me build/test non-stubbed CRUD functions like I can in Rails but I want to use a minimal number of gems (test unit&active record).
Anyone know of any resources that might help?
Thanks
Don't know of any resources specifically about this. To get active record working in a simple test you would just need to set up the connection details and your model classes assuming you have an existing database to work with that matches the active record conventions. Don't know what stage you are at but if you just need a simple example:
require 'rubygems'
require 'active_record'
require 'test/unit'
ActiveRecord::Base.establish_connection(
:adapter => 'sqlite3',
:database => 'db/my.db'
)
# Assuming a table like:
# CREATE TABLE people (id INTEGER PRIMARY KEY, name VARCHAR(100) NOT NULL, age INTEGER NOT NULL);
class Person < ActiveRecord::Base
end
class TestActiveRecord < Test::Unit::TestCase
def setup
#bob = Person.create(:name => 'Bob', :age => 95)
end
def teardown
#bob.destroy
end
def test_find_bob
bob = Person.find_by_name('Bob')
assert_not_nil(bob)
assert_equal(95, bob.age)
end
end
There are no other gems involved here other than those that active record itself depends on.
You'll have to work out what configuration settings you need depending on the type of database adapter you are using. If your database schema doesn't conform to the active record conventions then you will also have to specify some mappings in your model classes.

Resources