Seed a has_and_belongs_to_many table relationship - ruby-on-rails

I have a project with 2 models linked through a habtm relationship and
would like to seed the default relationships values as they are fixed and needed for the
web aplication to work.
Can't seem to find a way to access the model join table and seed the default values to the object1_id, object2_id as the table is not linked to a model. It's currently being done via SQL directly on postgreSQL.
Any suggestions?

You would need to generate a join table if you have not already done so:
rails g migration CreateJoinTable users roles
And as long as you have habtm in their respective classes:
# app/models/user.rb
class User < ActiveRecord::Base
has_and_belongs_to_many :roles
end
Just push the object you are creating onto the associative array:
# db/seeds.rb
bill = User.create(name: "bill")
bill.roles << Role.create(title: "admin")

If you can think of any place where you would want to access the model directly, create the model. Otherwise direct sql query is probably fine.

Related

Is it possible to use a joined table as a basis for a model?

What are my options for molding existing database table(s) to my model in rails? I have a has_one and belongs_to relation between two tables, but I'd like to join them and use that as a model (and select only the fields relevant). As this is an external table I'd also like to minimize the amount of queries.
I am inheriting an existing app and would like to not touch anything from the existing environment and slowly migrate. The existing database seems to have been made different from the rails way. I have a model of IdCard and IdCardRequest. One would assume that one IdCard hasmany IdCardRequests, however the IdCard has a property to the last IdCardRequest. It seems that the basic info such as applicant_name is a property of the IdCardRequest rather than IdCard. Luckily they both have a common property id_card_number and I could join it based on that by specifying foreign_key and primary_key to id_card_number. However for now I'd like a model IdCard with the rest of the fields of the IdCardRequest as property.
class IdCard < ExternalTable
self.table_name = 'id_cards'
belongs_to :security_id_request, :foreign_key => 'request_id'
default_scope { includes(:id_request) }
end
class IdRequest < ExternalTable
self.table_name = 'id_request'
has_one :id_card, :foreign_key => 'request_id'
end
# I would like IdCard.first.applicant_lastname
# I have to call IdCard.first.id_request.applicant_lastname
# I have to call IdCard.first.id_request.applicant_firstname
# I could write a delegate_to for every property, but this seems cumbersome and inefficient.
Do you have the option of creating a database view that encapsulates both tables, and renames columns to rails conventions?
e.g.
create view id_card_requests as
select
existing_column as desired_rails_column_name,
...
from id_cards
join id_card_requests on <whatever the join is>
You can then make a rails model IdCardRequests that will work as normal. You can make one of the columns a primary key in the view, or tell the model to use one of the columns with self.primary_key = :my_key_column

Iterate on SQL group by in Rails

Say I have the following models with associations:
House belongs_to User
User has_many House
Is there a way that I can do something like using SQL (not ruby group_by)
House.group(:user).each |houses_grouped_by_user|
#Each of these objects would be a set of houses with the same user id
end
Take a look at the docs for ActiveRecord associations.
As your commenter suggested, based on the associations in your model setup, you should lean on the functionality that the associations provide you!
Thus:
user.houses.each do |user_houses|
# do whatever you want to the user_houses
end
If you want to iterate through the houses in larger groups, you can use other iterative methods -- but start with the association and go from there.

Ruby on Rails ActiveRecord select

I am trying to create a ActiveRecord database by using Ruby on Rails
I have created a database Schema:
To create tables destinations, productions, rules I am using rails generate model Name parameter:integer
How to create a table rule_input with foreign key to rule?
Also, how to create a table or a model sources that would join all these tables and I could get source like: source = Source.find(1) and for example render json: {source: source}?
How to create a table rule_input with foreign key to rule?
Assuming you are asking for cli command - "rails generate model rule_input rule:references"
Also, how to create a table or a model sources that would join all these tables and I could get source like: source = Source.find(1) and for example render json: {source: source}?
Single table inheritance may be a possible solution here.
class Source < ActiveRecord::Base; end
class Rule < Source
has_many :rule_inputs
end
class Production < Source; end
class Destination < Source; end
class RuleInput < ActiveRecord::Base
belongs_to :rule
end
Basically single table inheritance lets different models inherit from a parent model within a single table, as long as your data structure between models are fairly similar this would be a viable option for you. (STI eliminate having 3 tables with all the same columns)
As #maxhungry mentioned you can do STI (Single Table Inheritance) to eliminate using three tables Rules, Productions and Destinations and replace it with "type" column using Source model.
Here are some good articles on STI and about refactoring such models.
https://about.futurelearn.com/blog/refactoring-rails-sti/
http://samurails.com/tutorial/single-table-inheritance-with-rails-4-part-1/
And about your question - do I need to think about model as an object or as a table?
Model is not same as Table as we can have table-less models. You may read this Rails model without a table. So if you subclass your model from ActiveRecord::Base then it will point to a Table. For example, this code
class Product < ActiveRecord::Base
end
This will create a Product model, mapped to a products table at the database. But if you just say
class Product
end
then its a table-less model.

