Is there a way to generate a scaffold for Rails models that have either a has_and_belongs_to_many or has_many :through relationship? If not, is there a developmental reason why some basic form of this functionality has not been included? Rails requires developers to generate and edit a "custom" "join table migration." Is the necessary attention to detail a way of reminding Rails developers of some important fact or is this simply an example of how Rails is a 'work in progress?'
A quick Stackoverflow search for:
"has_and_belongs_to_many" yields 821 questions
"has_many :through" yields 933 questions
... and many of these start with "How do I..." So it seems the topic is not trivial and relevant to SO users. I Guess I am wondering why it has not received attention. Is the matter more complex than it seems? The feature set for Rails seems so extensive. I keep wondering why scaffolding for associations with join tables has been omitted.
Please focus your answer toward the assertions above or "include statement like, "A scaffolding would have to include..." or "A rails generate script would need..."
Thanks!
I like your question, I have wondered for myself why this is not included in Rails (and as I suspect not available as a Gem). Here are some of the reasons why that could be a problem. I will them explain at the typical example with assemblies and parts.
Scaffolding works by using generators to create files. If you migrate to a newer version, there is sometimes the option to update existing files by doing the following steps:
Copy existing ones to a new name.
Overwrite existing ones by new ones.
That implies that there is no notion of adding to files or changing files.
EDIT: This is not true, there are mechanisms to add to files and even change in files.
If you look at the generation of e.g. has_many :through (should be similar to has_and_belongs_to_many) you have to do the following things:
Add a migration that creates the join table.
==> Should be possible for scaffolding by doing: rails g scaffold_hmt Assembly Part
Add a model for that join model.
==> Should be possible for scaffolding by the previous scaffold.
Change existing models to include the lines:
assembly.rb: has_many 'assemblies_parts'; has_many :parts, :through => 'assemblies_parts'
part.rb: has_many 'assemblies_parts'; has_many :assemblies, :through => 'assemblies_parts'
==> So no scaffolding possible
What to do with views is wide open. There are examples at RailsCast how to do it, but it is not at all trivial, and there is no one single technique that works well in all circumstances. I do think that the different patterns could be implemented as templates for scaffolding (e.g. to use checkboxes or multi-select lists or text entry with completion). The problem is the same as in has_many views, however.
So as a summary, a scaffold could be worth a try (see the following paragraph). The RailsGuides to Creating and Customizing Rails Generators & Templates seems plausible at least. And a solution that generates parts of files, names them accordingly and helps in the output of the scaffold on the console to do the rest by hand could be worth a try.
I have tried yesterday to come up with a partial solution, you may have a look at it at GitHub. It works like that:
Copy the contents of the directory scaffold_hmt (stands for has_many :through) to your rails application directory lib/generators.
You may call then the generator with: rails g scaffold_hmt Assembly Part.
It will then generate:
Migration for the join table
Model for the join table
It fails to change the files:
Model assembly.rb
Model part.rb
The reason for that is that the finding of the right place is not trivial. As a workaround, it prints out what should have inserted into the files.
c:\apps\ruby\rails3\minds>rails generate scaffold_hmt Assembly Part
create db/migrate/20111011135944_create_assemblies_parts.rb
create app/models/assemblies_part.rb
Try to insert into file: app/models/assembly.rb the following statements:
has_many :assemblies_parts
has_many :parts, :through => :assemblies_parts
insert app/models/assembly.rb
Try to insert into file: app/models/part.rb the following statements:
has_many :assemblies_parts
has_many :assemblies, :through => :assemblies_parts
insert app/models/part.rb
Give it a try and see if that will help you.
Related
I am having trouble figuring out why rails is showing validation errors. The relevant details of my app are as follows:
Programs are offered in Sessions (many to many) (ProgramSession associates Program and Session)
Courses are offered by Instructors (many to many) (CourseInstructor associates Course and Instructors)
Exams are conducted and each Exam has many Papers.
I have generated all resources using scaffold.
Problem: when I try to create a new paper Rails shows
2 errors prohibited this paper from being saved
- Program session must exist
- Course instructor must exist
Entire code is available on github repo, and has also been deployed on heroku
I would really appreciate all the help I can get.
Making following changes in app/models/paper.rb fixed the problem:
belongs_to :program_session, foreign_key: 'program_sessions_id'
belongs_to :course_instructor, foreign_key: 'course_instructors_id'
check if the value and id of foreign-key is the same as it has set in DB.
and can you provide the controller and model to troubleshoot more
Question:
In a post-attr_accessible Rails 4 world, in what way do you recommend, if at all, annotating your ActiveRecord model class files to communicate its (database) attributes?
Further Thoughts
As part of a Rails 3 -> 4 upgrade, we are making a switch, and happily so, away from attr_accessible and to strong parameters in the controller. I understand and agree with the improvement in security via this switch. If you want to know more about this, the information is out there, and it's not hard to find.
But, I enjoyed, in my Rails 3 world, having those reminders of what attributes made up a class up there at the top of the model file. Especially since we're moving toward a world in which ActiveRecord classes are just DAOs, what else is the class but a collection of database attributes? I don't want to go to the schema.rb file just to remember them.
Am I thinking about this incorrectly? In a DAO world, should I be creating my ActiveRecord model class file and then never opening it again?
I know about the annotate_models gem and used it way back in the day. I was not a fan of having the attributes described in commented-out lines. (unreadable, hackish, fragile)
Thoughts? Opinions?
How about:
Person.column_names
If you are using an IDE or and editor that has a console feature this becomes an easy way to be reminded what attributes there are. I am no Ruby or Rails expert, still pretty new here, but I've been using Rails 4 almost exclusively and it just seems like you wouldn't need to see the attributes that often in the model. The params get whitelisted in the controller because that is where they will usually be used, no? If you don't want to use comments you could store an array of the attributes in the model:
my_attr = [:fname, :lname, :age, :height, :weight]
But is that really any more useful than a comment? Would there be a case of attributes that would have been in attr_accessible that wouldn't be in your whitelist in your controller? It would be trick if you put some code in a rake task that would run every time you ran
rake db:...
that would update the my_attr array in your model so you wouldn't have to remember to do it when you modified the model. I go into my models to add class methods and scopes, so I do see a value in it. But I work in RubyMine so I just click on the DB tab on the left side if I need to be reminded of columns that aren't in my whitelist.
Yes, I know how to create many to many relationships but nothing else. I did a google search and read official mongoid documentation but I didn't anything about how to work with it. By work I mean inserting, updating, deleting, finding, counting...
For example, suppose I have 2 models (from documentation):
class Band
include Mongoid::Document
has_and_belongs_to_many :tags
end
class Tag
include Mongoid::Document
field :name, type: String
has_and_belongs_to_many :bands
end
What should I do to make all the operations I mentioned above?
P.S. I use Sinatra and mongoid 3.
Inserting
From the documentation table immediately following your sample models (plus my comment).
# Create a tag for a band
band.tags.create(name: "electro")
Finding
Also from the same table.
# Find a tag that belongs to that band, whose name is "electro"
tag = band.tags.where(name: "electro")
Updating
From the doc on persistence, modified for the band/tags example.
# using variable tag from previous line.
tag.update_attributes(name: "dubstep")
Counting
From the doc on querying, which also contains information useful for the other items here (also modified).
band.tags.length
Deleting
This is from the persistence document also.
# we will delete the tag from earlier
tag.delete
Lastly
All the information needed to work Mongoid is in the docs, which are actually very thorough and useful. There is definitely a lot, but you can make it through it in a couple hours.
I am using awesome_nested_fields gem for create nice and simple nested fields forms.
My problem is the next. I have a model called Product. This has_many :variants. These variants has_many :item_properties, :as => :propertiable
How could create a form for item_properties fields if these belongs to variants (And the form is for products)
Thanks in advance, sorry for my english and if you need more code or information please comment.
Probably is too late, but anyways. Take look at that wiki page https://github.com/lailsonbm/awesome_nested_fields/wiki/Multi-level-Nested-Fields
I haven't found a clear explanation for what the association method does and how to use it properly -- I see several examples where model Alpha has_many Beta and then when creating a new Beta using a Factory we say beta.association :alpha or something along those lines. But isn't Alpha also associated with Beta (Beta belongs_to Alpha)... so I'm just pretty confused. I thought an association (at least in normal English) is usually mutual, so I am not understanding what this method is supposed to do. Can someone please clarify this?
In addition to understanding it on a broad conceptual level, I would also like to know exactly what it does on a syntactical level (ie. is it adding methods like attr_accessor does? like what is this actually doing??)
Sorry I just have not found a clear explanation for this -- if anyone can explain this to me that would be great!!
From my experience you define "association" in FactoryGirl when you need to instantiate associated object while creating other object by factory, and without this association your new object would be invalid.
Let's say you have Company and Worker models, and in your application you have validations which prevent creating Worker with invalid company_id attribute. You can have Company without workers (that's why you shouldn't define association for workers in factory), but you can't have Worker without Company. You then add association in factory to lazy-instantiate Company for every Worker created.
So to summarize - you define association when you have belongs_to in model, and when your association in model also have presence validation.