I want to set up a many-to-many recursive association with the model Projects
a Project has_many Comps (or comparables), which are other Projects
and a Project can be the Comp of many Projects
I'd like to add other columns to each record, so I need a join table
I've done the following research which has not helped:
This article is a clear basic explanation of sql recursive associations, but there is no specifics on activerecord implementation
this stack overflow article deals with a one-to-many relationship,
and not a many-to-many
I tried this rails method of setting up what seems to be multiple
join tables, but it is confusing and did not work for me.
I tried this rails method and it did not work for me, maybe because it assigns primary keys to two columns in a table, which I did not do
In the last link, here is the code in question:
CREATE TABLE tutorship (
tutor_id INTEGER,
tutored_id INTEGER,
hasPaid BOOLEAN,
PRIMARY KEY (tutor_id, tutored_id)
);
how can you have two primary keys in the same table? If that is correct, is this the issue, and how do i set this up?
Generally, how do i set this up in active record?
The term you are looking for seems to be "self referential many to many join".
It's tricky to get right the first time definitely reccomend testing and retesting your associations in rails console as you build this out.
rails many to many self join
http://railscasts.com/episodes/163-self-referential-association?view=comments
```
class Project < ActiveRecord::Base
has_many :comparables
has_many :comps, :through => :comparables
# rest of class omitted.
end
class Comparables < ActiveRecord::Base
belongs_to :project
belongs_to :comp, :class_name => 'Project'
end
```
someone correct me if I err'd in the example above.
You can't have more than 1 primary key. By the very definition this doesn't make sense. You can have more than 1 unique index though. What you are describing is NOT recursive. It is a many to many relationship. I don't quite understand what you really want here because you have some code that talks about tutors but your original comment had to to with projects.
Let's say you have Projects. And any given project can have multiple comps. At the same time any given comp can be associated with more than 1 project. The way you do this is with a union table.
Something along these lines.
create table Projects
(
ProjectID int,
OtherColumnsHere
)
create table Comps
(
CompID int,
OtherColumnsHere
)
create table ProjectComps
(
ProjectID int,
CompID int,
AuditingColumnsLikeDateCreatedHere
)
Related
When working with nested resources in Rails is it possible to use a value/field other than the primary key of the parent resource as the foreign key for the child resource objects?
e.g. if I have "books" that belong to "authors", I pass the "author"'s primary key to the book when it's created with t.references :author, index: true in app/db/migrate/[timestamp]_create_books.rb (right?).
Is it possible to pass the author's name, instead? (Assuming that the "authors" table has a "name" field...)
I ask because I have a preexisting table of books with various fields (author, title, subject, year, etc.) and it seems simpler to create an authors table with the unique authors from the books table and then join them where authors.name=books.author instead of having to figure out a way of getting the unique primary keys from authors to associate with the correct author in books. (But I am probably totally wrong about this.) (In any case, I am curious if it can be done and/or what the proper way of bringing in the preexisting database that lacks the author-book associations would be.)
(I apologize if my terminology is off.)
So, your models:
class Author < ActiveRecord::Base
has_many :books
end
class Book < ActiveRecord::Base
belongs_to :author, primary_key: "name", foreign_key: "author_name"
end
It should work. But this way breaks one of the Rails cornerstones: "Convention over configuration" and that's why you have chances to end up with total mess in your DB someday.
And what else attributes except 'name' Author model has? If there are few of them (or even only one - 'name') it will be better and easier to have only one model 'Book' with the attribute 'author'.
In my rails app, I have three tables: forms,languages and levels.
My forms table holds info about curricula people submit.
It has various columns regarding the submitter personal info, and one of them is their foreign language knowledge level.
My languages table holds the languages that should be present in the curriculum form, and it has the id,name,created_at and updated_at columns.
My levels table holds the language knowledge levels, right now it has only Basic, Medium and Advanced, and it has the id,name,created_at and updated_at columns.
I was able to relate my languages table and my levels table using the following code:
#app/models/level.rb
class Level < ActiveRecord::Base
has_and_belongs_to_many :languages
end
#app/models/language.rb
class Language < ActiveRecord::Base
has_and_belongs_to_many :levels
end
Now, a fourth table exists in my database to relate these two tables, the languages_levels table, which has nothing else but the language_id and the level_id columns.
Is there a way, using Formtastic, to relate this languages_levels table to my forms table in a way that for every language recorded in the languages table, a set of radio button inputs present in the levels table to appear?
For what you described, using the bridge table (using the language_lavels table with has_and_belongs_to_many) seems to be a wrong approach. You just need three-way one-to-many relations.
In other words, you should create a model, say LanguageLevel, which belongs to Form, Language, and Level. Then you can populate LanguageLevel for each Language for each Form.
check this screencasts they might point you in the right direction:
http://railscasts.com/episodes/47-two-many-to-many
http://railscasts.com/episodes/185-formtastic-part-2
I'm doing my first rails(3) application.
Associations don't make sense. First, even the rails guides don't
really explain what they do, they just explain how to use them.
From what I gather, associations do two things:
a) Allow ActiveRecord to optimize the structure of the database.
b) Allow ActiveRecord to offer an alternate ruby syntax for
joins and the like (SQL queries). I want this.
I'm trying to understand associations, and how to properly use them. Based
on the example below, it seems like associations are 'broken' or at least
the documentation is.
Consider a trivial version of my application. A teacher modifying wordlists
for study.
There are 3 relevant tables for this discussion. For clarity, I've simply
included the annotate(1) tool's definition of the table, and removed
unnecessary fields/columns.
A wordlist management table:
Table name: wordlist_mgmnt_records
id :integer not null, primary key
byline_id :integer(8) not null
A table that maps words to a word list:
Table name: wordlists
wordlist_mgmnt_id :integer not null
word_id :integer not null
We don't actually care about the words themselves. But we do care about
the last table, the bylines:
Table name: bylines
id :integer(8) not null, primary key
teacher_id :integer not null
comment :text not null
Bylines record who, what tool was used, where, when, etc. Bylines are
mainly used to trouble shoot what happened so I can explain to users what
they should have done (and/or repair their mistakes).
A teacher may modify one or more word list management records at a time
(aka single byline). Said another way, a single change may update multiple
word lists.
For wordlist_mgmnt_records the associations would be:
has_many :bylines # the same byline id can exist
# in many wordlist_mgmnt_records
But what's the corresponding entry for bylines?
The Beginning Rails 3 (Carneiro, et al) book says:
"Note: For has_one and has_many associations, adding a belongs_to
on the other side of the association is always recommended. The
rule of thumb is that the belongs_to declaration always goes in
the class with the foreign key."
[ Yes, I've also looked at the online rails guide(s) for this. Didn't
help. ]
For the bylines table/class do I really want to say?
belongs_to :wordlist_mgmnt_records
That really doesn't make sense. the bylines table basically belongs_to
every table in the data base with a bylines_id. So would I really say
belongs_to all of them? Wouldn't that set up foreign keys in all of the
other tables? That in turn would make changes more expensive (too many
CPU cycles) than I really want. Some changes hit lots of tables, some of
them very large. I prize speed in normal use, and am willing to wait to
find bylines without foreign keys when using bylines for cleanup/repair.
Which brings us full circle. What are associations really doing in rails,
and how does one use them intelligently?
Just using associations because you can doesn't seem to be the right
answer, but how do you get the added join syntax otherwise?
I'll try to help your confusion....
A byline can have multiple wordlist_mgmnt_records, so defining the has_many there seems to make sense.
I'm not sure I understand your confusion in the other direction. Since you have defined the attribute wordlist_mgmnt_records.byline_id, any given wordlist_mgmnt_record can only 'have' (belong_to) a single byline. You're simply defining the crows foot via ruby (if you like database diagrams):
wordlist_msgmnt_records (many)>>----------(one) byline
Or read in english: "One byline can have many wordlist_mgmnts, and many individual wordlist_mgmnt's can belong to a single byline"
Adding the belongs_to definition to the wordlist_mgmnt model doesn't affect the performance of the queries, it just let's you do things like:
#record = WordlistMgmntRecord.find(8)
#record_byline = #record.byline
Additionally you're able to do joins on tables like:
#records = WordlistMgmntRecord.joins(:byline).where({:byline => {:teacher_id => current_user.id}})
Which will execute this SQL:
SELECT wordlist_mgmnt_records.*
FROM wordlist_mgmnt_records
INNER JOIN bylines
ON wordlist_mgmnt_records.byline_id = bylines.id
WHERE bylines.teacher_id = 25
(Assuming current_user.id returned 25)
This is based off of your current DB design. If you find that there's a way you can implement the functionality you want without having byline_id as a foreign key in the wordlist_mgmnt_records table then you would modify your models to accomodate it. However this seems to be how a normalized database should look, and I'm not really sure what other way you would do it.
I am working on an app that will manage students enrolled in a course. The app will have users who can log in and manipulate students. Users can also comment on students. So three of our main classes are Student, User, and Comment. The problem is that I need to associate individual comments with both of the other models: User and Student. So I've started with some basic code like this...
class Student < ActiveRecord::Base
has_many :comments
end
class User < ActiveRecord::Base
has_many :comments
end
class Comment < ActiveRecord::Base
belongs_to :student
belongs_to :user
attr_accessible :comment
end
So in the comments table, a single record would have the following fields:
id
comment
student_id
user_id
created_at
updated_at
This presents several problems. First, the nice Rails syntax for creating associated objects breaks down. If I want to make a new comment, I have to choose between the foreign keys. So...
User.comments.create(attributes={})
OR
Student.comments.create(attributes={})
Another option is to arbitrarily pick one of the foreign keys, and manually add it to the attrs hash. So...
User.comments.create(:comment => "Lorem ipsum", :student_id => 1)
The problem with this option is that I have to list student_id under attr_accessible in my Comment model. But my understanding is that this poses a security risk since someone could technically come along and reassociate the comment with a different student using mass assignment.
This leads to a further question about data modeling in general using Rails. The app I'm currently building in Rails is one that I originally wrote in PHP/MySQL a few years ago. When I first studied SQL, great importance was placed on the idea of normalization. So, for example, if you have a contacts table which stores names and addresses, you would use a lot of foreign key relationships to avoid repeating data. If you have a state column, you wouldn't want to list the states directly. Otherwise you could potentially have thousands of rows that all contain string values like "Texas." Much better to have a separate states table and associate it with your contacts table using foreign key relationships. My understanding of good SQL theory was that any values which could be repeating should be separated into their own tables. Of course, in order to fully normalize the database, you would likely end up with quite a few foreign keys in the contacts table. (state_id, gender_id, etc.)
So how does one go about this in "the Rails way"?
For clarification (sorry, I know this is getting long) I have considered two other common approaches: "has_many :through =>" and polymorphic associations. As best I can tell, neither solves the above stated problem. Here's why:
"has_many :through =>" works fine in a case like a blog. So we have Comment, Article, and User models. Users have many Comments through Articles. (Such an example appears in Beginning Rails 3 from Apress. Great book, by the way.) The problem is that for this to work (if I'm not mistaken) each article has to belong to a specific user. In my case (where my Student model is here analogous to Article) no single user owns a student. So I can't say that a User has many comments through Students. There could be multiple users commenting on the same student.
Lastly, we have polymorphic associations. This works great for multiple foreign keys assuming that no one record needs to belong to more than one foreign class. In RailsCasts episode #154, Ryan Bates gives an example where comments could belong to articles OR photos OR events. But what if a single comment needs to belong more than one?
So in summary, I can make my User, Student, Comment scenario work by manually assigning one or both foreign keys, but this does not solve the issue of attr_accessible.
Thanks in advance for any advice!
I had your EXACT question when I started with rails. How to set two associations neatly in the create method while ensuring the association_ids are protected.
Wukerplank is right - you can't set the second association through mass assignment, but you can still assign the association_id directly in a new line.
This type of association assignment is very common and is littered throughout my code, since there are many situations where one object has more than one association.
Also, to be clear: Polymorphic associations and has_many :through will not solve your situation at all. You have two separate associations (the 'owner' of a comment and the 'subject' of a comment) - they can't be rationalised into one.
EDIT: Here's how you should do it:
#student = Student.find_by_id(params[:id])
#comment = #student.comments.build(params[:comment]) #First association is set here
#comment.user = current_user #Second association is set here
if #comment.save
# ...
else
# ...
end
By using the Object.associations.build, Rails automatically creates a new 'association' object and associates it with Object when you save it.
I think polymorphic association is the way to go. I'd recommend using a plugin instead of "rolling your own". I had great results with ActsAsCommentable (on Github).
As for your attr_accessible problem: You are right, it's more secure to do this. But it doesn't inhibit what you are trying to do.
I assume that you have something that holds the current user, in my example current_user
#student = Student.find(params[:id])
#comment = Comment.new(params[:comment]) # <= mass assignment
#comment.student = #student # <= no mass assignment
#comment.user = current_user # <= no mass assignment
if #comment.save
# ...
else
# ...
end
The attr_accessible protects you from somebody sneaking a params[:comment][:student_id] in, but it won't prevent the attribute from being set another way.
You still can get all comments of your users through the has_many :comments association, but you can also display who commented on a student thanks to the belongs_to :user association:
<h1><%= #student.name %></h1>
<h2>Comments</h2>
<%- #student.comments.each do |comment| -%>
<p><%= comment.text %><br />
by <%= comment.user.name %></p>
<%- end -%>
PLUS:
Don't over engineer your app. Having a state:string field is perfectly fine unless you want to do something meaningful with a State object, like storing all districts and counties. But if all you need to know a students state, a text field is perfectly fine. This is also true for gender and such.
Well, for the first part. If I understood your question correctly, I think, that since comments are listed within students controller, you should associate them through Student model (it just seems logical to me). However, to protect it from assigning wrong user id, you could do something like this
#student = Student.find params[:id]
#student.comments.create :user => current_user
current_user might be a helper that does User.find session[:user_id] or something like that.
has_many :through association doesn't make sense here. You could use it to associate User and Student through Comment, but not User and Comment through Student
Hope that helps.
Like the title says, I want to move from a one-to-one relationship into a many-to-many relationship in my rails 3 app.
I'm fairly good with rails now but I lack a good understanding of databases and migrations.
Currently, I have a Project and User model. A Project belongs_to a User and a User has_many Projects.
I want to move into a situation where a project can have many users collaborating on it at once.
I'm pretty sure I need to set up a has_many :through type of relationship, but I am also curious as to how I can migrate all of my existing projects and users into this type of system.
Thanks!
You need another table that links the two tables together. This is how to support many to many relationships.
e.g. Say you have two tables like this:
Projects
----------
projectid
{other columns}
Users
-------
userid
{other columns}
The new table will look something like this:
New Table
Projects_Users
--------------
projectid
userid
Now you can add another user to a project by simply adding the userid and projectid to
the Projects_Users table. Similarly you will be able to add several users to the one project in the same table.
The primary key on that table is the composite key projectid & userid.
Right, I have the relationship set up now. How do I migrate all of my existing projects and users into this new format table ?
Well, it depends on how you have set the tables up now; how the two existing relations are implemented; where the ProjectUser data is sitting in the old table. Post your DDL. You should be able to simply INSERT ProjectUser SELECT FROM .... We need to complete that FROM.
Also the number of rows is relevant; if it is large, you may have to break it into batches.