RoR - Searching gem for uploading files - ruby-on-rails

I'm searching a gem (like paperclip or carrierwave) that allows you to upload images. But I want a relationship between the image and the otherside:
I have a table day
And I want a own table for the images so that at the and I'm going to have an has_many belongs_to relationship.
So one day has many images
any suggestions?

If I understand you correctly, you may want to look at the has_many :through relationship for this. We have this working with Paperclip & allows you to have many images to one record:
#app/models/day.rb
Class Day > ActiveRecord::Base
has_many :day_images, :class_name => "DayImage"
has_many :images, :class_name => "Image", :through => :day_images, dependent: :destroy
accepts_nested_attributes_for :day_images, :allow_destroy => true
end
#app/models/day_image.rb
Class DayImage > ActiveRecord::Base
belongs_to :day, :class_name => "Day"
belongs_to :image, :class_name => "Image"
accepts_nested_attributes_for :image, :allow_destroy => true
end
#app/models/image.rb
Class Image > ActiveRecord::Base
has_many :day_images, :class_name => "DayImage"
has_many :days, :class_name => "Day", :through => :day_images, dependent: :destroy
end
The join model would then look like this:
day_images table
id | day_id | image_id | extra attribute | extra attribute | created_at | updated_at
This would allow you to allocate images to the day model using the accepts_nested_attributes_for, like this:
Nested Models
Nested models are pretty tough to get right at first, but get easier the more you do them
Using the models I outlined above, you'll have to add several important factors to make accepts_nested_attributes_for work for you. Here's how:
#app/controllers/days_controller.rb
def new
#day = Day.new
#day.day_images.build.build_image
end
def create
#day = Day.new(day_params)
#day.save
end
private
def day_params
params.require(:day).permit(:day, :variables, day_images_attributes: [:image_id, :extra_attributes, :in, :join, :model, image_attributes: [:image]])
end
This will allow you to create a form like this:
#app/views/days/new.html.erb
<%= form_for #day do |f| %>
<%= f.text_field :day_attribute %>
<%= f.fields_for :day_images do |day_image| %>
<%= day_image.text_field :caption %>
<%= day_image.collection_select(:image_id, Image.where(:user_id => current_user.id), :id, :image_name, include_blank: 'Images') %> --> this will allow you to assign images
<%= day_image.fields_for :image do |i| %>
<%= i.file_field :image %> --> uploads new image
<% end %>
<% end %>
<% end %>
This is all based off live code. If you need any more help, let me know!

Related

Form for has_many :trough association with creating object

