Defining table compound primary keys in rails for Cassandra - ruby-on-rails

Given the following pseudo-cql table structure:
CREATE TABLE searches (
category text,
timestamp timestamp,
no_of_searches int,
avg_searches double,
PRIMARY KEY((category, timestamp), no_of_searches)
);
and the following Rails Cequel model:
class Search
include Cequel::Record
# Table columns
key :category, :text
key :timestamp, :timestamp
key :no_of_searches, :int
column :avg_searches, :double
end
when I try to synchronise the model using:
rake cequel:migrate
the following rake error is thrown:
rake aborted!
Cequel::InvalidSchemaMigration: Existing partition keys category,timestamp differ from specified partition keys timestamp
I'm trying to get the above rails model to synchronise with the above table using the partition keys, although it's stating that the two sets of keys are different. I've tried defining the keys in the same order, but has not worked.
My objective is to get the pre-defined database table with partition keys working with the rails model. Any help would be grateful!

The key method supports an options hash as a third parameter. The options hash adds further support to the defined key such as order and partitioning.
Based upon the given table definition it would mean your table columns would look something like this:
# Table columns
key :category, :text, { partition: true }
key :timestamp, :timestamp, { partition: true }
key :no_of_searches, :int
column :avg_searches, :double
Unfortunately this is not documented within the Cequel README, however it is documented within the code, for which can be found here.

Related

How to update a table which doesn't have primary id column in rails

I am dealing with legacy code and do not have access to the database. There is a table which does not have a primary id column.
I can find a record using record = Model.find_or_initialize_by(listing_id: rating.pid, criteria_id: 33).
I can increment an attribute with record.rating_count += 1 but when I try to save it with record.save!, it gives the error TypeError: nil is not a symbol nor a string.
I think this is because record does not have a primary id (key) but I am not sure why it doesn't update the record.
If you have any suggestions, please let me know.
I also received the error TypeError: nil is not a symbol nor a string when trying to update a model which has no id column (or other primary key I could use). I had to add one.
You can add a primary key retrospectively using a migration:
add_column :people, :id, :primary_key

How to get values from different table using association in rails?

I've two tables in my Postgres database structures given below:
CREATE TABLE tables(
id serial NOT NULL,
name character varying(255),
table_state_id integer,
table_type integer,
CONSTRAINT tables_pkey PRIMARY KEY (id)
)
AND
CREATE TABLE table_states
(
id serial NOT NULL,
name character varying(255),
CONSTRAINT table_states_pkey PRIMARY KEY (id)
)
Table Model :
class Table < ActiveRecord::Base
attr_accessible :name, :table_type, :table_state_id,
belongs_to :table_state
end
TableState Model:
class TableState < ActiveRecord::Base
attr_accessible :name, :color_code
has_many :tables
end
In View I'm accessing like that:
= debug #table_data.table_state.name # Where `#table_data` is a variable from `tables` table
Through association I want to access name column of table_states table whose ID is present in table_state_id column of tables table. How can I do it?
You are having everything right in your code. There must be nil saved in your table_state_id in your tables table.
Error indicates that your table_state associated object is nil. this #table_data.table_state will return table_state object and error referring this to nil.
Try run select table_state_id from tables on your db console and see results. And make necessary changes in record through db console.
For future you can also use try to rescue such problem (specially production mode)
= debug #table_data.table_state.try(:name)

How to change primary key in rails migration file?

I need to migrate an old mysql table like this:
Products
name (string, primary_key)
to this schema:
Products
id (integer, primary_key, auto_generated)
name (unique)
I need the Products.id values populated in the new table.
How can i write the rails migration file? I am using Rails 3.2.7
I have 2 problems now:
1. I can't find a method to remove primary key in ActiveRecord::Migration
2. I don't know how to generate values for newly added primary key.
You could execute arbitrary SQL in your migration:
execute "ALTER TABLE `products` DROP PRIMARY KEY"
and then add the new column:
add_column :products, :id, :primary_key
See:
Remove Primary Key in MySQL
how to add a primary key to a table in rails
http://thinkwhere.wordpress.com/2009/05/09/adding-a-primary-key-id-to-table-in-rails/
http://api.rubyonrails.org/classes/ActiveRecord/Migration.html
If you're on Postgresql, the syntax is slightly different.
ALTER TABLE <table_name> DROP CONSTRAINT <table_name>_pkey;

