How to save 2 id in joint table for many-to-many relationship in rails 3.1 - ruby-on-rails

There are two models. One is rfq and another one is standard. It is like:
class Rfq << ActiveRecord::Base
has_and_belongs_to_many :standards
end
class Standard << ActiveRecord::Base
has_and_belongs_to_many :rfqs
end
Table rfqs_standards has been created. My question is when creating rfq, how to save the paid of rfq_id and standard_id in table rfqs_standards automatically.
Thought adding accepts_nested_attributes_for :standard in rfq model. However since there is no real attributes (but just pair of id) saved for this many-to-many relationship, this seems not the right way.
Both rfq and standard was declared in routes.rb as resources :rfqs and resources :standards.
The procedure is when creating rfq, standard will be picked up via a drop down list. Then rfq is saved and at the same time, a new entry in joint table is created. When creating new standard, there is no need to create entry in joint table.
Any suggestion to save the id in joint table? Thanks.

this is easier than you might think because it's handled automatically by ActiveRecord.
When you say "has_and_belongs_to_many", you're telling AR to associate those two models with a many-to-many relationship using the table, and for the most part you no longer need to worry about the join table. When you add an instance of Standard to an Rfq's list of standards, this will be done for you.
Here's an example:
rfq = Rfq.create
standard = Standard.create
rfq.standards << standard
We've created each of the objects, and the third line creates the connection, saving a new record in the rfqs_standards table with the proper ids. rqf.standards looks and acts like a normal array, but when you assign objects to it, ActiveRecord does the database work for you.
After creating the records, you could have also done:
standard.rfqs << rfq
You could also do both at the same time:
rfq = Rfq.create
standard rfq.standards.create
This created an rfq, then created a standard that is automatically connected to the rfq. You can do the same thing in reverse:
standard = Standard.create
rfq = standard.rfqs.create
I hope this helps!
UPDATE: Since you mentioned forms and automatic saving, read my article on nested attributes that shows how to implement that, including full code samples.

Related

Hierarchy within one active record table?

