I have two models; question and category which have a HABTM association between them. Now I want to have a form where I can edit the questions categories, however I don't know how. I started with this but I am lost, I am unsure on what to name the "name" attributes etc and how it is automatically edited/created with the question, how do I set this up?
<%= f.fields_for :categories do |categories_form| %>
<%= categories_form.select "category_ids", Category.all.collect { |c| [c.description, c.id] }, {}, {:multiple => true, :size => 9} %>
<% end %>
I managed to set up question(has_many) --> answer with fields_for and accepts_nested_attributes_for, but not this.
You should take a look at the following screencasts by Ryan Bates Nested Model Form Part 1 and Nested Model Form Part 2.
Migrations
You need to create the migrations for the tables
You need to create the migration for the middle table of the association
+ the middle table name that is created by the association is :categories_questions
or :questions_categories, in the second case you must define the name in models as shown in the link
Do I need to manually create a migration for a HABTM join table?
class CreateCategoriesQuestions < ActiveRecord::Migration
def self.up
create_table :categories_questions, :id => false do |t|
t.references :category
t.references :question
end
add_index :categories_questions, [:category_id, :question_id]
add_index :categories_questions, [:question_id, :category_id]
end
def self.down
drop_table :categories_questions
end
end
Question Model
class Question < ActiveRecord::Base
has_and_belongs_to_many :categories
end
Category Model
class Category < ActiveRecord::Base
has_and_belongs_to_many :questions
end
Controller Stuf
questions_controller.rb
def new
#question = Question.new
#question.categories.build #Build a categories_questions so as to use fields_for
end
Form Stuff
= f.fields_for :categories do |categories_fields|
= categories_fields.text_field :name
= categories_fields.text_field :description
At this point i must tell you ( i am new in ruby & rails ) that to create a new object here you can use jquery to append a html block name properly, or create helpers (that use javascript in the end) to add a new object and on save, save the association.
In the next link someone demonstrated the exact way .
http://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for#512-Setting-child-index-while-using-nested-attributes-mass-assignment
Related
I have tried every possible suggestion I could find on here to make this work but still can't get it to happen.
I have two models: districts and former_offices -- a district can have many former offices and a former office has a district.
Districts:
class District < ApplicationRecord
has_and_belongs_to_many :former_office
end
Former Office:
class FormerOffice < ApplicationRecord
has_and_belongs_to_many :districts
end
Join Model:
class DistrictFormerOffice < ApplicationRecord
belongs_to :district
belongs_to :former_office
end
Here is my form setup for creating a new former office:
<%= bootstrap_form_for(#former_office, label_errors: true) do |f| %>
<%= f.select(:district_ids, District.all.collect { |v| ["#{v.jurisdiction.name} - #{v.name} (District #{v.district}) #{v.term_expires}", v.id ] }, { :include_blank => true}) %></div>
<% end %>
Former Offices controller:
def former_office_params
params.require(:former_office).permit(district_ids: [])
end
Migration:
class CreateJoinTableDistrictFormerOffices < ActiveRecord::Migration[5.2]
def change
create_table :districts_former_offices, id: false do |t|
t.bigint :district_id
t.bigint :former_office_id
end
add_index :districts_former_offices, :district_id
add_index :districts_former_offices, :former_office_id
end
end
I am able to create the Former Office without any errors. All the other fields populate just find, except for "districts." When I pull up the object in the console and type former_office.districts, it comes up as "nil." So it appears either data isn't being added to the database, or is added in the wrong manner.
In the views, if I set it to display #former_office.districts, it just shows up blank.
Let me know if you need further information from my end. I sense this is an easy fix...but just can't figure it out.
I don't think you can add "district_ids" directly into your FormerOffice model. Instead you need to load all the districts from the district model, like #districts = District.find(former_office[:district_ids)), and then do something like #former_office.districts = #districts
project.rb
class Project < ActiveRecord::Base
has_many :details, dependent: :destroy
accepts_nested_attributes_for :details, allow_destroy: true
end
details.rb
class Details < ActiveRecord::Base
belongs_to :project
enum question: {
0: "Question 1...",
1: "Question 2..."
}
end
details table
create_table "details", force: :cascade do |t|
t.integer "project_id"
t.integer "question"
t.string "answer"
end
project form
= form_for #project do |f|
...
%h3 Questions
= f.fields_for :details do |d|
.nested-fields
.field
= d.label (question value here)
= d.hidden_field (question ID here)
= d.text_field :answer
%br/
...
.actions
= f.submit "Submit"
Basically my project has a details table that is suppose to store a collection of 5 questions. Those questions are static and will always be the same. What I thought as a solution was to make and enum, and store in the details table the ID of the question and a string with the answer. But now I'm stuck with the form and I would like some help on how can make a form to create and edit the project details. On the form code you can find exactly what I'm trying to do in pseudo-code. I would also be open to a different implementation solution. Thanks!
You use ActiveRecord::Enum in a wrong way. It is meant to map values to integers:
enum question: {question1: 1, question2: 2},
while you're using it to map integers to values. I'd suggest that you create a simple accessor method:
def questions(index)
["Question1", "Question2"][index]
end
So that you can pick a question like that:
Detail.questions(n)
By the way, if you use Detail just for holding theses questions, get rid of it and put the above method in Project.
I'm trying to print all the comments and their associated users for a post on a blog. The comments are passed into the view, and their users_id and posts_id are accessible, but I cannot access their users; comment.user returns nil.
Model:
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :post
end
Migration:
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.string :text
t.references :users
t.references :posts
t.timestamps
end
end
end
In the controller:
#post = Post.find(params[:id])
#comments = Comment.where(posts_id: params[:id]).order(:created_at)
In the view:
<% #comments.each do |c| %>
<% c.user.nil? %>
<p><%=c.text%></p>
<p><%=c.users_id%></p>
<p><%=c.posts_id%></p>
<% else %>
<p><%=c.text%></p>
<p><%=c.user.name%></p> #Here is where things would break if I didn't check "c.user.nil?"
<% end %>
<% end %>
The text and ids for the comments print as expected, but the user is still considered nil. Does anyone know how to access user from within the comment model in the view?
I believe the issue are the names of the foreign key columns in your database. When declaring a post association while explicitly stating the foreign key (you could do this), it's going to look for post_id on the model. So, you should either change your column in the DB to post_id, or change the association method call to this:
belongs_to :post, foreign_key: :posts_id
In your controller:
Change:
#comments = Comment.where(posts_id: params[:id]).order(:created_at)
To:
#comments = Comment.where(post_id: params[:id]).order(:created_at)
You should have a post_id column in your comments table in the database. Then, you can have access to the user for a given comment like this:
comment.user
and eventually:
comment.user.name
will work too!
Your migrations does not seems right to complete what #Jake Shorty said, I would say When doing :
t.references :users
Rails will actually transform it such as :
t.integer 'users_id'
which does not match with the default Rails conventions, so it cannot retrieve the associated user using user_id foreign key.
To fix it, you need to either change the migration, or change your model :
class Comment < ActiveRecord::Base
belongs_to :user, foreign_key: :users_id
end
I was under the impression that with Rails you're not supposed to define any dependencies in the database, but rather just use your has_many and belongs_to stuff to define relationships. However, I'm going through the rails guide, and it has the following.
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.string :commenter
t.text :body
t.references :post
t.timestamps
end
add_index :comments, :post_id
end
end
I thought this wasn't okay...? I'm trying to do something like a comment field that creates a new instance each time you call the show method, but I think without these "references" and "add_index," it's not storing the post_id in the comment row.
All this migration does is create post_id and tells the database that it should index this column (improves performance)
t.references :post is basically the same as t.integer :post_id so, yes, it is storing the post_id in the comment. You'll still need to define your relationships in your models.
You are actually wrong on the philosophy.
Rails magic is good, only when backed at the DB level by actual foreign keys.
The docs clearly state this
Rails magic comes in, when you have correctly named your foreign keys, so that it can use the convention to figure out the associations.
What's wrong with expressing relationships within the ORM, that's where it's supposed to be done. I believe you are getting mixed up between db vendor specifics such as foreign key constraints and relationships.
class Comment < ActiveRecord::Base
attr_accessible :post, :post_id
belongs_to :post
end
class Post < ActiveRecord::Base
has_many :comments
end
class CommentsController < ApplicationController
def create
#comment = Comment.create(params[:comment]) # where params[:comment] = {post_id: 1, message: ''}
#post = comment.post
respond_with(#comment)
end
end
Imagine I have two models
Film
-name
-description
-duration
-year_made
-rating
-actors
Actor
-name
-d_o_b
-biography
-films
Actors are nested in a Film and vice versa.
How do I represent this relationship in my Ruby models? Realistically I would have a third table mapping actor_id with film_id.
Whilst adding details to a film I would like to be able to create an actor on the fly(if an actor does not exist create a new one with the name supplied)
Thank you in advance.
ADDITION:
Just found a link to a similar question.
You're looking at a Has and Belongs to Many (HABTM) relationship between the two tables.
Read about HABTM relationship in the Rails guides here: http://edgeguides.rubyonrails.org/association_basics.html#has_and_belongs_to_many-association-reference
First you'll need to generate a migration which will look something like this:
class AddActorFilmTable < ActiveRecord::Migration
def self.up
create_table :actors_films, :id => false do |t|
t.integer :actor_id, :null => :false
t.integer :film_id, :null => :false
end
add_index :actors_films, [:actor_id, :film_id], :unique => true
end
def self.down
drop_table :actors_films
end
end
and then specify in your models:
class Actor < ActiveRecord::Base
has_and_belongs_to_many :films
end
class Film < ActiveRecord::Base
has_and_belongs_to_many :actors
end
This will allow you to use all of the additional Rails methods for this type of relationship. To use this in a form, you could follow RailsCast 17: HABTM Checkboxes - though it's old, it should still apply. Alternatively, you can use a gem like Simple Form to easily generate the associations for you like so:
form_for #actor do |f|
f.collection_check_boxes :film_ids, Film.all, :id, :name
end