7

I'm building a form where a user should be able to create a template with fields. However they should also be able to create an optional Collection which belongs_to each Field at the same time.

So far it looks like this:

Tables

templates: id, name
template_fields: id, template_id, collection_id, field_name
collections: id, name
collection_values: id, collection_id, value

Models

Template

class Template < ActiveRecord::Base
  has_many :template_fields
  accepts_nested_attributes_for :template_fields
end

Template Fields

class TemplateField < ActiveRecord::Base
  belongs_to :template
  belongs_to :collection
end

Collection

class Collection < ActiveRecord::Base
  has_many :collection_values
end

CollectionValue

class CollectionValue < ActiveRecord::Base
  belongs_to :collection
end

How can I create these objects? How do I go about this in the controller?

The only way I can think of is to create it as a nested association (even though Collection isn't really a nested attribute) or create the Collections before the Templates, and somehow link each Field to each Collection that was created.

Controller

def create
  @template = Template.new(template_params)
  if @template.save
    flash[:notice] = "Template successfully created."
    flash[:color]= "valid"
    redirect_to templates_path
  else
    flash[:notice] = "Form is invalid"
    flash[:color]= "invalid"
    render :new
  end
end

def template_params
  params.require(:template).permit(:id,:name, template_fields_attributes: [:id, :template_id, :field_name, :collection_id, collection_values_attributes: [:value] ])
end
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Shannon
  • 2,988
  • 10
  • 21
  • 3
    What happened when you googled for "rails nested-form associations"? (AFAIK there are hundreds of solutions to this problem). What happened when you tried one of those solutions? what errors did you observe? Note: stack overflow isn't really about asking for "the best solution" (that is primarily opinionated) it's about helping you figure out how to get past bugs/issues you've had with a solution that you've already tried yourself. – Taryn East Mar 02 '17 at 23:21
  • I've seen many solutions but none that deal with a nested `belongs_to` association that doesn't exist yet. So it doesn't have an ID to get in the controller, then being able to link it with each field. – Shannon Mar 02 '17 at 23:29
  • I'm unclear what you mean by " a nested belongs_to association that doesn't exist yet. " - do you mean that the belongs_to association doesn't exist yet? or do you mean the association exists, but the nested resource doesn't exist yet? If the latter - then yes, there are lots of solutions that do this... you use `build` to create an empty one in the new/edit action, and `fields_for` in the form to display the fields, then submit the form and see what comes through as `params` and use `nested_attributes_for` - which is all described in lots of other articles :). – Taryn East Mar 03 '17 at 00:13
  • `No it's the former` so... what i meant by "the association doesn't exist" is that "in the class file, you do not have the words `belongs_to` and the association name" which you do... therefore it's the latter. – Taryn East Mar 03 '17 at 03:36
  • What do you have int he relevant controller-action? – Taryn East Mar 03 '17 at 03:37
  • Updated with controller – Shannon Mar 03 '17 at 03:54
  • This is only half of the required controller-actions... also no form... also - I really meant it - googel for nested forms. give it a go... do as much as you can (even if it doesn't fit your situation exactly, do the bits that you can do). See what happens, then come back and tell us what error messages you got. – Taryn East Mar 03 '17 at 04:46
  • So... show us what you tried and the errors you got. :) We will help you make it work, but we really don't want to write it for you. – Taryn East Mar 03 '17 at 05:15
  • I still think your question is not so clear, are you trying to define a relationship for the nesting on your model? Or looking for how to tidy your view and make it more dynamic? – Chidi Ekuma Mar 05 '17 at 21:59
  • @user3402754 the relationships are set-up and the form is set-up, but I don't know how to go about saving these objects in the controller (in a way that isn't convoluted). – Shannon Mar 05 '17 at 22:28
  • @Shannon Is this understanding of your models correct, A `template` has many `tempate_fields` that have a `collection` that has_many `collection_values` – Chidi Ekuma Mar 06 '17 at 11:05
  • It's hard to answer this without seeing your form and jquery. Also, can you include the value of `params` when it comes into the controller? It might end up being easier to just serialize your params into json and then decode it on the server - this will preserve hash and array types. See http://stackoverflow.com/questions/6255344/how-can-i-use-jquery-to-post-json-data – max pleaner Mar 06 '17 at 18:51
  • @user3402754 That's right. But a collection is also optional for each field. – Shannon Mar 06 '17 at 21:52
  • Let me know if the answer is helpful, or if you'd require a code snippet customized it to your particular use-case. – Chidi Ekuma Mar 06 '17 at 22:44

2 Answers2

5

I think you need to add these kind of relationships to your model has_many :through and has_one :through to relate collection and collection_values to template and then modify your template.rb to include:

has_one :collection, through: :template_fields

The line above assumes that one template has only one collection

has_many :collection_values, through: :collection
accepts_nested_attributes_for :template_fields, :collection, :collection_values

You also need to modify the template_params in your controller to include collection_attributes and collection_values_attributes

For a more detailed implementation, your template_model will look like this

Template

class Template < ActiveRecord::Base
  has_many :template_fields
  has_many :collections, through: :template_fields
  has_many :collection_values, through: :collection
  accepts_nested_attributes_for :template_fields, :collections, :collection_values
end
Chidi Ekuma
  • 2,811
  • 2
  • 19
  • 30
  • this might not work - a template can have many collections (a template can have many fields, and each field can have a collection) – Shannon Mar 06 '17 at 22:51
  • 1
    Instead of `has_one :collection` use `has_many :collections` and remember to pluralize `collection` in `nested_attr` declaration – Chidi Ekuma Mar 06 '17 at 23:12
4

For requirements given you may have:

class CollectionValue < ActiveRecord::Base
  belongs_to :collection
end

class Collection < ActiveRecord::Base
  has_many :collection_values
  accepts_nested_attributes_for :collection_values
end

class TemplateField < ActiveRecord::Base
  belongs_to :template
  belongs_to :collection
  accepts_nested_attributes_for :collection
end

class Template < ActiveRecord::Base
  has_many :template_fields
  accepts_nested_attributes_for :template_fields
end

That would make following code work:

Template.create name: 'template_name',
  template_fields_attributes: [
    {name: 'field_name', collection_attributes:
      {name: 'collection_name', collection_values_attributes: [
        {name: 'col_value_1'},
        {name: 'col_value_2'}
      ]}
    }
  ]

You may try it in rails console. This example would create all of records as it doesn't include any IDs. It would work same way in TemplateController#create

It's better for you to read and understand accepts_nested_attributes_for

Dmitriy Budnik
  • 1,467
  • 13
  • 22
  • If I figured it out correctly you have 0 or 1 collection per template_value. In that case you could have just 3 models: Move `collection_name` to `TemplateField` and you don't need collection model. You could just make CollectionValue's belong_to TemplateField – Dmitriy Budnik Mar 12 '17 at 14:36
  • This is the right answer and what I used before switching to an AJAX approach (must be able to add a collection built on-the-fly to multiple fields). – Shannon Mar 13 '17 at 21:59