Updating / Deleting JoinTable records with ActiveRecord - ruby-on-rails

I have 3 models: movies, movie_tags and movie_tag_counts
It is a classic has many through relationship. My use case is that every movie can have multiple tags and user can vote on tags that were already added.
My Problem is that I can't seem to update an existing object in movie_tag_counts
movie_tag_count = MovieTagCount.first
movie_tag_count.count += 1
movie_tag_count.save
the result is this error message
TypeError: nil is not a symbol nor a string
My best guess is that the reason is that movie_tag_counts table doesn't have an id column of its own, but I still have no idea how to fix it.
My current workaround is to execute a sql statement directly

Turns out my guess was right, ActiveRecord expects an id column, I added it like this
add_column :movie_tag_counts, :id, :primary_key
and everything worked perfectly. I'm sure there's a way to do it without the id column by overwriting some AR methods, but I guess having another column won't hurt that much

Related

The right way to remove attribute from rails model object

There is phone attribute in customer model with our rails 3.2.12 app. We would like to remove the phone attribute sometime when retrieving customers. Here is what we did:
#customers = Customer.all
#customers.delete :phone
However there is error:
delete_all doesn't support limit scope
What's the right way to remove an attribute from a model object? Thanks.
You can use Customer.select('name, address') to retrieve only the fields you want. Noting that you pass a string with comma separated field named.
This will generate an SQL request like this
SELECT customer.name, customer.address FROM customer
You then get only the data you want without deleting it from the database (which is what your original call is trying to do).
My original response showed incorrect use of pluck, which only works for a single column.
I know that you are using Rails 3.2.12, but in Rails 4 pluck also works with multiple columns, allowing something like
Customer.pluck(:name, :address)

Rails single table inheritance with lowercase type names

I'm working up an app that interfaces with a legacy database which has a type column used for single table inheritance. This database is still used by an existing PHP application so I have to work with what is there. I'm trying to set up some models for this and one of the key tables is set up with an STI scheme and a type column, however, all of the types in that column are entirely lowercase.
In my testing so far, rails works fine with the type column if I change the value to match the class name (for example, Claimant instead of claimant). However I don't want to go changing those values in the production database even though it would probably be ok, nor do I want to have to go in and modify the legacy app to save the names differently...
In order to fix this I have two questions...
1) Is there anyway I can configure the model to recognize that type = 'claimant' maps to class = 'Claimant'?
2) failing that, is there a way I can tell rails to not use STI on this table even though it has a type column?
I've done some googling and haven't come up with much yet...
I haven't tried this in an STI setting, but when using a legacy table I was able to use the method "set_table_name" on the ActiveRecord model.
class Claimant < ActiveRecord::Base
set_table_name 'claimant'
end
Or with a custom method:
def table_name
'claimant'
end
Apologies I haven't got an STI table handy to test this on, but thought it might help solve your problem.
In answer to the second part of your question, I believe you can disable Rails looking at the type column, by just specifying a non-existant column name.
class Claimant < ActiveRecord::Base
inheritance_column = :_type_column_disabled
end

rails model validation in the database

I have a table and have the validation for uniqueness setup in the table. eg.
create table posts (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY UNIQUE,
title varchar(255) unique,
content text
);
Here title is unique. Do also need to inform the model class about this uniqueness? If not when i insert a duplicate title, it gives me error. How do I catch that. Currently rails shows me the backtrace and i could not put my own error messages
def create
#f = Post.new(params[:post])
if #f.save
redirect_to posts_path
else
#flash['message'] = "Duplicated title"
render :action=>'new'
end
end
I am not being redirected to the new and instead show a big backtrace.
Use the validates_uniqueness_of validation. "When the record is created, a check is performed to make sure that no record exists in the database with the given value for the specified attribute (that maps to a column)"
You will have to add all of the validations to your models. There is a gem called schema_validations, which will inspect your db for validations and create them in your models for you. https://github.com/lomba/schema_validations
Yes you do as noted in other answers, the answer is validate_uniqueness_of - http://ar.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M000086. Note, even though you have a validation in your model a race condition does exist where Rails may try and do two inserts unaware of there being a unique record already in the table
When the record is created, a check is performed to make sure that no
record exists in the database with the given value for the specified
attribute (that maps to a column). When the record is updated, the
same check is made but disregarding the record itself.
Because this check is performed outside the database there is still a
chance that duplicate values will be inserted in two parallel
transactions. To guarantee against this you should create a unique
index on the field. See add_index for more information.
So what you have done, by creating a unique index on the database is right, though you may get database driver exceptions in your exception log. There are workarounds for this, such as detecting when inserts happen (through a double click).
The Michael Hartl Rails Tutorial covers uniqueness validation (re. the "email" field) here. It appears the full uniqueness solution is:
Add the :uniqueness validation to the model.
Use a migration to add the unique index to the DB.
Trap the DB error in the controller. Michael's example is the Insoshi people_controller--search for the rescue ActiveRecord::StatementInvalid statement.
Re. #3, it looks like Michael just redirects to the home page on any DB statement exception, so it's not as complex (nor as accurate) as the parsing suggested by #Ransom Briggs, but maybe it's good enough if, as #Omar Qureshi says, the uniqueness constraint covers 99% of the cases.

