Save has_and_belongs_to_many link in basic RoR app - ruby-on-rails

I try to learn the has_and_belongs_to_many relationship between my 2 fresh new and simple models Product and Author, where a Product can have many authors and where author can have a lots of products.
I wrote this :
class Author < ActiveRecord::Base
has_and_belongs_to_many :products
end
class Product < ActiveRecord::Base
has_and_belongs_to_many :authors
end
In the partial form of view for the products, I have :
<p>Products</p>
<%= collection_select(:product, :author_ids, #authors, :id, :name, :prompt => " ", :multiple => true) %>
but when I hit the update button, I get this strange message I can't resolve myself :
NoMethodError in ProductsController#update
undefined method `reject' for "1":String
Rails.root: /home/stephane/www/HABTM
Application Trace | Framework Trace | Full Trace
app/controllers/products_controller.rb:63:in block in update'
app/controllers/products_controller.rb:62:inupdate'
Request
Parameters:
{"utf8"=>"✓",
"_method"=>"put",
"authenticity_token"=>"2GlTssOFjTVZ9BikrIFgx22cdTOIJuAB70liYhhLf+4=",
"product"=>{"title"=>"Le trésor des Templiers",
"original_title"=>"",
"number"=>"1",
"added_by"=>"",
"author_ids"=>"1"},
"commit"=>"Update Product",
"id"=>"1"}
What's wrong ? Is there a problem with :product_ids... I saw on internet I had to pu a "s" but I'm not sure of what it represents....
How can I link the table authors_products to the key which is given back by the drop-down menu ? (here "author_ids"=>"1")
Thx !
More info :
May be solved with this info, but still no saving of the relationship :
collection_select("sales_agent", "customer_id", #customers, "id", "name")
Assuming you had a customer model with an ID attribute and a name attribute, this would produce exactly the above code. So looking at the values we pass into the collection_select call:
The first parameter is the model that contains one element from the collection (eg. sales_agent)
Next is the field name in the model that refers to the collection element it contains (eg. customer_id)
Next is the variable containing the collection of items that we want to list (eg. #customers)
Next is the value attribute of the option tag (eg. the customer id)
Next is the display attribute of the option tag (eg. the customer name)
So I now wrote
<p>Products</p>
<%= collection_select(:author, :author_id, #authors, :id, :name, :prompt => " ", :multiple => true) %>
and it worked, but without saving the link, for the moment... (only the update of the normal fields are saved, not the relationship :-(

Do you have a separate model called author_products for the HABTM relationship?
You'll need to run another migration by doing something like rails g model author_product and the table should only contain two fields:
belongs_to :author
belongs_to :product
Make sure there is no primary key.
Something like:
def self.up
create_table(:author_products), :id => false do |t|
t.references :author
t.references :product
end
end

Related

Rails - Can't create a record that has an attribute with the same name of the class

I'm having troubles trying to create (or even update) a database record, because both the parent class name and the name of an attribute are equal. I'm using a consolidated database, so I can't alter the tables.
I have a class Period with an attribute 'period'. One period has many tax scales.
#period model
has_many :tax_scales,
foreign_key: 'period',
primary_key: 'period'
#tax_scale model
belongs_to :period,
foreign_key: :period,
primary_key: :period
So, when I use this in my tax_scale view:
#new.html.haml
= f.label :period
= f.number_field :period
I get this error:
Period(#97477176) expected, got String(#8598408)
My controller params are:
params.require(:tax_scale).permit(:minimum, :maximum, ..., :period)
How can I edit (or create) a tax_scale record succesfully?
I found the solution: use methods using self[:attribute], to be sure the attribute is accessed
#tax_scale model
def current_period
self[:period]
end
def current_period=(p)
self[:period] = p
end
#new.html.haml
= f.label :current_period
= f.number_field :current_period
Notice I have to use self[:period] and not self.period, because the second one is still accessing the parent record, not the attribute.

How to display associated model's attribute in Active Admin index with belongs_to/has_many relationship (Rails 3.2/Active Admin)

I'm building a daily deal Rails app to learn RoR.
I am facing a problem for the past few hours : i can't get a model's attribute of an other associated model on active admin. Let me show you exactly the problem :
I have two models: Brand (i.e the brand of the deal) and Deal. A deal belongs to a Brand but a Brand can have many Deals.
models/deal.rb is like this:
class Deal < ActiveRecord::Base
belongs_to :brand
and we have models/brand.rb:
class Brand < ActiveRecord::Base
has_many :deals
attr_accessible :name
And i did the t.belongs_to in my migrations so this is ok.
In Active Admin's Deals' create form , i type, as admin, which brand the deal is associated with:
admin/game.rb
ActiveAdmin.register Deal do
# -- Form -----------------------------------------------------------
form do |f|
f.inputs "Brand (i.e. client)" do
f.input :brand_id, :label => "Select a brand:", :as => :select, :collection => Brand.all
end
it works great, and i can create Deals with a certain brand.
but I CAN'T manage to display the NAME of the Brand in my list of Deals:
ActiveAdmin.register Deal do
index do
selectable_column
# id_column
column :title
column :deal_amount
column :brand do |deal|
link_to deal.brand.name
end
...doesn't work.
How can I do that ?
I tried everything but i basically don't know how to fetch the name of a Brand given it matches the brand_id in the Deal's table.
Any help appreciated.
show do |f|
panel "Subject" do
attributes_table_for f, :name, :description, :is_visible
end
panel "Pages in List View" do
table_for(f.pages) do |page|
column :name
column :permalink
column :is_visible
end
end
panel "Pages in View " do
div_for(f.pages) do |page|
panel page.name do
attributes_table_for page, :name, :description, :is_visible
end
end
end
end
end
You can do nested relations in same style as parent model
A couple things seem missing:
class Deal < ActiveRecord::Base
belongs_to :brands, foreign_key: :brand_id, class_name: 'Brand'
end
This is assuming that you mean partner to be a Brand and your schema uses brand_id for that relationship.
In your form, you can simply use:
form do |f|
f.inputs "Brand (i.e. client)" do
f.input :partner, label: 'Select a brand:'
end
end
Your link_to call won't actually link to a url the way you have it.
column :brand do |deal|
link_to deal.partner.name, admin_brand_path(deal.partner)
# or simpler
auto_link deal.partner
end
I would highly recommend trying to be consistent in your naming, as it will make things a lot less confusing and will require less code to make things work. i.e.
class Deal < ActiveRecord::Base
belongs_to :brand
end
f.input :brand, label: 'Select a brand:'
auto_link deal.brand
And your DB column can still be named brand_id.

Can't display in Active Admin index attribute of associated model (belongs_to/has_many) - Rails 3.2

I'm building a daily deal Rails app to learn RoR.
I am facing a problem for the past few hours : i can't get a model's attribute of an other associated model on active admin. Let me show you exactly the problem :
I have two models: Brand (i.e the brand of the deal) and Deal. A deal belongs to a Brand but a Brand can have many Deals.
models/deal.rb is like this:
class Deal < ActiveRecord::Base
belongs_to :brand
and we have models/brand.rb:
class Brand < ActiveRecord::Base
has_many :deals
attr_accessible :name
And i did the t.belongs_to in my migrations so this is ok.
In Active Admin's Deals' create form , i type, as admin, which brand the deal is associated with:
admin/deal.rb
ActiveAdmin.register Deal do
# -- Form -----------------------------------------------------------
form do |f|
f.inputs "Brand (i.e. client)" do
f.input :brand_id, :label => "Select a brand:", :as => :select, :collection => Brand.all
end
it works great, and i can create Deals with a certain brand. but I CAN'T manage to display the NAME of the Brand in my list of Deals iun Active Admin'x index :
ActiveAdmin.register Deal do
index do
selectable_column
# id_column
column :title
column :deal_amount
column :brand do |deal|
link_to deal.brand.name, admin_brand_path(deal.brand)
end
...doesn't work.
How can I do that ?
I tried everything but i basically don't know how to fetch the name of a Brand given it matches the brand_id in the Deal's table.
Any help appreciated.
UPDATE
the error i'm getting is it doesn't understand function .name: unknown method 'name'
You need to handle the case of deal.brand being nil.
ActiveAdmin.register Deal do
index do
selectable_column
# id_column
column :title
column :deal_amount
column :brand do |deal|
if deal.brand.present?
link_to deal.brand.name, admin_brand_path(deal.brand)
else
status_tag('Empty')
end
end
end
end
Your :brand column looks correct to me. If you find a deal in the console what do you get if do deal.brand and deal.brand.name?

How to include an array into params for model backed form

I have a complex model backed form and want to include multiple objects into my params.
My setup:
Network.rb
class Network < ActiveRecord::Base # t.string "name"
has_many :trackings
has_many :installations, through: :trackings
end
Installation.rb
class Installation < ActiveRecord::Base # t.string "name"
has_many :trackings
has_many :networks, through: :trackings
end
Tracking.rb
class Tracking < ActiveRecord::Base # t.string "code
belongs_to :network # t.integer "network_id",
belongs_to :installation # t.integer "installation_id
end
When I create a new Tracking-Object, I want to render each Network in the database and add a textfield for :code
My form:
= form_for [:admin, #tracking] do |f|
= f.select :installation_id, options_for_select(Installation.all.map { |i| [i.name, i.id] }), prompt: "Select an Installation"
- Network.all.each do |network|
= f.text_field :code
= f.submit
Currently all text fields get rendered but when I submit, only the last input of the last text field gets into my params. How can I include ALL of them?
For non model-backed form I found this, but it's not working for my form:
= text_field_tag "tracking[][code]"
The problem is that all form fields in each iteration of network have the same name so they're all mapped to the same position on your input hash... which is why you only see the last entry.
If you do...
=text_field_tag "admin[][code[][#{network_id}]]"
... you'll get code returned as a hash with network_id as the key, and user input as the value. So you'll get separate values that you can manipulate.
The problem is that when a view gets rendered, it all gets translated into a format suitable for an HTML PUT, which does not support arrays. You'll need to assign a different name to each code field, perhaps appending the array index to the end of each one after a delimiter (:code-2 for example). Each value will then have it's own parameter. When the form gets posted, you can split the names using the delimiter to find those that start with post, and get the array index back so you can assign it to the array again.

How to create Categories in Rails

i'm trying to Add Categories to my Rails app, but don't quite know how to do this.
I have many Pins(Images) and want the user to be able to assign a category on those Pins.
ASSIGN not create, edit, or delete a Category, just selecting one for their Pin.
Meaning that, When a user uploads a pin, he can choose from a dropdown list a Category.
Then, another user can choose from the Menu a Category, and ONLY the Pins in this Category will be listed.
How do i do this? Where to start ?
Thank you
First If you don't want to manage categories in your application, then you can simply add a category field in your table and category select in your application :
<%= f.select :category, [ 'Box', 'Cover', 'Poster' ], :prompt => 'Select One' %>
Second, If you want to manage categories in your application, than you have to maintain a separate model and table for it. So you can start with generating your model:
rails g model category
it will add model and migration in your application directory. Add stuff to your migration:
class CreateCategories < ActiveRecord::Migration
def change
create_table :categories do |t|
t.string :name
t.text :description
## you can add more stuff as per your requirements
t.timestamps
end
end
end
Define associations in category & Pin model add validation for this :-
In Category Model:
has_many :pins
In Pin Model :
belongs_to :category
validates :category, presence: true
Create some categories by categories controller and form (I don't think, I need to tell you that stuff, you are able to do it yourself)
In your pin uploading form add this select :-
<%= f.select :category, Category.all, :prompt => "Select One" %>
Hope, It will help.
you might wanna add a def to_s method on your Category model. I believe it will display some weird memory address code just by using plain Category.all on the f.select option. Everything else looks great!

Resources