I've been banging my head against this for over a day so here we go:
I have a User model. But there are 3 types of user (more actually, but let's get this done). Let's say there is Talent, Managers, and Directors. But they are all users. A manager has many Talents, Talent has one Manager. Director has many Managers, Manager has one Director. Talent has one Director through Manager.
Obviously, with three separate models, this would be trivial. But they are all users and it is necessary to keep them in the User model.
What is the easiest way of doing this? If I have to use a gem, so be it, but I'd rather not...
Thank you!
Single Table Inheritance is most fitting to your problem description:
Single table inheritance Active Record allows inheritance by storing
the name of the class in a column that by default is named “type” (can
be changed by overwriting Base.inheritance_column). This means that an
inheritance looking like this:
class Company < ActiveRecord::Base; end
class Firm < Company; end
class Client < Company; end
class PriorityClient < Client; end
When you do Firm.create(name: "37signals"), this record will be saved in
the companies table with type = “Firm”. You can then fetch this row
again using Company.where(name: '37signals').first and it will return
a Firm object.
Be aware that because the type column is an attribute on the record
every new subclass will instantly be marked as dirty and the type
column will be included in the list of changed attributes on the
record. This is different from non Single Table Inheritance(STI)
classes:
Company.new.changed? # => false
Firm.new.changed? # => true
Firm.new.changes # => {"type"=>["","Firm"]} If you don't have a
type column defined in your table, single-table inheritance won't be
triggered. In that case, it'll work just like normal subclasses with
no special magic for differentiating between them or reloading the
right type with find.

Rails - assigning the foreign id at item creation between two related model objects

I am having difficulty successfully assigning the foreign key in a one-to-many relationship at the creation of one of the "many" model objects.
In more concrete terms... I have two models, Course and Section, in which Course has_many :sections, and each Section belongs_to :course. Each section, among other attributes, has the foreign key course_id.
I would like to write a helper method called current_course, so that when I write my create function I could write current_course.sections.build(params[:section]) which will automatically assign the foreign key course_id at creation. In other words, current_course would take the id of the class that it was clicked from (i.e. /class/1/) and make equal to course_id of the newly created section. I tried along the lines of #current_course ||= Course.find(params[:course_id]).. but it keeps on throwing an ID error at creation. What am I doing wrong, and how do I correct my current_course method? Thanks!
What you're doing looks basically correct.
Your create controller would generally have a line like this at the beginning. (You can't find something that hasn't been created after all.)
#course = Course.new(params[:course])
If you know that you're going to build exactly one Section, you might want to do #course.sections.build in your new controller. Then from there, make sure your Courses model accepts_nested_attributes_for :sections then you can fill in the Section fields in your view with the fields_for function. When you do it this way, your controller can simply say #course.save and the Section will be saved along with it.
I hope that helps, but if not then please add a comment with the error that you're seeing.

Structuring a many-to-many relationship between models for Rails and Backbone.js

I'm trying to set up an item model and a tag model that have a many-to-many relationship (items have multiple tags and tags belong to multiple items). I'm using Rails and Backbone.js, so I need to have them store, retrieve and update models seamlessly between each other. I would also love it if I could save a new list of tags for a specific item in one go from the client.
What's the correct way to structure the models and controllers on the Rails side and the models on the Backbone side to keep the system RESTful and make it easy to share models between them? Specifically, what would the API look like on the server, and what would the JSON representation of the models be in saving and retrieving them?
I would really appreciate any advice on structure, and I don't really need any code or implementation details -- just a high level setup would be great. Thanks!
Looks like you found your rails answer. Maybe I can help with the backbone side:
Backbone has 2 model constructs: The Model, and the Collection (the collection just being a list of models). There is no formal way of describing relationships with backbone (afaik), so you have to do it yourself. I think what I would do to handle this structure would be 3 collections:
ItemCollection
The item collection would hold all of your items, and each item would, in turn, have it's own TagCollection, which holds the tag models that are related to it.
ItemCollection.TagCollection
Holds references to the main TagCollection instance, but is a local list for this Item only. Since you can '.add' a model to a collection, then you can have multiple collections with the same models populating them.
TagCollection
The TagCollection holds your tags. It's the "main" list of tags that every ItemCollections TagCollection would reference.
For example: You have 3 tags in your TagCollection, and 2 items.
item_1.TagCollection has tag_A and tag_B
item_2.TagCollection has tag_A and tag_C
If, item_1 then has tag_C added to it, you would simply: item_1.TagCollection.add(tag_C) Similarly, removing: item_1.TagCollection.remove(tag_C) would remove it from item_1 collection, but not any others.
Regardless of the methods you utilize, you'll need to write some code in order to have it do mass updates / creates. Remember that backbone just passes the attribute list along as a JSON string in the body of the request when it does a sync. It doesn't care what it sends. So, so long as your controller was setup to accept a list (1 or more) on it's create method, you should be able to do this pretty simply by doing TagCollection.create([list of tags]). The difficult part would be to override the backbone sync to handle the successful creation, and turning [list of tags] into individual models for the collection.
Hope that helps!
[In addition to Pope's answer:]
For reference, the Rails answer (from Creating multiple resources in a single RESTful POST in rails) is to use accepts_nested_attributes_for:
class Item < ActiveRecord::Base
has_many_and_belongs_to :tags
accepts_nested_attributes_for :tags
end
class Tag < ActiveRecord::Base
has_many_and_belongs_to :items
end
The following assumes that you've added ActiveRecord::Base.include_root_in_json = false to one of your initializers (see here for why).
To save a list of tags for an item from Backbone, the answer (from Saving nested objects with Rails, backbone.js, and accepts_nested_attributes_for) is to override sync on the Item model:
sync: (method, model, options) ->
data = JSON.stringify model.toJSON()
if (method == "create" || method == "update")
json = model.attributes
json = _.extend json, {tags_attributes: model.tags.toJSON()}
data = JSON.stringify json
options.data = data
options.contentType = 'application/json'
Backbone.sync method, model, options
This solution might require a bit more hackery to get Rails to understand Backbone, but this is how you would start setting them up.

Rails nested form with uniquness condition

Rails 2.3.5, Ruby 1.8.7.
I have three models, Person, AcademicTerm, and PersonTermStatus.
class PersonTermStatus {
belongs_to :academic_term
belongs_to :person
validates_uniquness_of :academic_term_id, :scope => :person_id
# ...
}
class Person {
has_many :person_term_statuses
}
In a dynamic nested form for a Person record, I allow the editing of the person_term_statuses. But I get validation errors if the user does either of the following:
Deletes a status and creates a new one with the same academic term in the same change.
Swaps the academic terms between two existing statuses.
I understand why this is happening. In (1), the status marked for deletion is not actually deleted before validation of the new status's uniquness condition. In (2), the uniquness condition again is applied before any changes, and it finds another record with the same academic_term.
The problem is, I can't figure a way around this. Is there a known solution?
(My nested form implmenetation is currrently using pretty much exactly the technique from RailsCast [ Part I and Part II )
There is no workaround for this that I know of. However, you can add foreign keys to your database to enforce the uniqueness on the database side and then use the following approach.
Add a before_validation to the parent model that deletes and recreates as new records all the children. Then add a custom validation function that manually checks the children records for uniqueness based on what's in memory (rather than what's in the database).
The downsides to this approach include:
The children don't retain the same IDs.
The created timestamp changes.

How does the Rails' single table inheritance works?

I have a user table, and a teacher that I newly created. The teacher is sub class of user, so, I use scaffold generator to generate the teacher table, than, I modify the model to do teacher is subclass of user. After all that, I did a db:migrate. Then, I go to
http://localhost:3000/teachers/new
It shows an error:
undefined method `teacherSalary' for #<Teacher:0x103331900>
So, my question is what did I do wrong? I want to create a page for doing user register, the user can ONLY be a teacher / student. But I can't add a teacher record ... ... Moreover, I go to
http://localhost:3000/users/new
I want to have a combo box that allow user register their user to be a "teacher" or a "student". But everything seems not work like I expected. What I need to do? Thank you very very much for your help.
Within your database you should have a single table called users. This table should have a string column which by default is called type. If you use another name for this column then you will have to set the inheritance column name manually using self.inheritance_column = "column_name"
Within your application you have three models, User, Student and Teacher. User inherits from ActiveRecord::Base as usual, Student and Teacher both inherit from User.
You should then be able to instantiate new Teacher and Student objects. Internally this works by writing the model name to the type field on the user tables and then when you use Student.find it adds a clause to the SQL to only return rows where the type = 'Student'
You can add shared behaviour to the User class, e.g. validations etc then add additional behaviour to the inherited classes.
A fuller description of how STI works can be found in Martin Fowlers Book(Patterns of Enterprise Application Architecture).
I found this definition really handy:
STI means one table contains the data of more than one model, usually differentiated by the "type" column. ("users" table contains data for the models "Teacher", ""Pupil", "Employee", "Assistant", etc.)
Keeps similar models in the same table instead of creating new ones.
A Polymorphic Association means that one model can be associated with more than one other model(Comment can belong to post, image, file, user_type...)
To prevent foreign key conflicts, the association is reperesented with the *_id and *_type columns instead of only *_id.
For what you have here , I am not sure if STI is the best way go . STI should generally be used when there is a OO like inheritance and the Models have the same Attribute but different behaviour . In your case Teacher and Student can sure have a few shared attributed , but they are also bound to have different ones as well .
You might want to experiment with a polymorphic association as well .

Resources