Problem
With my rails app I'm trying to accomplish something like image below.
Invoice should has many Items. For example Invoice_1 would have in invoice_items container:
[{item: Egg, quantity: 3, unit_price: 4}, {item: Stick, quantity: 4, unit_price: 2}].
How to made a form to add n Items to Invoice?
Or how to edit this form/controller to create fixed number of InvoiceItems for a start. Later I'll figure how to create Items and create associations dynamically in html.
Source
Models
class Item < ActiveRecord::Base
has_many :invoice_items
has_many :invoices, through: :invoice_items
end
class Invoice < ActiveRecord::Base
has_many :invoice_items, inverse_of: :invoice
has_many :items, through: :invoice_items
accepts_nested_attributes_for :invoice_items
validates :items, :length => { :minimum => 1 }
end
class InvoiceItem < ActiveRecord::Base
belongs_to :invoice
belongs_to :item
validates_presence_of :invoice
validates_presence_of :item
accepts_nested_attributes_for :item
end
Invoice controller
# GET /invoices/new
def new
#invoice = Invoice.new
#invoice.invoice_items.build
end
private:
def invoice_params
#params.fetch(:invoice, {})
params.require(:invoice).permit(
:date,
:seller_id,
:client_id,
invoice_items_attributes: [ item_attributes:
[:name, :quantity, :unit, :unit_price_cents, :unit_price_currency, :price_cents, :price_currency ]
],
invoice_name_attributes: [:number, :month, :year],
)
end
Invoice form
<%= form_for(#invoice) do |invoice_form| %>
<%= invoice_form.fields_for :invoice_items do |invoice_item_form| %>
<%= invoice_item_form.fields_for :item do |item_form| %>
<%= item_form.text_field :name %>
<% end %>
<% end %>
<div class="actions">
<%= invoice_form.submit %>
</div>
<% end %>
Based on what I'm reading, you probably just want to focus on making InvoiceItem records built from an invoice. You can build a fixed number (n) of invoice items by using code such as n.times { invoice.invoice_items.build } in the new method in your controller.
When you want to create dynamic fields there is a gem called Cocoon which allows you to dynamically add associated records to a form.
My advice is just to use fields_for :invoice_items as you have done already in your form and omit the fields_for :items. Instead in the invoice_items form add a select_tag that includes all of the Items you want to add to an invoice.

Nested Form and database update

I'm quite new to rails, so my question may seem noobish.
I have HABTM self association. A product can become a "COMBO" of products, having N products.
class Product < ActiveRecord::Base
has_many :combo_elements, class_name: "ComboElement", foreign_key: :combo_id
has_many :element_combos, class_name: "ComboElement", foreign_key: :element_id
has_many :combos, :through => :element_combos
has_many :elements, :through => :combo_elements
accepts_nested_attributes_for :elements
end
class ComboElement < ActiveRecord::Base
belongs_to :combo, :class_name => 'Product'
belongs_to :element, :class_name => 'Product'
end
With the above code, I can list all "elements" of a combo. Also I can list all "combos" a product is part of (the ComboElement table has a combo_id and an element_id)
Here is my form
<%= f.simple_fields_for :elements, #element do |b| %>
<%= b.input :element_id, :collection => Product.all.collect{ |t| [t.name, t.id ]}, selected: b.object.id %>
<%= b.link_to_remove "Remove this element" %>
<% end %>
I can successfully list all Products a combo has, but whenever I try to update, it simply doesn't work.
My Product controller has the following code
def product_params
params[:product].permit(:name, :price, elements_attributes: [:id, :element_id, :_destroy])
end
I appreciate any help.
Thanks in advance!

Nested forms with multiple table inheritance

How do I build a nested form for objects using multiple table inheritance in rails? I am trying to make a nested form to create an object using a model with a has_many relationship to another set of models that feature multi-table inheritance. I am using formtastic and cocoon for the nested form and the act_as_relation gem to implement the multiple table inheritance.
I have the following models:
class Product < ActiveRecord::Base
acts_as_superclass
belongs_to :store
end
class Book < ActiveRecord::Base
acts_as :product, :as => :producible
end
class Pen < ActiveRecord::Base
acts_as :product, :as => :producible acts_as :product, :as => :producible
end
class Store < ActiveRecord::Base
has_many :products
accepts_nested_attributes_for :products, :allow_destroy => true, :reject_if => :all_blank
end'
For this example, the only unique attribute that book has compared to other products is an author field. In reality, I have a number of unique attributes for book which is why I chose multi-table inheritance over the more commonplace single table inheritance.
I am trying to create a nested form that allows you to create a new store with products. Here's my form:
<%= semantic_form_for #store do |f| %>
<%= f.inputs do %>
<%= f.input :name %>
<h3>Books/h3>
<div id='books'>
<%= f.semantic_fields_for :books do |book| %>
<%= render 'book_fields', :f => book %>
<% end %>
<div class='links'>
<%= link_to_add_association 'add book', f, :books %>
</div>
<% end %>
<%= f.actions :submit %>
<% end %>
And the book_fields partial:
<div class='nested-fields'>
<%= f.inputs do %>
<%= f.input :author %>
<%= link_to_remove_association "remove book", f %>
<% end %>
</div>
I get this error:
undefined method `new_record?' for nil:NilClass
Based on reading the issues on the github page for act_as_relation, I thought about making the relationship between store and books more explicit:
class Product < ActiveRecord::Base
acts_as_superclass
belongs_to :store
has_one :book
accepts_nested_attributes_for :book, :allow_destroy => true, :reject_if => :all_blank
end
class Book < ActiveRecord::Base
belongs_to :store
acts_as :product, :as => :producible
end
class Store < ActiveRecord::Base
has_many :products
has_many :books, :through => :products
accepts_nested_attributes_for :products, :allow_destroy => true, :reject_if => :all_blank
accepts_nested_attributes_for :books, :allow_destroy => true, :reject_if => :all_blank
end
Now, I get a silent error. I can create new stores using the form, and cocoon allows me to add new book fields, but when I submit the store gets created but not the child book. When, I go through the `/books/new' route, I can create a new book record that spans (the products and books table) with no problem.
Is there a workaround to this problem? The rest of the code can be found here.
Maybe you could:
Build the books relation manually on your stores_controller#new action
#store.books.build
Store manually the relation on you stores_controller#create action
#store.books ... (not really confident on how to achieve it)
Keep us posted.
You might want to consider creating your own form object. This is a RailsCast pro video, but here are some of the examples in the ASCIIcast:
def new
#signup_form = SignupForm.new(current_user)
end
This signup form can include relations to your other objects, just as you would in your original controller code:
class SignupForm
# Rails 4: include ActiveModel::Model
extend ActiveModel::Naming
include ActiveModel::Conversion
include ActiveModel::Validations
validates_presence_of :username
validates_uniqueness_of :username
validates_format_of :email, with: /\A([^#\s]+)#((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/
validates_length_of :password, minimum: 6
def persisted?
false
end
def subscribed
subscribed_at
end
def subscribed=(checkbox)
subscribed_at = Time.zone.now if checkbox == "1"
end
def generate_token
begin
self.token = SecureRandom.hex
end while User.exists?(token: token)
end
end
Here is the link to the RailsCast. Getting a pro membership might be worth your time. I have been getting lucky with a membership through www.codeschool.com where you can get 'prizes' when you finish courses:
RailsCast:
http://railscasts.com/episodes/416-form-objects

accept nested attributes for has_many relationship

Below are my two model classes
class Patient < ActiveRecord::Base
belongs_to :user, :dependent => :destroy
has_many :enrollments, :dependent => :destroy
has_many :clients, :through => :enrollments
accepts_nested_attributes_for :user
accepts_nested_attributes_for :enrollments
attr_accessible :user_attributes,:enrollments_attributes, :insurance
end
class Enrollment < ActiveRecord::Base
belongs_to :client
belongs_to :patient
attr_accessible :client_id, :patient_id, :patient_id, :active
end
In my patient form I would like to have a multi select box where a patient can be assigned to clients. Is there a way this can be done so I don't have to have any logic in the
controller except for
#patient = Patient.new(params)
#patient.save
I have tried this:
<%= patient_form.fields_for :enrollments do |enrollments_fields| %>
<tr>
<td class="label">
<%= enrollments_fields.label :client_id %>:
</td>
<td class="input">
<%= enrollments_fields.collection_select(:client_id, #clients, :id, :name, {}, :multiple => true) %>
</td>
</tr>
<% end %>
But it only saves the first client. If I remove the multiple part, it functions but I can only select 1 client!
The html value of the select is:
I ended up doing the following:
<%= check_box_tag "patient[client_ids][]", client.id, #patient.clients.include?(client) %>
I am not sure if this is the best way...any comments (I had to update my model to include attr_accessible :client_ids
In Rails 3 (not sure about previous versions) you don't even need to use accepts_nested_attributes_for to accomplish this. You can simply remove all the view code you listed and replace it with the following:
<%= patient_form.select(:client_ids, #clients.collect {|c| [ c.name, c.id ] }, {}, {:multiple => true})%>
Rails will do its magic (because of you named the select "client_ids") and it will just work.
Instead of
:client_id
in the collection_select, try
"client_id[]"
The second form specifies that you're accepting an array of IDs for the attribute rather than a single one.
Here's a good resource on the usage of the select helpers in forms: http://shiningthrough.co.uk/Select-helper-methods-in-Ruby-on-Rails

Assigning a nested attribute with Formtastic

I've been trying to figure this one out for a while but still no luck. I have a company_relationships table that joins Companies and People, storing an extra field to describe the nature of the relationship called 'corp_credit_id'. I can get the forms working fine to add company_relationships for a Person, but I can't seem to figure out how to set that modifier field when doing so. Any ideas?
More about my project: People have many companies through company_relationships. With that extra field in there I am using it to group all of the specific relationships together. So I can group a person's Doctors, Contractors, etc.
My models:
Company.rb (abridged)
class Company < ActiveRecord::Base
include ApplicationHelper
has_many :company_relationships
has_many :people, :through => :company_relationships
Person.rb (abridged)
class Person < ActiveRecord::Base
include ApplicationHelper
has_many :company_relationships
has_many :companies, :through => :company_relationships
accepts_nested_attributes_for :company_relationships
company_relationship.rb
class CompanyRelationship < ActiveRecord::Base
attr_accessible :company_id, :person_id, :corp_credits_id
belongs_to :company
belongs_to :person
belongs_to :corp_credits
end
My form partial, using formtastic.
<% semantic_form_for #person do |f| %>
<%= f.error_messages %>
<% f.inputs do %>
...
<%= f.input :companies, :as => :check_boxes, :label => "Favorite Coffee Shops", :label_method => :name, :collection => Company.find(:all, :conditions => {:coffee_shop => 't'}, :order => "name ASC"), :required => false %>
So what I would like to do is something like :corp_credit_id => '1' in that input to assign that attribute for Coffee Shop. But formtastic doesn't appear to allow this assignment to happen.
Any ideas on how to do this?
Are you looking for something like
<% semantic_form_for #person do |form| %>
<% form.semantic_fields_for :company_relationships do |cr_f| %>
<%= cr_f.input :corp_credit_id %>
<% end %>
It is in the documentation

Resources