ActiveRecord, Postgres and partitioned tables

I've set up a trigger-based partitioning scheme on one of our pg 8.3 databases according to the pg docs here:. Basically, I have a parent table, along with several child tables. An insert trigger on the parent redirects any inserts on the parent into the appropriate child table -- this works well.
The ActiveRecord pg adapter, however, seems to rely on the postgres INSERT ... RETURNING "id" extension to get the id of the returned row after the initial insert. But the trigger seems to break the RETURNING clause -- no id is returned, although the row is created correctly.
While I suppose this behavior makes sense -- after all, nothing is being inserted in the main table, I really need to find some kind of work-around, as other child records will be inserted that require the row id of the just-inserted row.
I suppose I could add some kind of unique id to row prior to insert and then re-read it using this key after insert, but this seems pretty kludgy. Does anyone have a better work-around?
Since Rails v.2.2.1, you can turn off 'returning id' behavior just by overriding #supports_insert_with_returning method in PostgreSQLAdapter.
class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
def supports_insert_with_returning?
false
end
end
Currently it looks like my best option is to just change the table prefix in a before_create event so that the insert happens on the underlying partition table directly, bypassing the insert trigger altogether. This is not a perfect solution, however, but seems to be the most performant and the simplest.
The only other solution I can come up with is to add a guid column to each table, and re-read the row from the parition table by guid immediately after insert to get the id.
Any other suggestions are welcome. Thanx -- m

How to interpret this Rails error?

Hi does anyone know what this means? I only get this error when I app is deployed on a server and using PostgresQL. When I'm running locally and testing on SQLite, it is fine. I have a features_simulations join table, I think it is related to it somehow.
Processing AdminController#confirmed (for 211.30.107.155 at 2009-03-25 09:06:21) [GET]
Session ID: 59d7fdbbb6ec77367c310df0c0928a2a
ActiveRecord::StatementInvalid (PGError: ERROR: relation "features_simulations_id_seq" does not exist
: SELECT currval('features_simulations_id_seq')):
/usr/local/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract_adapter.rb:188:in `log'
/usr/local/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/connection_adapters/postgresql_adapter.rb:503:in `execute'
/usr/local/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/connection_adapters/postgresql_adapter.rb:1000:in `select_raw'
/usr/local/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/connection_adapters/postgresql_adapter.rb:987:in `select'
/usr/local/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/database_statements.rb:7:in `select_all_without_query_cache'
/usr/local/lib/ruby/gems/1.8/gems/activerecord-2.2.2/lib/active_record/connection_adapters/abstract/query_cache.rb:60:in `select_all'
ActiveRecord doesn't really use compound keys. The joining tables still have to have an ID in them for atomic deletes and updates. I think everyone else has said the same thing but in a more roundabout way.
I'm not sure, but maybe you need id in features_simulations table. Id isn't needed if you use has_and_belongs_to_many relations. But I think for has_many :through, you need id column in your join table.
Try adding it in migration:
add_column :features_simulations, :id, :integer, :primary_key
I think "features _simulations _id _seq" is a sequence that has to be created in the database.
This sequence seems to be generating the id for the table.
In Postgres you can use a serial type for an auto-incrementing field, which will automatically create the necessary sequence. You can use an integer type and manually create the sequence if you want, setting a default as the next value from the sequence.
Seems like the code is trying to find the current value of the sequence and failing because the sequence doesn't exist. I'm not sure if rails automatically creates the right type for Postgres primary keys.
Its a problem with the fixtures. Check your Fixture names against your table names. You will get this error if there is a mismatch between the two.

Resources