I cannot seem to save my record with nested forms.
This is what pry says:
pry(#<VenuesController>)> #venue.save
(0.3ms) begin transaction
Tag Exists (0.2ms) SELECT 1 AS one FROM "tags" WHERE ("tags"."name" = 'All ' AND "tags"."id" != 2) LIMIT 1
Tag Exists (0.1ms) SELECT 1 AS one FROM "tags" WHERE "tags"."name" = '' LIMIT 1
(0.1ms) rollback transaction
=> false
I thought I followed everything correctly.
This is my nested form for tags
Tags:
<%= f.collection_check_boxes :tag_ids, Tag.all, :id, :name %><br>
Make a New Tag Here: <br>
<%= f.fields_for :tags, Tag.new do |tag_field| %>
<%= tag_field.label :name_tag %>
<%= tag_field.text_field :name %>
<% end %>
This is my venue model
class Venue < ActiveRecord::Base
has_many :venue_tags
has_many :tags, :through => :venue_tags
accepts_nested_attributes_for :tags, allow_destroy: true
end
And my tag model
class Tag < ActiveRecord::Base
has_many :venue_tags
has_many :venues, :through => :venue_tags
validates_uniqueness_of :name
end
However, when take off the 'validate uniqueness of name', I can save it, but new tags are added by itself.
And this is the pry log that tells it's true, but now I'm getting the correct tag added to the venue, but ALSO a new tag added to the venue (one I did not create myself). I am assuming this is happening because the New Tag Fields_for text was blank, which created a new tag by itself.
(0.2ms) begin transaction
SQL (0.6ms) INSERT INTO "venues" ("name", "address", "discount", "latitude", "longitude", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?) [["name", "SPICEBOX"], ["address", "33 Pell St, New York, NY 10013, United States"], ["discount", "10% OFF with Student ID"], ["latitude", 40.714831], ["longitude", -73.998628], ["created_at", "2015-11-03 06:12:52.400643"], ["updated_at", "2015-11-03 06:12:52.400643"]]
SQL (0.2ms) INSERT INTO "venue_tags" ("tag_id", "venue_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["tag_id", 2], ["venue_id", 11], ["created_at", "2015-11-03 06:12:52.404715"], ["updated_at", "2015-11-03 06:12:52.404715"]]
SQL (0.2ms) INSERT INTO "tags" ("name", "created_at", "updated_at") VALUES (?, ?, ?) [["name", ""], ["created_at", "2015-11-03 06:12:52.408821"], ["updated_at", "2015-11-03 06:12:52.408821"]]
SQL (0.1ms) INSERT INTO "venue_tags" ("venue_id", "tag_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["venue_id", 11], ["tag_id", 9], ["created_at", "2015-11-03 06:12:52.411692"], ["updated_at", "2015-11-03 06:12:52.411692"]]
(1.4ms) commit transaction
=> true
You should probably add a validates :presence to tag name. You seem to have a tag in your db that has no name and when you add another with no name it is not unique and won't pass validations.
Take a look at the form or the strong parameters to see how that value is being cleared, if it is.
If it's blank because it was intentionally submitted that way, you can ignore it if blank.
accepts_nested_attributes_for :tags, allow_destroy: true, :reject_if => lambda { |a| a[:name].blank? }
However, when take off the 'validate uniqueness of name', I can save
it
Check the database for the name and see if it already exists.
Since you are able to save the record, you code is probably ok.
To see why the record doesn't want to save, you can ask out the errors of the object you are trying to save. You can do this in your pry console.
#venue.save
...
=> false
#venue.errors.messages
=> ...
It will probably tell you that you already have a record with the same name. If that it's the case, then it would make sense your record isn't saving.
Related
I'm creating an app to represent the pedigree of livestock. Each child has one dam (e.g. ewe) and one sire (e.g. ram). A dam/sire pairing can have multiple children (e.g. lambs) and a dam and sire may have many more children independent of the other. I am trying to represent this relationship so that I could do something like ewe.children and get a listing of her offspring. Similarly, I'd like to be able to do something like lamb.ewe to get her mother or lamb.ewe.ewe to get her maternal grandmother.
from schema.rb...
create_table "parent_child_relationships", force: :cascade do |t|
t.integer "parent_id"
t.integer "child_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["child_id"], name: "index_parent_child_relationships_on_child_id"
t.index ["parent_id", "child_id"], name: "index_parent_child_relationships_on_parent_id_and_child_id", unique: true
t.index ["parent_id"], name: "index_parent_child_relationships_on_parent_id"
end
from parent_child_relationship.rb...
class ParentChildRelationship < ApplicationRecord
belongs_to :sire, class_name: "Animal"
belongs_to :dam, class_name: "Animal"
belongs_to :children, class_name: "Animal"
end
from animal.rb...
has_one :sire_relationship, class_name: "ParentChildRelationship",
foreign_key: "child_id",
dependent: :destroy
has_one :dam_relationship, class_name: "ParentChildRelationship",
foreign_key: "child_id",
dependent: :destroy
has_many :child_relationships, class_name: "ParentChildRelationship",
foreign_key: "parent_id",
dependent: :destroy
has_one :sire, through: :sire_relationship, source: :child
has_one :dam, through: :dam_relationship, source: :child
has_many :children, through: :child_relationships, source: :parent
In the console, I run the following commands to grab the animals I want to relate to each other...
s = Shepherd.first
ewe = s.animals.find_by(id: 37)
ram = s.animals.find_by(id: 133)
lamb = s.animals.find_by(id: 61)
Now, when I try to create the sire_relationship and dam_relationship I get an error since it doesn't seem to see the relationship as being unique. The sire_relationship is replaced by the dam_relationship...
>> lamb.create_sire_relationship(parent_id: ram.id)
(0.1ms) begin transaction
SQL (0.7ms) INSERT INTO "parent_child_relationships" ("parent_id", "child_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["parent_id", 133], ["child_id", 61], ["created_at", "2018-01-15 15:33:06.649936"], ["updated_at", "2018-01-15 15:33:06.649936"]]
(2.5ms) commit transaction
ParentChildRelationship Load (0.2ms) SELECT "parent_child_relationships".* FROM "parent_child_relationships" WHERE "parent_child_relationships"."child_id" = ? LIMIT ? [["child_id", 61], ["LIMIT", 1]]
=> #<ParentChildRelationship id: 1, parent_id: 133, child_id: 61, created_at: "2018-01-15 15:33:06", updated_at: "2018-01-15 15:33:06">
>> lamb.create_dam_relationship(parent_id: ewe.id)
(0.1ms) begin transaction
SQL (0.6ms) INSERT INTO "parent_child_relationships" ("parent_id", "child_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["parent_id", 37], ["child_id", 61], ["created_at", "2018-01-15 15:33:35.045703"], ["updated_at", "2018-01-15 15:33:35.045703"]]
(1.0ms) commit transaction
ParentChildRelationship Load (0.1ms) SELECT "parent_child_relationships".* FROM "parent_child_relationships" WHERE "parent_child_relationships"."child_id" = ? LIMIT ? [["child_id", 61], ["LIMIT", 1]]
(0.0ms) begin transaction
SQL (0.5ms) DELETE FROM "parent_child_relationships" WHERE "parent_child_relationships"."id" = ? [["id", 1]]
(1.2ms) commit transaction
=> #<ParentChildRelationship id: 2, parent_id: 37, child_id: 61, created_at: "2018-01-15 15:33:35", updated_at: "2018-01-15 15:33:35">
Creating the children_relationships, I get these errors...
>> ewe.child_relationships.create(child_id: lamb.id)
(0.1ms) begin transaction
SQL (0.9ms) INSERT INTO "parent_child_relationships" ("parent_id", "child_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["parent_id", 37], ["child_id", 61], ["created_at", "2018-01-15 15:37:11.436086"], ["updated_at", "2018-01-15 15:37:11.436086"]]
(0.1ms) rollback transaction
ActiveRecord::RecordNotUnique: SQLite3::ConstraintException: UNIQUE constraint failed: parent_child_relationships.parent_id, parent_child_relationships.child_id: INSERT INTO "parent_child_relationships" ("parent_id", "child_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)
from (irb):13
>> ram.child_relationships.create(child_id: lamb.id)
(0.1ms) begin transaction
SQL (0.6ms) INSERT INTO "parent_child_relationships" ("parent_id", "child_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["parent_id", 133], ["child_id", 61], ["created_at", "2018-01-15 15:37:25.264947"], ["updated_at", "2018-01-15 15:37:25.264947"]]
(2.5ms) commit transaction
Finally, if I check to see whether I can access the sire of lamb, I get another error...
>> lamb.dam
ActiveRecord::HasManyThroughSourceAssociationNotFoundError: Could not find the source association(s) :child in model ParentChildRelationship. Try 'has_many :dam, :through => :dam_relationship, :source => <name>'. Is it one of sire, dam, or children?
from (irb):21
>> lamb.sire
ActiveRecord::HasManyThroughSourceAssociationNotFoundError: Could not find the source association(s) :child in model ParentChildRelationship. Try 'has_many :sire, :through => :sire_relationship, :source => <name>'. Is it one of sire, dam, or children?
from (irb):22
I get similar errors if I do ewe.children or ram.children.
I'm looking for an extra pair of eyes to tell me what I'm doing wrong or whether there's an easier way to achieve what I'm after.
The problem is that you only have one parent_id in your animals table which can only store the ID of a single parent. This works for bacteria but not for animals which have two parents. Your parent_id is getting written to when you set the dam and the sire.
There are multiple ways to do this but I think the simplest is to have a dam_id and sire_id in your animals table.
This is the migration to create the table:
class CreateAnimals < ActiveRecord::Migration[5.1]
def change
create_table :animals do |t|
t.integer :dam_id, index: true
t.integer :sire_id, index: true
t.timestamps
end
end
end
This is what your model will look like this. Notice that you need two belongs_to/has_many relationships:
class Animal < ApplicationRecord
belongs_to :dam, class_name: 'Animal'
belongs_to :sire, class_name: 'Animal'
has_many :children_as_sire, class_name: 'Animal', foreign_key: :sire_id
has_many :children_as_dam, class_name: 'Animal', foreign_key: :dam_id
def children
children_as_dam + children_as_sire
end
end
Notice the getter method children that grabs both children_as_dam and children_as_sire. This will result in two SQL queries which is not ideal. If you're tracking the sec of the Animal, you could do something like:
def children?
case sex
when 'male'
children_as_sire
when 'female'
children_as_dam
end
end
I wrote some specs to demonstrate:
require 'rails_helper'
RSpec.describe Animal, type: :model, focus: true do
it 'can be created' do
expect { Animal.create }.to_not raise_error
end
it 'can have a dam' do
animal = Animal.new
animal.update! dam: Animal.create
expect(animal.dam).to be_a(Animal)
expect(animal.sire).to be_nil
end
it 'can have a sire' do
animal = Animal.new
animal.update! sire: Animal.create
expect(animal.sire).to be_a(Animal)
expect(animal.dam).to be_nil
end
it 'can have both a dam and a sire and tell the difference' do
dam = Animal.create
sire = Animal.create
child = Animal.create dam: dam, sire: sire
expect(child.reload.dam).to eq(dam)
expect(child.reload.sire).to eq(sire)
end
it 'grandma' do
grandma = Animal.create
dam = Animal.create dam: grandma
child = Animal.create dam: dam
expect(child.reload.dam.dam).to eq(grandma)
end
it 'has children' do
sire = Animal.create
animal = Animal.create sire: sire
expect(sire.reload.children).to include(animal)
end
end
Notice that you can't add children to a model:
animal = Animal.create
animal.children << Animal.create # will raise an error
Instead, you have to manually set the sire and dam (which is probably what you want to do since you're keeping track).
As far as I know, assign_attributes (unlike update_attributes) is not supposed to save the record or for that matter, any record.
So it quite startled me when I discovered that this is not true when supplying _ids for a has_many through: relation.
Consider the following example:
class GroupUser < ApplicationRecord
belongs_to :group
belongs_to :user
end
class Group < ApplicationRecord
has_many :group_users
has_many :users, through: :group_users
end
class User < ApplicationRecord
has_many :group_users
has_many :groups, through: :group_users
validates :username, presence: true
end
So we have users and groups in an m-to-m relationship.
Group.create # Create group with ID 1
Group.create # Create group with ID 2
u = User.create(username: 'Johny')
# The following line inserts two `GroupUser` join objects, despite the fact
# that we have called `assign_attributes` instead of `update_attributes`
# and, equally disturbing, the user object is not even valid as we've
# supplied an empty `username` attribute.
u.assign_attributes(username: '', group_ids: [1, 26])
The log as requested by a commenter:
irb(main):013:0> u.assign_attributes(username: '', group_ids: [1, 2])
Group Load (0.2ms) SELECT "groups".* FROM "groups" WHERE "groups"."id" IN (1, 2)
Group Load (0.1ms) SELECT "groups".* FROM "groups" INNER JOIN "group_users" ON "groups"."id" = "group_users"."group_id" WHERE "group_users"."user_id" = ? [["user_id", 1]]
(0.0ms) begin transaction
SQL (0.3ms) INSERT INTO "group_users" ("group_id", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["group_id", 1], ["user_id", 1], ["created_at", "2017-06-29 08:15:11.691941"], ["updated_at", "2017-06-29 08:15:11.691941"]]
SQL (0.1ms) INSERT INTO "group_users" ("group_id", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["group_id", 2], ["user_id", 1], ["created_at", "2017-06-29 08:15:11.693984"], ["updated_at", "2017-06-29 08:15:11.693984"]]
(2.5ms) commit transaction
=> nil
I daresay that update_attributes and the _ids construct are mostly used for processing web forms - in this case a form that updates the user itself as well as its group association. So I think it is quite safe to say that the general assumption here is all or nothing, and not a partial save.
Am I using it wrong in some way?
#gokul-m suggests reading about the issue at https://github.com/rails/rails/issues/17368. One of the comments in there points to a temporary workaround: https://gist.github.com/remofritzsche/4204e399e547ff7e3afdd0d89a5aaf3e
an example of my solution to this problem:
ruby:
def assign_parameters(attributes, options = {})
with_transaction_returning_status {self.assign_attributes(attributes, options)}
end
You can handle validation with assign_attributes like so
#item.assign_attributes{ year: "2021", type: "bad" }.valid?
I have two models.
Parent model Tag:
class Tag < ApplicationRecord
has_many :keywords, inverse_of: :tag, dependent: :destroy
accepts_nested_attributes_for :keywords
validates :keywords, presence: true
end
As you can see tag should have at least one keyword.
Child model Keyword:
class Keyword < ApplicationRecord
belongs_to :tag, inverse_of: :keywords
validates :tag, presence: true
end
Here is the code of FactoryGirl factories
tag factory:
FactoryGirl.define do
factory :tag do
sequence(:name) { |n| "Tag#{n}" }
after(:build) do |tag_object|
tag_object.keywords << build(:keyword, tag: tag_object)
end
end
end
keyword factory:
FactoryGirl.define do
factory :keyword do
tag
sequence(:name) { |n| "Keyword#{n}" }
end
end
When I create a new record in keywords table with keyword factory it creates one more record in keywords table which is associated with the same parent record in tags table.
How to omit creating one more record in keywords table and keep factories valid?
irb(main):023:0> FactoryGirl.create :keyword
(0.1ms) BEGIN
Keyword Exists (0.7ms) SELECT 1 AS one FROM "keywords" WHERE "keywords"."name" = $1 LIMIT $2 [["name", "Keyword1"], ["LIMIT", 1]]
Tag Exists (0.3ms) SELECT 1 AS one FROM "tags" WHERE "tags"."name" = $1 LIMIT $2 [["name", "Tag1"], ["LIMIT", 1]]
SQL (0.5ms) INSERT INTO "tags" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["name", "Tag1"], ["created_at", 2017-01-21 19:20:14 UTC], ["updated_at", 2017-01-21 19:20:14 UTC]]
SQL (0.6ms) INSERT INTO "keywords" ("tag_id", "name", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["tag_id", 36], ["name", "Keyword1"], ["created_at", 2017-01-21 19:20:14 UTC], ["updated_at", 2017-01-21 19:20:14 UTC]]
(10.4ms) COMMIT
(0.1ms) BEGIN
Keyword Exists (0.4ms) SELECT 1 AS one FROM "keywords" WHERE "keywords"."name" = $1 LIMIT $2 [["name", "Keyword2"], ["LIMIT", 1]]
SQL (0.4ms) INSERT INTO "keywords" ("tag_id", "name", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["tag_id", 36], ["name", "Keyword2"], ["created_at", 2017-01-21 19:20:14 UTC], ["updated_at", 2017-01-21 19:20:14 UTC]]
(4.4ms) COMMIT
=> #<Keyword id: 63, tag_id: 36, name: "Keyword2", created_at: "2017-01-21 19:20:14", updated_at: "2017-01-21 19:20:14">
irb(main):024:0>
You can see that it created a record in tags, a record in keywords table, and after that one more record in keywords table.
FactoryGirl creates all the stated associations for the model during the build process. Which means a FactoryGirl.build :keyword will do a FactoryGirl.create :tag so it will have an id for Keyword#tag_id to help pass validations on the Keyword model.
This is consistent with the database activity you are seeing.
irb(main):023:0> FactoryGirl.create :keyword
### keywordA = Keyword.new
### call create(:tag) because of association
### tag1 = Tag.new
### call build(:keyword) in after(:build)
###.keywordB.new(tag: tag1) # which prevents trying to make a new tag!
### tag1.save # which saves the keywordB
(0.1ms) BEGIN
Keyword Exists (0.7ms) SELECT 1 AS one FROM "keywords" WHERE "keywords"."name" = $1 LIMIT $2 [["name", "Keyword1"], ["LIMIT", 1]]
Tag Exists (0.3ms) SELECT 1 AS one FROM "tags" WHERE "tags"."name" = $1 LIMIT $2 [["name", "Tag1"], ["LIMIT", 1]]
SQL (0.5ms) INSERT INTO "tags" ("name", "created_at", "updated_at") VALUES ($1, $2, $3) RETURNING "id" [["name", "Tag1"], ["created_at", 2017-01-21 19:20:14 UTC], ["updated_at", 2017-01-21 19:20:14 UTC]]
SQL (0.6ms) INSERT INTO "keywords" ("tag_id", "name", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["tag_id", 36], ["name", "Keyword1"], ["created_at", 2017-01-21 19:20:14 UTC], ["updated_at", 2017-01-21 19:20:14 UTC]]
(10.4ms) COMMIT
### keywordA.tag = tag1
### keywordA.save
(0.1ms) BEGIN
Keyword Exists (0.4ms) SELECT 1 AS one FROM "keywords" WHERE "keywords"."name" = $1 LIMIT $2 [["name", "Keyword2"], ["LIMIT", 1]]
SQL (0.4ms) INSERT INTO "keywords" ("tag_id", "name", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["tag_id", 36], ["name", "Keyword2"], ["created_at", 2017-01-21 19:20:14 UTC], ["updated_at", 2017-01-21 19:20:14 UTC]]
(4.4ms) COMMIT
### Since keywordA gets saved after keywordB,
### keywordB gets a 1 from the sequence and
### keywordA gets a 2 from the sequence
=> #<Keyword id: 63, tag_id: 36, name: "Keyword2", created_at: "2017-01-21 19:20:14", updated_at: "2017-01-21 19:20:14">
irb(main):024:0>
This is just the gist of what happens. Personally, I cannot imagine wanting a keyword without it's tag based on the database's schema so I would just call create(:tag) and get the first keyword as mentioned before. But the schema is simple enough so the following should hold up in 100% of the situations we would want to test:
FactoryGirl.define do
factory :tag do
sequence(:name) { |n| "Tag#{n}" }
after(:build) do |this|
this.keywords << build(:keyword) if this.keywords.empty?
end
end
end
FactoryGirl.define do
factory :keyword do
sequence(:name) { |n| "Keyword#{n}" }
after(:build) do |this|
this.tag ||= build(:tag)
end
end
end
build(:tag) # unsaved
build(:tag).keyword # also unsaved
create(:tag) # saved
create(:tag).keyword # also saved
build(:keyword) # unsaved
build(:keyword).tag # also unsaved
create(:keyword) # saved
create(:keyword).tag # also saved
# And it still lets you be specific
create(:tag, keywords: [create(:keyword, name: "More of a phrase")])
create(:keyword, tag: create(:tag, name: "Pop Me!"))
A few more options to consider:
# Fake the association
FactoryGirl.define do
factory :keyword do
sequence(:name) { |n| "Keyword#{n}" }
tag_id 1 # Danger!
# Will make it pass validation but you
# will forget and #tag will not be found
# or not what you expect
end
end
# use a trait
FactoryGirl.define do
factory :keyword do
sequence(:name) { |n| "Keyword#{n}" }
trait :with_tag do
tag
end
end
end
# make a new factory
FactoryGirl.define do
# do not need parent if inside the "factory :tag do"
factory :tag_with_keyword, parent: :tag do
sequence(:name) { |n| "Tag#{n}" }
keyword
end
end
# but now we are back to it creating the keyword on build(:tag)
FactoryGirl does give you enough options to solve many situations but the trick is understanding how it sets up the associations and try to stay away from setting up implicit circular ones.
Keywords and tags cannot exist independent of each other. Your tag factory creates a keyword every time it is called, so you should be calling the tag factory. Try this:
tag = FactoryGirl.create(:tag)
keyword = tag.keywords.first
I cannot seem to find out why my has_many record will not save. I've test with has_one in the models and it works fine but when I change it to has_many it doesn't work. I've check a few simular stackoverflow posts and I seem to have this correct but it doesn't work. Nested form in activeadmin not saving updates & ActiveAdmin Form not saving nested object
This is for Rails version 5.1.0.alpha and Activeadmin version 1.0.0.pre4
I do not get any errors nor do I see the nested object get created though I see the params when I create an object here is the output
Started POST "/admin/abq_districts" for ::1 at 2016-12-05 10:04:44 -0700
Processing by Admin::AbqDistrictsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"bHzfUG8xIMI66tTHNwSmL5FVnaa4nyuzJ3AM8tnLJSq69TLP1o8iUcLLeNnoS0FVgA8ju3x7Ioc+EV4xRv/T7Q==", "abq_districts_district"=>{"name"=>"test district", "title"=>"", "sub_title"=>"", "paragraph"=>"", "sections_attributes"=>{"0"=>{"text"=>"test section"}}}, "commit"=>"Create District"}
AdminUser Load (1.0ms) SELECT "admin_users".* FROM "admin_users" WHERE "admin_users"."id" = $1 ORDER BY "admin_users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
(0.2ms) BEGIN
SQL (0.4ms) INSERT INTO "abq_districts_districts" ("name", "title", "sub_title", "paragraph", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["name", "test district"], ["title", ""], ["sub_title", ""], ["paragraph", ""], ["created_at", "2016-12-05 17:04:44.756215"], ["updated_at", "2016-12-05 17:04:44.756215"]]
(0.6ms) COMMIT
Redirected to http://localhost:3000/admin/abq_districts/41
Completed 302 Found in 34ms (ActiveRecord: 2.1ms)
here is my info
ActiveAdmin.register
ActiveAdmin.register AbqDistricts::District, as: 'ABQ Districts' do
permit_params :name, :title, :sub_title, :paragraph, sections_attributes: [ :text, :_destroy ]
index do
selectable_column
id_column
column :name
column :title
column :sub_title
column :paragraph
column :created_at
column :updated_at
actions
end
filter :name
filter :title
filter :updated_at
filter :created_at
form do |f|
f.inputs "Admin Details" do
f.input :name
f.input :title
f.input :sub_title
f.input :paragraph
f.inputs do
f.has_many :sections, heading: 'Sections', allow_destroy: true, new_record: true do |a|
a.input :text
end
end
end
f.actions
end
end
abq_districts.rb
module AbqDistricts
def self.table_name_prefix
'abq_districts_'
end
end
district.rb
class AbqDistricts::District < ApplicationRecord
has_many :sections, foreign_key: :abq_districts_district_id
accepts_nested_attributes_for :sections, allow_destroy: true
end
section.rb
class AbqDistricts::Section < ApplicationRecord
belongs_to :abq_districts_district
validates_presence_of :abq_districts_district
end
UPDATE
I just testing in the rails console and it worked fine (still does not work through ActiveAdmin browser
2.3.0 :001 > params = {"name"=>"test district", "title"=>"", "sub_title"=>"", "paragraph"=>"", "sections_attributes"=>{"0"=>{"text"=>"test section"}}}
=> {"name"=>"test district", "title"=>"", "sub_title"=>"", "paragraph"=>"", "sections_attributes"=>{"0"=>{"text"=>"test section"}}}
2.3.0 :002 > d = AbqDistricts::District.create(params)
(0.2ms) BEGIN
SQL (0.5ms) INSERT INTO "abq_districts_districts" ("name", "title", "sub_title", "paragraph", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id" [["name", "test district"], ["title", ""], ["sub_title", ""], ["paragraph", ""], ["created_at", "2016-12-05 18:58:02.973482"], ["updated_at", "2016-12-05 18:58:02.973482"]]
SQL (2.2ms) INSERT INTO "abq_districts_sections" ("text", "abq_districts_district_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["text", "test section"], ["abq_districts_district_id", 63], ["created_at", "2016-12-05 18:58:02.979025"], ["updated_at", "2016-12-05 18:58:02.979025"]]
(0.4ms) COMMIT
=> #<AbqDistricts::District id: 63, name: "test district", title: "", sub_title: "", paragraph: "", created_at: "2016-12-05 18:58:02", updated_at: "2016-12-05 18:58:02">
I was able to patch by adding in ActiveAdmin.register
controller do
def create
#section = AbqDistricts::District.create!(permitted_params[:abq_district].as_json)
redirect_to admin_faq_sections_path, notice: "Section was successfully created!"
end
end
This is just a patch. Notice and the only way it worked is if I used .as_json
it would not work with to_json or just the params alone
Using activeadmin in application, and added nested form which creates record twice on create action, update action is woking fine.
Using Course and Image model.
Gemfile
gem 'rails', '~> 5.0.0'
gem 'activeadmin', github: 'activeadmin'
Course.rb
class Course < ApplicationRecord
has_many :images, dependent: :destroy
validates_presence_of :title
accepts_nested_attributes_for :images, allow_destroy: true
end
Image.rb
class Image < ApplicationRecord
belongs_to :course
mount_uploader :image, ImageUploader
validates_presence_of :image
end
active_admin/course.rb
ActiveAdmin.register Course do
permit_params :title, :description, :publish, images_attributes: [:id, :image, :_destroy]
index do
column :title
column (:description) { |c| raw(c.description) }
column :publish
actions
end
show do
attributes_table do
row :title
row (:description) { |c| raw(c.description) }
row :publish
row "Images" do |c|
ul do
c.images.each do |img|
li do
image_tag(img.image.url(:small))
end
end
end
end
end
end
form do |f|
f.inputs "Course Details" do
f.input :title, :placeholder => "eg. General English"
f.input :description
f.input :publish
end
f.inputs "Images" do
f.has_many :images, heading: false, allow_destroy: true, new_record: true, html: {multipart: true} do |i|
i.input :image, :as => :file, :hint => image_tag(i.object.image)
end
end
f.actions
end
end
console log
Processing by Admin::CoursesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"mK74DdPZ37i3YnMjwq2bej2j/+F0BvAXVJbn5KZamoyGq3A1xeBXPLVCfrPdS4mY9RPYvaC2ZMrZdp36RO3DRw==", "course"=>{"course_type_id"=>"2", "title"=>"General Englisj", "description"=>"this is the description of the course", "publish"=>"1", "images_attributes"=>{"0"=>{"image"=>#<ActionDispatch::Http::UploadedFile:0xd585c98 #tempfile=#<Tempfile:/tmp/RackMultipart20160728-8817-5eo5a6.jpg>, #original_filename="pVxrlkgM3GDl.jpg", #content_type="image/jpeg", #headers="Content-Disposition: form-data; name=\"course[images_attributes][0][image]\"; filename=\"pVxrlkgM3GDl.jpg\"\r\nContent-Type: image/jpeg\r\n">}}}, "commit"=>"Create Course"}
AdminUser Load (1.3ms) SELECT "admin_users".* FROM "admin_users" WHERE "admin_users"."id" = $1 ORDER BY "admin_users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
Location Load (0.9ms) SELECT "locations".* FROM "locations" WHERE 1=0
CACHE (0.0ms) SELECT "locations".* FROM "locations" WHERE 1=0
(0.4ms) BEGIN
SQL (14.3ms) INSERT INTO "courses" ("title", "description", "created_at", "updated_at", "course_type_id") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["title", "General Englisj"], ["description", "this is the description of the course"], ["created_at", 2016-07-28 08:29:24 UTC], ["updated_at", 2016-07-28 08:29:24 UTC], ["course_type_id", 2]]
SQL (58.6ms) INSERT INTO "images" ("image", "course_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["image", "pVxrlkgM3GDl.jpg"], ["course_id", 36], ["created_at", 2016-07-28 08:29:24 UTC], ["updated_at", 2016-07-28 08:29:24 UTC]]
SQL (0.7ms) INSERT INTO "images" ("image", "course_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["image", "pVxrlkgM3GDl.jpg"], ["course_id", 36], ["created_at", 2016-07-28 08:29:24 UTC], ["updated_at", 2016-07-28 08:29:24 UTC]]
(21.4ms) COMMIT
Redirected to http://localhost:3000/admin/courses/36
Completed 302 Found in 3578ms (ActiveRecord: 174.0ms)
so same image get generate twice in application
Finally installing gem locally in rails 5 application works for me.
instead of using github path
gem 'activeadmin', github: 'activeadmin'
i have used following activeadmin version.
gem 'activeadmin', '~> 1.0.0.pre4'
The best way to overcome this is for you to override the activeadmin create action, you can do something like this
controller do
def create
#course = Course.new(permitted_params[:course])
super
end
end