Getting types of the attributes in an ActiveRecord object

I would like to know if it is possible to get the types (as known by AR - eg in the migration script and database) programmatically (I know the data exists in there somewhere).
For example, I can deal with all the attribute names:
ar.attribute_names.each { |name| puts name }
.attributes just returns a mapping of the names to their current values (eg no type info if the field isn't set).
Some places I have seen it with the type information:
in script/console, type the name of an AR entity:
>> Driver
=> Driver(id: integer, name: string, created_at: datetime, updated_at: datetime)
So clearly it knows the types. Also, there is .column_for_attribute, which takes an attr name and returns a column object - which has the type buried in the underlying database column object, but it doesn't appear to be a clean way to get it.
I would also be interested in if there is a way that is friendly for the new "ActiveModel" that is coming (rails3) and is decoupled from database specifics (but perhaps type info will not be part of it, I can't seem to find out if it is).
Thanks.
In Rails 3, for your model "Driver", you want Driver.columns_hash.
Driver.columns_hash["name"].type #returns :string
If you want to iterate through them, you'd do something like this:
Driver.columns_hash.each {|k,v| puts "#{k} => #{v.type}"}
which will output the following:
id => integer
name => string
created_at => datetime
updated_at => datetime
In Rails 5, you can do this independently of the Database. That's important if you use the new Attributes API to define (additional) attributes.
Getting all attributes from a model class:
pry> User.attribute_names
=> ["id",
"firstname",
"lastname",
"created_at",
"updated_at",
"email",...
Getting the type:
pry> User.type_for_attribute('email')
=> #<ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::MysqlString:0x007ffbab107698
#limit=255,
#precision=nil,
#scale=nil>
That's sometimes more information than needed. There's a convenience function that maps all these types down to a core set (:integer, :string etc.)
> User.type_for_attribute('email').type
=> :string
You can also get all that data in one call with attribute_types which returns a 'name': type hash.
You can access the types of the columns by doing this:
#script/console
Driver.columns.each {|c| puts c.type}
If you want to get a list of all column types in a particular Model, you could do:
Driver.columns.map(&:type) #gets them all
Driver.columns.map(&:type).uniq #gets the unique ones
In rails 5 this will give you a list of all field names along with their data type:
Model_Name.attribute_names.each do |k| puts "#{k} = #{Model_Name.type_for_attribute(k).type}" end
Rails 5+ (works with virtual attributes as well):
Model.attribute_types['some_attribute'].type
This snippet will give you all the attributes of a model with the associated database data types in a hash. Just replace Post with your Active Record Model.
Post.attribute_names.map {|n| [n.to_sym,Post.type_for_attribute(n).type]}.to_h
Will return a hash like this.
=> {:id=>:integer, :title=>:string, :body=>:text, :created_at=>:datetime, :updated_at=>:datetime, :topic_id=>:integer, :user_id=>:integer}
Assuming Foobar is your Active Record model. You can also do:
attributes = Foobar.attribute_names.each_with_object({}) do |attribute_name, hash|
hash[attribute_name.to_sym] = Foobar.type_for_attribute(attribute_name).type
end
Works on Rails 4 too
In Rails 4 You would use Model.column_types.

How to generate a model with a enum type field?

I want to generate a model and the corresponding database table in Rails using the generator script. The database table has a field with "enum" type. How can I generate it?
The table’s definition in SQL:
create table works {
id int unsigned not null auto_increment,
nickname varchar(20) not null,
sex enum('m', 'f') not null
};
The Rails generator command:
script/generator work nickname:string sex:(what should I write here?)
Rails 4.1 added ActiveRecord::Enum, which emulates enums using an integer-type column. You can use them as long as you are willing to change the column type to an integer in the database.
To use these enums, put integer in your generate command:
bin/rails generate Work nickname:string sex:integer
Then add a call to enum in the generated model file:
class Work < ActiveRecord::Base
enum sex: [ :male, :female ]
end
See Enum’s documentation for more details.
You could just use a string and then add validation on the model like this:
validates_inclusion_of :sex, :in => %w( m f )
Unfortunately, the valid column types are: integer, float, datetime, date, timestamp, time, text, string, binary, and boolean
Try making the column a string and using validates_inclusion_of.
Use enum_column to add enum support to active record
https://github.com/mdsol/enum_column

Resources