ActiveModel::MissingAttributeError: can't write unknown attribute

I'm running into an issue when trying to associate two records via a belongs_to:
class Enrollment < ActiveRecord::Base
belongs_to :offering,
foreign_key: [:term_id, :class_number]
end
#enrollment = Enrollment.new
#enrollment.offering = Offering.last
This throws:
ActiveModel::MissingAttributeError: can't write unknown attribute `[:term_id, :class_number]'
What am I doing wrong?
Rails unfortunately does not support composite keys. If you need something like this, you'd better go with a custom :has_many like #yannick's comment cites.
On the other side, you could do it with a model that's backed by a view, using a table (say my_offerings) which has a rails-friendly ID, and the two PK columns from the other table (term_id, class_number) and join it with the other table in code.
Then, have a small process that parses the offerings table, and builds any missing my_offering record.
In the end the view will have an ID, and the join in the view (that's transparent for rails) will deal with the composite key.

Model association changes in production environment, specifically converting a model to polymorphic?

I was hoping I could get feedback on major changes to how a model works in an app that is in production already.
In my case I have a model Record, that has_many PhoneNumbers.
Currently it is a typical has_many belongs_to association with a record having many PhoneNumbers.
Of course, I now have a feature of adding temporary, user generated records and these records will have PhoneNumbers too.
I 'could' just add the user_record_id to the PhoneNumber model, but wouldn't it be better for this to be a polymorphic association?
And if so, if you change how a model associates, how in the heck would I update the production database without breaking everything? >.<
Anyway, just looking for best practices in a situation like this.
Thanks!
There's two approaches that might help you with this.
One is to introduce an intermediate model which handles collections of phone numbers. This way your Record and UserRecord can both belong_to this collection model and from there phone numbers and other contact information can be associated. You end up with a relationship that looks like this:
class Record < ActiveRecord::Base
belongs_to :address_book
delegate :phone_numbers, :to => :address_book
end
class UserRecord < ActiveRecord::Base
belongs_to :address_book
delegate :phone_numbers, :to => :address_book
end
class AddressBook < ActiveRecord::Base
has_many :phone_numbers
end
This kind of re-working can be done with a migration and a bit of SQL to populate the columns in the address_books table based on what is already present in records.
The alternative is to make UserRecord an STI derived type of Record so you don't need to deal with two different tables when defining the associations.
class Record < ActiveRecord::Base
has_many :phone_numbers
end
class UserRecord < Record
end
Normally all you need to do is introduce a 'type' string column into your schema and you can use STI. If UserRecord entries are supposed to expire after a certain time, it is easy to scope their removal using something like:
UserRecord.destroy_all([ 'created_at<=?', 7.days.ago ])
Using the STI approach you will have to be careful to scope your selects so that you are retrieving only permanent or temporary records depending on what you're intending to do. As UserRecord is derived from Record you will find they get loaded as well during default loads such as:
#records = Record.find(:all)
If this causes a problem, you can always use Record as an abstract base class and make a derived PermanentRecord class to fix this:
class PermanentRecord < Record
end
Update during your migration using something like:
add_column :records, :type, :string
execute "UPDATE records SET type='PermanentRecord'"
Then you can use PermanentRecord in place of Record for all your existing code and it should not retrieve UserRecord entries inadvertently.
Maintenance page is your answer.
Generate migration which updates table structure and updates existing data. If you're against data updates in migrations - use rake task.
Disable web access (create maintenance page)
Deploy new code
Run pending migrations
Update data
Enable web access (remove maintenance page).

Resources