I have a model A that "has many" B.
class A < ActiveRecord::Base
has_many :B
attr_accessible :title
end
class B < ActiveRecord::Base
belongs_to :A
attr_accessible :name
end
I want to add a field to my "edit A" form : a textarea in which I will enter my B's :name for each line, and on submit, parse the field, and process each line.
The question is, how should I do that ?
Edit
Following Rails - Add attributes not in model and update model attribute I have come to this :
class A < ActiveRecord::Base
has_many :B
attr_accessible :title
def my_b
list = ""
self.B.each do |b|
list += "#{b.name}\n"
end
logger.debug("Displayed Bs : " + list)
list
end
def my_b=(value)
logger.debug("Saved Bs : " + value)
# do my things with the value
end
end
But def bees=(value) never seems to be fired.
What am I doing wrong ?
Edit 2
My actual code is visible here : https://github.com/cosmo0/TeachMTG/blob/edit-deck/app/models/deck.rb
You can put an :attr_accessor, like:
class A < ActiveRecord::Base
has_many :B
attr_accessible :title
attr_accessor :field_of_happiness
def field_of_happiness=(value)
# override the setter method if you want
end
def field_of_happiness(value)
# override the getter method if you want
end
end
Reference: attr_accessor api doc
It helps you on some way?
Oh my. Turns out the problem was not lying in the model, but in the controller... I forgot to add a simple line in the update method to assign my POST value to my class field...
Anyway, final solution is this :
In my controller :
def update
#a.whatever = params[:a][:whatever]
#a.my_b = params[:a][:my_b]
#a.save
end
In my model :
class A < ActiveRecord::Base
has_many :B
attr_accessible :whatever
def my_b
list = ""
self.B.each do |b|
list += "#{b.name}\n"
end
list
end
def my_b=(value)
# parse the value and save the elements
end
end
Related
Looking for a cleaner way to set a default value if attribute is not set yet or has been deleted, and returns nil.
class Category < ActiveRecord::Base
has_and_belongs_to_many :restaurants
belongs_to :picture
def set_picture
if self.picture.nil?
Picture.default_pic
else
self.picture
end
end
end
class Picture < ActiveRecord::Base
belongs_to :review
def self.default_pic
Picture.new(url: "/assets/default.jpg")
end
end
# index.html.erb
<%= image_tag category.set_picture.url %>
categories has many restaurants, and restaurants has many reviews. Reviews has one to one picture. category should be allowed to select from one of its associated pictures, or defaults to image in assets folder.
The #set_picture needs to get refactored out. Hopefully to a callback of some type:
class Category < ActiveRecord::Base
belongs_to :picture, defaults_to: Picture.default_pic
end
Is there a callback that does the above? Can I create one? Or is my framework wrong?
I think you could just override the accessor and call super. If this returns nil then you could return your default picture:
class Category < ActiveRecord::Base
belongs_to :picture
def picture
super || Picture.default_pic
end
end
I have some problems getting a nested form working. In the below example I have a User that can define multiple custom labels for a post. The user should be able to enter a value for each particular label.
So one Post can have multiple labels, but should only have one value for each label! In example: Post can have a label named "Date" and also have a label named "Mood". For both labels there should be just one value.
The problem is when a User creates a Label -let say "Date"- it should only be possible to enter one value for this post for this particular label. So if a value for date is given, the form shouldn't build another field for date again. (the post already has a date)
User creates custom labels (this works)
On the edit page of the Post, User sees the labels he created in step 1 (this works)
User can enter a value for each of the Label (here is the problem)
I have the following models:
class User < ActiveRecord::Base
has_many :posts
has_many :labels
end
class Post < ActiveRecord::Base
has_many :label_values
belongs_to :user
accepts_nested_attributes_for :label_values, allow_destroy: true
end
class Label < ActiveRecord::Base
has_many :label_values
belongs_to :user
end
class LabelValue < ActiveRecord::Base
belongs_to :post
belongs_to :label
end
In my controller I have
class PostsController < ApplicationController
def edit
#labels = current_user.labels.all
#post.label_values.build
end
end
My form:
= simple_form_for #post do |f|
=f.fields_for :label_values do |s|
=s.association :label, :include_blank => false
=s.input :value
= f.button :submit
Like this, every time a User enters a value for a particular label, the next time a new label_value is build again and that is not what I want. For each label the User should be able to enter one value.
If you intend to use Label to attach metadata to Post you may want to consider using a relationship somewhat like the following:
class User < ActiveRecord::Base
has_many :user_labels # Labels which have been used by this user.
has_many :labels, through: :user_labels
has_many :posts
end
class Post < ActiveRecord::Base
has_many :post_labels
belongs_to :user, as: :author
has_many :labels, through: :post_labels
accepts_nested_attributes_for :post_labels
def available_labels
Label.where.not(id: labels.pluck(:label_id))
end
end
class Label < ActiveRecord::Base
# #attribute name [String] The name of the label
has_many :user_labels
has_many :post_labels
has_many :users, through: :user_labels
has_many :posts, through: :post_labels
validates_uniqueness_of :name
end
# Used to store label values that are attached to a post
class PostLabel < ActiveRecord::Base
belongs_to :post
belongs_to :label
validates_uniqueness_of :label, scope: :post # only one label per post
# #attribute value [String] the value of the label attached to a post
end
# Used for something like displaying the most used labels by a user
class UserLabel < ActiveRecord::Base
belongs_to :user
belongs_to :label
end
In this setup labels act somewhat like "tags" on Stackoverflow. Each separate label is unique in the system but may be attached to many posts - by many users.
Note the validates_uniqueness_of :label, scope: :post validation which ensures this.
The actuals values attached to a tag are stored on the value attribute of PostLabel. One major drawback to this approach is that you are really limited on by type of the PostLabel value column and may have to do typecasting. I really would not use it for dates as in your example as it will be difficult to do a query based on date.
def PostController
def new
#available_labels = Label.all()
end
def edit
#post = Post.joins(:labels).find(params[:id])
#available_labels = #post.available_labels
end
def create
#post = Post.new(create_params)
if (#post.save)
#post.labels.each do |l|
#post.user.labels << l
end
# ...
else
# ...
end
end
private
def create_params
params.permit(:post).allow(:post_labels)
end
end
Added
This is simply some opinionated recommendations:
To add new labels to a post I would add a text input below the already assigned labels.
I would autocomplete using GET /labels. and filter against the inputs already present in the form. You can allow users to create new labels on the fly by doing an ajax request to POST /labels.
I finally came to something. In the Post model I created:
def empty_labels
Label.where.not(:id => labelValue.select(:label_id).uniq)
end
def used_simfields
LabelValue.where(post_id: id)
end
Then in the view I did:
= simple_form_for #post do |f|
-#post.used_labels.each do |used_label|
=f.fields_for :label_values, used_label do |old_label|
=old_label :value
-#post.empty_labels.each do |empty_label|
=empty_label.fields_for :label_values, #post.label_values.new do |new_label|
=new_label.association :label
=new_label.input :value
I am very sure there are nicer ways to achieve this, so new ideas are very welcome.
Consider a relationship like this:
class BuyableComponent < ActiveRecord::Base
attr_accessible :cost
end
class CartItem < ActiveRecord::Base
attr_accessible :quantity
belongs_to :buyable_component
def total_cost
# This should be buyable_component.cost, but how do I make an alias so
# I can just use 'cost'?
cost * quantity
end
end
I have a buyable_components table and a cart_items table. Like the comment describes, I would like to be able to use cart_item.cost instead of cart_item.buyable_component.cost. alias_attribute seems to be close to what I need, but not quite.
I'm looking for a way to declare this for all attributes of BuyableComponent.
try something like:
class CartItem < ActiveRecord::Base
delegate :cost, :to => :buyable_component
end
this should work I suppose
I have a model called Course which needs to be associated with exams and assignments. I want to able to write code like this:
>>c = Course.new
>>assignment1 = c.assignments << Assignment.new
>>exam1 = c.exams << Exam.new
c.assessments should now include both exam1 and assignment1
How I think this should be accomplished (using single table inheritance from the Assessment model):
class Course < ActiveRecord::Base
has_many :assessments
attr_accessible :title, :name, :startDate, :endDate, :color
end
class Assessment < ActiveRecord::Base
belongs_to :course
attr_accessible :end_at, :name, :start_at, :type, :weight
end
class Assignment < Assessment
end
class Exam < Assessment
end
I've tried my best to find out how to do this but i cant seem to figure it out. Any help would be appreciated.
Course has only assesments associations so you should be able to write code like this:
c = Course.new
c.assesments << Assignment.new
c.assesments << Exam.new
Also make sure that assesments table has type column with datatype string.
Hi I have two model Company and Feed
company.rb
class Company < ActiveRecord::Base
attr_accessible :rss_url, :name
has_many :feeds
end
feed.rb
class Feed < ActiveRecord::Base
attr_accessible :guid, :name, :published_at, :summary, :url
after_create { |feed| FeedEntry.update_from_feed(feed.feed_url) }
belongs_to :company
def self.update_from_feed(rss_url) ?????
feed = Feedzirra::Feed.fetch_and_parse(rss_url) ?????
add_entries(feed.entries)
end
end
How two take RSS_URL from company to feed ?
I don't know what add_entries does, but if you want to access the rss_url of Company inside Feed, you can use the association. Define this instance method on your Feed class.
def do_something
url = self.company.rss_url
puts "This feed belongs to a company with the following rss url: #{url}"
end