I am trying to show the value of an attribute via its id with formtastic. I have two models hat are setup like this
class Membership < ActiveRecord::Base
has_many :members
attr_accessible :membership_type
end
class Member < ActiveRecord::Base
belongs_to :membership
accepts_nested_attributes_for :membership
attr_accessible :membership_id, :forename, :middlename, :surname, :house_no, :house_name, :street, :town, :postcode, :home_tel, :mobile_tel, :work_tel, :email
end
My index view is set up like this so far
index do
column :forename
column :middlename
column :surname
column :house_no
column :house_name
column :street
column :town
column :postcode
column :home_tel
column :mobile_tel
column :work_tel
column :email
column :membership
default_actions
end
but the value for the membership is output as
#<Membership:0x007f2c2064c370>
How would i get the actual value so it will say "Student" for example
Normally i would do something like this i guess
<% #members.each do |m| %>
<%= m.forname%>
<%= m.surname%>
<% m.memberships.each do |s| %>
<%= s.membership_type%>
<% end %>
but cant figure it out within formtastic
Thanks
ActiveAdmin tries its best to find a display name for your objects using a list of possible attributes:
:display_name
:full_name
:name
:username
:login
:title
:email
To ensure your Membership object has a recognizable name throughout the system you can add one of those attributes as a method to Membership:
class Membership < ActiveRecord::Base
has_many :members
attr_accessible :membership_type
def display_name
membership_type
end
end
Alternatively, you can do like MichaĆ Albrycht suggests and configure views to perform custom rendering for the membership column.
Try this:
column 'Membership type' do |member|
"#{member.membership.membership_type}".html_safe
end
column 'Other members' do |member|
member.membership.members.each() do |m|
"#{m.forname} #{m.surname}<br/>".html_safe
end
end
Thanks #Cmaresh. Problem is solved. This is the commit link where i solved the problem in my project. Any one can see the source code and can easily understand how to solve the problem.
Related
I got a variable in a nested model (account) belonging the user model in which there is a column for :skills. in my edit_user_path, i would like to be able to save multiple skills into that column via checkboxes. For doing that I permitted the skills in the controller as array, even though it is saved as string into the database.
My controller:
def user_params
params.require(:user).permit(:email, :password, :password_confirmation, :account,
account_attributes:[:id, :username, {:skills => []}, :description, :location, :avatar, :tags, :tag_list])
end
If i save multiple values via checkboxes into this variable inside of a nested form, i do it like this:
<% skills = ["Coding", "Design", "Petting Cats"] %>
<% skills.each do |skill| %>
<div class="form-check form-check-inline">
<div class="custom-control custom-checkbox">
<%= form.check_box :skills, { multiple: true, class:"custom-control-input",id: skill }, skill, false %>
<%= form.label skill, class:"custom-control-label", for: skill %>
</div>
</div>
<% end %>
This works and the values are getting saved as an array, but oddly the array once saved into the database looks like this:
"[\"Artist\", \"Mixing\", \"Mastering\"]"
instead of this:
["Artist", "Mixing", "Mastering"]
Which leads to troubles, since i would like to iterate through all users later "filtering" for certain skills, like User.account.where(skills: "Petting Cats") if a user has Petting Cats saved somewhere inside of the array.
For Development i am using SQLite, for production PostgresQL.
How do i save multiple strings into a string variable as clean array without mess, and how to iterate through the array later with a where query method?
You're falling into the common double whammy noob trap of using an array column and serialize.
The reason you are getting "[\"Artist\", \"Mixing\", \"Mastering\"]" stored in is that you are using serialize with a native array column. serialize is an old hack to store stuff in varchar/text columns and the driver actually natively converts it to an array. When you use serialize with a native array/json/hstore column your actually casting the value into a string before the driver casts it. The result is:
["[\"Artist\", \"Mixing\", \"Mastering\"]"]
Removing serialize will fix the immediate problem but you're still left with a substandard solution compared to actually doing the job right:
# rails g model skill name:string:uniq
class Skill < ApplicationRecord
validates :name, uniqueness: true, presence: true
has_many :user_skills, dependent: :destroy
has_many :users, through: :user_skills
end
# rails g model user_skill user:belongs_to skill:belongs_to
# use `add_index :user_skills, [:user_id, :skill_id], unique: true` to ensure uniqueness
class UserSkill < ApplicationRecord
validates_uniqueness_of :user_id, scope: :skill_id
belongs_to :user
belongs_to :skill
end
class User < ApplicationRecord
has_many :user_skills, dependent: :destroy
has_many :skills, through: :user_skills
end
<%= form_with(model: #user) do |form| %>
<div class="field">
<%= form.label :skill_ids %>
<%= form.collection_select :skill_ids, Skill.all, :id, :name %>
</div>
<% end %>
def user_params
params.require(:user).permit(
:email, :password, :password_confirmation, :account,
account_attributes: [
:id, :username, :description, :location, :avatar, :tags, :tag_list,
],
skill_ids: []
)
end
This gives you:
Queryable data
Referential integrity
Normalization
Encapsulation
ActiveRecord Assocations!
The pleasure of not looking like an idiot in a review/audit
I'm trying to make e-commerce shop with RoR. Most of the required functionality I did without any problems, but now I really need somebody's help here.
I want to make product attributes, like a "Size", "Weight", "Color", etc.
Easiest way is to define this attributes in model migration, but now I want to make attributes dynamic. The main problem is that I can't get all params with attributes from forms when trying to create product.
products/new.html.erb
<%= form_for #product, url: admin_products_path(#product) do |f| %>
<%= f.label :name, 'Name' %>
<%= f.text_field :name, class: "form-control" %>
<%= text_field_tag "product[product_feature][]" %>
<%= text_field_tag "product[product_feature][]" %>
<%= f.submit "Submit" %>
<% end %>
So, I want to generate many fields with attribute name and value, fill them and use these params in controller to interate them and finally create product attributes.
Like
params[:product_features].each do |k, v|
ProductFeature.create(name: k, value: v, product_id: product_id)
end
All gems, that can manipulate with dynamic attributes aren't working with Rails 5+, so I need to find solution for this problem.
I even have working simple db solution for this, but it's uncomfortable to create params. Here it is.
Product.rb
class Product < ApplicationRecord
has_many :product_features
has_many :features, :through => :product_features
end
ProductFeature.rb
class ProductFeature < ApplicationRecord
belongs_to :product
belongs_to :feature
end
Feature.rb
class Feature < ApplicationRecord
end
Make a new model, a child of product called ProductAttribute with two attributes.
Class ProductAttribute < ApplicationRecord
belongs_to :product
validates :name, presence: true
validates :value, presence: true
end
Then use cocoon, or just accepts_nested_attributes
class Product < ApplicationRecord
has_many :product_attributes, as: :attributes
accepts_nested_attributes_for :attributes, allow_destroy: true
end
class ProductsController < ApplicationController
.
.
.
private
.
.
def product_params
params.require(:product).permit(. . . attributes_attributes: [:id, :name, :value])
end
end
Cocoon is definitely what you're looking for.
Heres a quick example i found
class Product
belongs_to :collection
end
class Collection
has_many :products
end
and then in your view something like this
<%= collection_select(:product, :collection_id, Collection.all, :id, :name) %>
I'm creating an admin interface where the admin (of a company) can add custom fields to their employees.
Example:
Models:
Employee: Basic info like name, contact info, etc (has_many employee_field_values)
EmployeeFields: These are the dynamic ones the admin can add (every company has different needs, it could be anything), lets say favorite_food
EmployeeFieldValues: The actual values based on the fields above, say pizza (belongs_to both models above)
What's a smart way of adding the EmployeeFieldValues fields while editing an employee?
I'm trying something simple like this, but not sure if I like it
# Controller
#custom_fields = EmployeeFields.all
# View
<%= form_for(#employee) do |f| %>
<%= f.text_field :first_name %>
<% #custom_fields.each do |custom_field| %>
<%= custom_field.name %>
<%= text_field_tag "employee_field_values[#{custom_field.name}]" %>
<% end %>
<%= f.submit :save %>
<% end %>
And then when updating, params[:employee_field_values] gives this:
<ActionController::Parameters {"favorite_food"=>"pizza"}>
So, not sure if this is a good direction, also I'm not sure how to handle future edits to an employee's custom_fields if they change.
I think it will be better to use EmployeeField as nested model and EmployeeFieldValue for select field.
For example:
Models
class Employee < ActiveRecord::Base
validates :name, presence: true
has_many :employee_field_values
accepts_nested_attributes_for :employee_field_values, reject_if: ->(x) { x[:value].blank? }
end
class EmployeeFieldValue < ActiveRecord::Base
belongs_to :employee
belongs_to :employee_field
end
class EmployeeField < ActiveRecord::Base
has_many :employee_field_values, inverse_of: :employee_field, dependent: :destroy
validates :title, presence: true, uniqueness: true
end
Controller
class EmployeesController < ApplicationController
def new
#employee = Employee.new
#employee.employee_field_values.build
end
end
View
= simple_form_for #employee, url: '/' do |f|
= f.input :name
= f.simple_fields_for :employee_field_values do |ff|
= ff.input :value
= ff.input :employee_field_id, collection: EmployeeField.all.map{|x| [x.title, x.id]}
Also you need to make buttons for adding/removing :employee_field_value, and you can do it with gem cocoon for example
OR you can build all objects in controller(for each EmployeeField) and do without select box
I am having trouble rendering a model's attributes in foreign model's view. I think it is a nested attributes problem because f.bike works but f.bike.biketype gives an undefined method error. Below is my error and code. Any help would be great. Thanks!
Error from Browser:
NoMethodError in Carts#show
Showing /Users/willdennis/rails_projects/spinlister/app/views/carts/show.html.erb where line #4 raised:
undefined method `biketype' for nil:NilClass
Extracted source (around line #4):
1: <h2>Your Cart</h2>
2: <ul>
3: <% #cart.line_items.each do |f| %>
4: <li><%= f.bike.biketype %></li>
5: <% end %>
6: </ul>
views/carts/show.html.erb
<h2>Your Cart</h2>
<ul>
<% #cart.line_items.each do |f| %>
<li><%= f.bike.biketype %></li>
<% end %>
</ul>
cart.rb
class Cart < ActiveRecord::Base
has_many :line_items, :dependent => :destroy
belongs_to :user
accepts_nested_attributes_for :line_items
attr_accessible :bike_id, :name, :description, :size, :biketype, :price, :photo, :id, :address, :city, :state, :zip, :latitude, :longitude, :neighborhood
end
line_item.rb
class LineItem < ActiveRecord::Base
belongs_to :bike
belongs_to :cart
accepts_nested_attributes_for :bike, :cart
attr_accessible :name, :description, :size, :biketype, :price, :photo, :id, :address, :city, :state, :zip, :latitude, :longitude, :neighborhood
end
bike.rb
class Bike < ActiveRecord::Base
has_many :line_items
attr_accessible :name, :description, :size, :biketype, :price, :photo, :id, :address, :city, :state, :zip, :latitude, :longitude, :neighborhood
end
carts_controller.rb
def show
#cart = Cart.find(params[:id])
end
Your error is telling you that the object f.bike is of class nil. You would have been expecting it to be an instance of class Bike. Obviously you have a line item in your view that doesn't have a bike record attached to it.
To take care of the error so your view will display just add a check for blank?
li><%= f.bike.biketype unless f.bike.blank? || f.bike.biketype.blank? %></li>
As to why you have a line item with no bike? Well that's an entirely different question and there is not enough info here to answer that.
UPDATE Based on comments below
Your button_to looks a little wrong. Try
<%= button_to "Rent this Bicycle!", {line_items_path(:bike_id => #bike)}, {:id => "rentthisbike"} %>
Using the curly braces ensures that Rails knows the second param is a css style not a param to be passed into the controllers action
To check if your bike id is getting into the controller then check the params that are actually getting into the controller action that add the bike to the line item. You will be able to find this in your development.log file, find the post request for the action and one of the first lines in that action will list all the params. If you see the bike_id in that list then either one of 2 possible situations is occurring.
1) You are not accessing the params hash properly to get the bike id from it
or
2) You are not setting the bike ID on the line item before saving the record.
You should be able to access the id using params[:bike_id]
If you don't see the bike id then you need to look again at the button_to code to see why it's not passing the bike ID
End of update
Hope that helps
following up with this topic:
(Rails) How to display child records (one-to-many) in their parent's form?
I am creating a category that may have many products. So, I use the Formtastic as user Chandra Patni suggested. But I find there is a problem when I added attr_accessible to the product.rb.
class Product < ActiveRecord::Base
validates_presence_of :title, :price
validates_numericality_of :price
validates_uniqueness_of :title
attr_accessible :category_id, :name
belongs_to :category
end
and this is the category.rb:
class Category < ActiveRecord::Base
attr_accessible :name
has_many :products
end
After I added the attr_accessible :category_id, :name , the validation gets crazy, no matter I type, it treats me as null in the text value. But after I remove the attr_accessible :category_id, :name all stuff works.
One more thing, in the products/new.html.erb , I created this to input the product information,
<% semantic_form_for #product do |f| %>
<% f.inputs do %>
<%= f.input :title %>
<%= f.input :price %>
<%= f.input :photoPath %>
<%= f.input :category %>
<% end %>
<%= f.buttons %>
<% end %>
But I find that it return the :category id instead of the category name. What should I do?
Thx in advanced.
You will need to add other attributes to attr_accessible for rails to perform mass assignment.
attr_accessible :category_id, :name, :title, :price, :photoPath
Rails will only do mass assignment for attributes specified in attr_accessible (white list) if you have one. It is necessary from security point of view. Rails also provides a way to blacklist mass assignments via attr_protected method.
You should see a drop down for category if you have name attribute in your Category model. See this and this.
class Category < ActiveRecord::Base
has_many :products
attr_accessible :name
end