I have one table for Products and the product can either be in the interior or exterior or both. So I created another table to save the Products location. Now when admin adds the product I have provided the option to select the location(s) the product can be in, but when it is posted the code says the field can't be blank because of the validation. I am not sure what I am missing or the approach is wrong.
The product model:
class Product < ApplicationRecord
validates :name, presence: true
has_many :product_locations
accepts_nested_attributes_for :product_locations
end
The product location model:
class ProductLocation < ApplicationRecord
enum locations: [:exterior, :interior]
validates :location, presence: true
validates :product_id, presence: true
belongs_to :product
end
The ActiveAdmin file for Product:
ActiveAdmin.register Product do
permit_params :name, product_locations_attributes: {}
actions :all, except: [:show, :destroy]
filter :name
index do
column 'Product Name', :name
actions
end
form do |f|
f.semantic_errors *f.object.errors.keys
f.inputs "Products" do
f.input :name
end
f.has_many :product_locations do |location|
location.inputs "Locations" do
location.input :location, as: :select, multiple: true, collection: ProductLocation.locations.keys
end
end
f.actions
end
controller do
def scoped_collection
Product.where(user_id: nil)
end
end
end
I get a multi-select for the locations which has "Interior" and "Exterior" for selection, but it says the field can't be blank when I select the location and submit the form
The error on save click I get is:
Location can't be blank
The params that get posted are:
Parameters: {"utf8"=>"✓", "product"=>{"name"=>"Test Product", "product_locations_attributes"=>{"0"=>{"location"=>["0", "1"]}}}, "commit"=>"Create Product"}
First, the permit attributes should be,
product_locations_attributes: [:id, :location]
Then, in your form
location.input :location, as: :select, multiple: true, collection: ProductLocation.locations.keys
Since ProductLocation.locations is an array, array.keys is an invalid method.
So, use directly
location.input :location, as: :select, multiple: true, collection: ProductLocation.locations.map { |n| [n,n] }
To store an array of multiple values take serialize field as an array,
class ProductLocation < ApplicationRecord
enum locations: [:exterior, :interior]
serialize :location, Array
validates :location, presence: true
validates :product_id, presence: true
belongs_to :product
end
Note: Inorder to get the serialize work, you need to have the dataType of the location as a text. If it is not text run a migration to change to text data type
Reason for text field: Rails will convert all those object into plain text when storing in database
Related
I am having 2 problems.
1) formtastic will only show the last input field instead of all of them. In this case it will only display:
r.input :sort_order
2) I had to do some wierd wrap f.inputs around each field to get it to show up which i believe is the wrong way. But when i submit the form it says Unpermitted parameters: page. When I did clearly define page I dont know how else to get permit params accept this.
Here is my model
class Fact < ActiveRecord::Base
has_one :page, as: :pageable, dependent: :destroy
accepts_nested_attributes_for :page
end
The other model:
class Page < ActiveRecord::Base
belongs_to :pageable, polymorphic: true
end
My active admin:
ActiveAdmin.register Fact do
permit_params :id, page_attributes: [:type, :name, :description :sort_order ]
form do |f|
f.inputs "My Page", for: [:page, f.object.page || Page.new] do |r|
r.input :name
r.input :description
r.input :sort_order
end
end
end
I can't get ActiveAdmin to save the associated model when the initial model is saved.
I have two models that look like this:
# app/models/account.rb
class Account < ActiveRecord::Base
has_one :endpoint, inverse_of :account, class_name: 'Abcd::Endpoint'
accepts_nested_attributes_for :endpoint
delegate :access_key, to: :endpoint
end
# app/models/abcd/endpoint.rb
class Abcd::Endpoint < ActiveRecord::Base
attr_accessible :account_id, :access_key
belongs_to :account
end
My ActiveAdmin file looks like:
# app/admin/account.rb
Activeadmin.register Account do
form do |f|
f.inputs do
f.input :name
end
f.inputs title: 'endpoints', for: [:endpoint. f.object.endpoint || Endpoint.new] do |nested_form|
nested_form.input :access_key,
label: 'Access Key',
as: :string
end
f.actions
end
show do |account|
row 'endpoint has access_key' do
account.access_key
end
end
end
When I click on "Update Account" the Account gets updated but the Endpoint
model doesn't get updated. It appears that the Endpoint attributes aren't
being sent to the Endpoint model.
Does anyone know how to get the Endpoint model to get updated with its
attributes or what I need to fix?
give it a try
form do |f|
f.inputs "Account" do
f.input :name
end
f.inputs do
f.has_one :endpoint, inverse_of :account, class_name: 'Abcd::Endpoint' do |nested_form|
nested_form.input :access_key,
nested_form.label('Access key')
end
end
f.actions
end
So here is my issue. I have been working with users I created at the beginning of my project for a month now. Today I switched from sqllite to sqlserver to meet client requirements and when I went to use my registration form to create a new user I got the following error:
can't convert Symbol into Integer
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"51nF50CYGNqz3N4o7TUYSyWeTadulXojQBPqERjvlcY=",
"user"=>{
"email"=>"test#blizzardlabs.com",
"login"=>"bgarrison",
"password"=>"[FILTERED]",
"password_confirmation"=>"[FILTERED]",
"profile_attributes"=>{
"prefix"=>"",
"first_name"=>"Bill",
"last_name"=>"Garrison",
"suffix"=>"",
"birthday"=>"1983-06-01",
"phone_numbers_attributes"=>{
"0"=>{
"info"=>"1234567890",
"label"=>"Cell"
}
}
}
},
"commit"=>"Register"}
I have a feeling that at some point I messed up the registration process but I can't for the life of me figure out where. User-> has_one profile-> has_many phone_numbers.
User Controller:
def create
#user = User.new(params[:user])
if #user.save
#profile = #user.profile
flash[:notice] = "Your account has been created."
redirect_to(#user)
else
flash[:notice] = "There was a problem creating you."
render :action => :new, :layout => 'logged_out'
end
end
User Model:
class User < ActiveRecord::Base
# Accessible attributes
attr_accessible :login,
:email,
:password,
:password_confirmation,
:profile_attributes,
:active
# Associations
has_one :profile, dependent: :destroy, autosave: true
# Allows for a profile hash in user creation (stored in :profile_attributes)
accepts_nested_attributes_for :profile
Profile Model:
class Profile < ActiveRecord::Base
# Accessible Attributes
attr_accessible :birthday,
:company_id,
:first_name,
:last_name,
:prefix,
:suffix,
:phone_numbers_attributes,
:addresses_attributes
# Model Associations
has_many :phone_numbers, :as => :contactable, :class_name => "PhoneNumber", autosave: true
accepts_nested_attributes_for :phone_numbers, allow_destroy: true, reject_if: :all_blan
Any help would be appreciated. Thanks!
Update:1 Also, I have tested some and realized if I leave out the phone number then it works.....if I then update using the same form and add a phone number everything works fine.
Nested attributes should be passed in as Array:
"user"=>{
"email"=>"test#blizzardlabs.com",
"login"=>"bgarrison",
"password"=>"[FILTERED]",
"password_confirmation"=>"[FILTERED]",
"profile_attributes"=>[
{
"prefix"=>"",
"first_name"=>"Bill",
"last_name"=>"Garrison",
"suffix"=>"",
"birthday"=>"1983-06-01",
"phone_numbers_attributes"=>{
"0"=>{
"info"=>"1234567890",
"label"=>"Cell"
}
}
}
]
}
So, after a couple days of banging my head against the wall I have finally figured this out. However to understand it I need to explain my model's a bit better.
Basically, from above you can see that a User has a profile which has many phone_numbers and addresses through a polymorphic association (:as => :contactable ). However, contactable is actually a base class called ContactInformation which uses STI to contain contactable information of all types.
At one point I decided that having 4 extra fields for addresses was cluttering up the STI relationship but I still wanted to keep it. My solution was to serialize all those fields into the "info" field of ContactInformation. Right now, phone numbers only have "number" as a field that is serialized and stored into "info" but if I ever want to seperate it out into "area code" "extension" etc the implementation will be simple.
This leads to the problem. On my registration form I was using label / info for my phone_number fields instead of label / number. I had edited my edit form but not my new form (yes i know they should be the same one but I have a special ajax form for editing).
Here is the code for ContactInformation / PhoneNumber / Address
class ContactInformation < ActiveRecord::Base
attr_accessible :contactable_id, :contactable_type, :info, :label, :type
belongs_to :contactable, :polymorphic => true
end
class PhoneNumber < ContactInformation
attr_accessible :number
stash :number, in: :info
#----------------------------------Validations--Start-------------------------
validates :number, presence: true
#----------------------------------Validations--End---------------------------
end
class Address < ContactInformation
attr_accessible :street_address, :city, :state, :postal
stash :street_address, :city, :state, :postal, in: :info
#----------------------------------Validations--Start-------------------------
validates :street_address, :city, :state, :postal, presence: true
#----------------------------------Validations--End---------------------------
end
I've been puzzling over this for quite some time now and can't figure it out.
I've got 2 models:
class Vehicle < ActiveRecord::Base
attr_accessible :year, :capacity,
:size, :body, :model_id, :maker_id, :parameters_attributes
validates :year, numericality: { greater_than: 1900 }
validates :year, :capacity, :size, :body, presence: true
belongs_to :model
belongs_to :maker
has_many :parameters
accepts_nested_attributes_for :parameters
end
and
class Parameter < ActiveRecord::Base
attr_accessible :tag, :value
validates :tag, :value, presence: true
belongs_to :vehicle
end
in new vehicle view i've got:
= form_for [:admin, #vehicle], html: { multipart: true } do |f|
=# some other stuff in between
= f.text_field :value, size: 4
I get this error
undefined method `value'
Just can't seem to get it working. Help, anyone?
EDIT
routes.rb
resources :vehicles
resources :parameters
resources :makers do
resources :models
end
If you are using nested form, you should have something like
f.fields_for :parameters do |parameter|
and than:
parameter.text_field :value, size: 4
Also, remember to create the some parameters in the controller, for example:
def new
#vehicle = Vehicle.new
2.times { #vehicle.parameters.build } #it will create 2 parameters
...
end
f refers to #vehicle, it seems only Parameter bears this field. That's why it fails.
Sidenotes:
In Vehicle you have accepts_nested_attributes_for :parameters but you don't have parameters_attributes in the attr_accessible, can't be good.
If you want to call the relationship in the form consider using fields_for
Ok, I've made a mess of things.
Firstly I've been trying to
def new
#vehicle = #vehicle.parameters.build
end
hence the error undefined method. After a while I got to the correct syntax, which is the one gabrielhilal added after a while.
def new
#vehicle = Vehicle.new
#vehicle.parameters.build
end
No matter ;) Still had problems, because after clicking "create" he wouldn't add records in the database. Turned out that I've set the validates presence: true for tag, but didn't assign any value to it. After fixing that, it worked like a charm. Thanks a lot for all the help.
On to the next puzzle.
I'm trying to call a method when a button is clicked to go and fetch a tweet using the Twitter gem, and store that in my database.
I have a model called Sponsor (which includes a column storing a twitter user name), and a model called Sponsortweet:
models/sponsor.rb:
class Sponsor < ActiveRecord::Base
attr_accessible :facebook, :name, :twitter
has_many :sponsortweets, dependent: :destroy
validates :name, presence: true, uniqueness: { case_sensitive: false }
VALID_TWITTER_REGEX = /\A^([a-zA-Z](_?[a-zA-Z0-9]+)*_?|_([a-zA-Z0-9]+_?)*)$/
validates :twitter, format: { with: VALID_TWITTER_REGEX },
uniqueness: { case_sensitive: false }
def create_tweet
tweet = Twitter.user_timeline(self.twitter).first
self.sponsortweets.create!(content: tweet.text,
tweet_id: tweet.id,
tweet_created_at: tweet.created_at,
profile_image_url: tweet.user.profile_image_url,
from_user: tweet.from_user,)
end
end
models/sponsortweet.rb:
class Sponsortweet < ActiveRecord::Base
attr_accessible :content, :from_user, :profile_image_url, :tweet_created_at, :tweet_id
belongs_to :sponsor
validates :content, presence: true
validates :sponsor_id, presence: true
default_scope order: 'sponsortweets.created_at DESC'
end
In controllers/sponsors_controller.rb:
def tweet
#sponsor = Sponsor.find_by_id(params[:id])
#sponsor.create_tweet
end
Relevant line in my routes.rb:
match 'tweet', to: 'sponsors#tweet', via: :post
In my view (views/sponsors/show.html.haml):
= button_to :tweet, tweet_path
With this code, I get the following error when clicking on the button:
undefined methodcreate_tweet' for nil:NilClass`
If I change to use find (instead of find_by_id), the error is:
Couldn't find Sponsor without an ID
...which makes me think that an ID isn't being passed, since as far as I know, using find raises an error, whereas find_by_id returns nil.
What should I change to cause an ID to be passed?
You need to pass through the id parameter with the path helper:
= button_to :tweet, tweet_path(:id => #sponsor.id)
If you don't want it in the query string:
= form_tag tweet_path do |f|
= hidden_field_tag :id => #sponsor.id
= submit_tag "Tweet"
This does the same thing as your button_to, but adds a hidden field to the form that is generated.