Using rails3-jquery-autocomplete to avoid duplicate entries? - ruby-on-rails

I'm using the rails3-jquery-autocomplete gem my artist field on a releases form and want to make sure any new releases use the id of an existing artist if found via autocomplete or create a new artist if not found. At the moment the autocomplete gem returns artists as expected but created a new entry even if that artist exists.
My models are as follows:
class Artist < ActiveRecord::Base
has_and_belongs_to_many :releases
end
class Release < ActiveRecord::Base
has_and_belongs_to_many :artists
accepts_nested_attributes_for :artists, :reject_if => lambda { |a| a[:name].blank? }
end
And controllers:
class ArtistsController < ApplicationController
def index
#artists = Artist.find(:all, :order => :name, :group => :name)
end
def create
#release = Release.find(params[:release_id])
#artist = #release.artists.create(params[:artist])
redirect_to release_path(#release)
end
def destroy
#release = Release.find(params[:release_id])
#artist = #release.artists.find(params[:id])
#artist.destroy
redirect_to release_path(#release)
end
end
class ReleasesController < ApplicationController
autocomplete :artist, :name
def new
#release = Release.new
#release.artists.build
end
def create
#release = Release.new(params[:release])
#release.user_id = current_user.id
end
end
My routes contains:
resources :releases do
get :autocomplete_artist_name, :on => :collection
end
And finally the artist fields:
<%= f.fields_for :artists do |builder| %>
<%= render 'artist_fields', :f => builder %>
<% end %>
(The rendered fields)
<%= f.label :name, "Artist" %><br />
<%= f.autocomplete_field :name, autocomplete_artist_name_releases_path, :class => "text" %>

I suggest you watch this railscast on autcomplete associations
It clearly does what you need.
I don't want to spoil your viewing pleasure, but it all revolves around find_or_create_by

Why don't you fetch the id of the artist with
:id_element => '#artist_id' on the f.autocomplete_field?
If the artist_id param does not come back, you can create it

Related

can't write unknown attribute `company_profile_id`

I'm trying to add a set of users to a company_profile object. The idea is a user will create a company and then add more users to the company in various roles.
The company profile has an address object, and when I pull up the form on the new call I get this error:
"can't write unknown attribute company_profile_id"
company_profile -> new
<%= form_for(setup_companyProfile(#companyProfile), validate: true, html: { multipart: true }) do |f| %>
<%= f.fields_for :address do |address| %>
<%= render :partial => 'shared/address', :locals => {:f => address} %>
<% end %>
<% end %>
user.rb
belongs_to :company_profile
helper.rb
def setup_companyProfile(companyProfile)
if(companyProfile.address.present? == false)
companyProfile.address ||= Address.new
end
companyProfile
end
company_profile.rb
class CompanyProfile < ApplicationRecord
has_many :users
has_one :address
accepts_nested_attributes_for :address
end
company_profile_controller.rb
class CompanyProfileController < ApplicationController
def new
#companyProfile = CompanyProfile.new
end
def edit
#companyProfile = CompanyProfile.find(current_user.company_profile_id)
end
def update
end
def show
end
end
When you use has_one from the company-profile model... Rails expects there to be a belongs_to :company_profile on the Address model... and this belongs_to requires a column called company_profile_id on the addresses table... do you have that? If not - you will need to create a migration that adds it.

Rails nested fields creation in loop

I have three model classes related to each other.
class Student < ActiveRecord::Base
has_many :marks
belongs_to :group
accepts_nested_attributes_for :marks,
reject_if: proc { |attributes| attributes['rate'].blank?},
allow_destroy: true
end
This class describes a student that has many marks and I want to create a Student record along with his marks.
class Mark < ActiveRecord::Base
belongs_to :student, dependent: :destroy
belongs_to :subject
end
Marks are related both to the Subject and a Student.
class Subject < ActiveRecord::Base
belongs_to :group
has_many :marks
end
When I try to create the nested fields of marks in loop labeling them with subject names and passing into in it's subject_id via a loop a problem comes up - only the last nested field of marks is saved correctly, whilst other fields are ignored. Here's my form view code:
<%= form_for([#group, #student]) do |f| %>
<%= f.text_field :student_name %>
<%=f.label 'Student`s name'%><br>
<%= f.text_field :student_surname %>
<%=f.label 'Student`s surname'%><br>
<%=f.check_box :is_payer%>
<%=f.label 'Payer'%>
<%= f.fields_for :marks, #student.marks do |ff|%>
<%#group.subjects.each do |subject| %><br>
<%=ff.label subject.subject_full_name%><br>
<%=ff.text_field :rate %>
<%=ff.hidden_field :subject_id, :value => subject.id%><br>
<%end%>
<% end %>
<%= f.submit 'Add student'%>
<% end %>
Here`s my controller code:
class StudentsController<ApplicationController
before_action :authenticate_admin!
def new
#student = Student.new
#student.marks.build
#group = Group.find(params[:group_id])
#group.student_sort
end
def create
#group = Group.find(params[:group_id])
#student = #group.students.new(student_params)
if #student.save
redirect_to new_group_student_path
flash[:notice] = 'Студента успішно додано!'
else
redirect_to new_group_student_path
flash[:alert] = 'При створенні були деякі помилки!'
end
end
private
def student_params
params.require(:student).permit(:student_name, :student_surname, :is_payer, marks_attributes: [:id, :rate, :subject_id, :_destroy])
end
end
How can I fix it?
#student.marks.build
This line will reserve an object Mark.
If you want multi marks, May be you need something like this in new action :
#group.subjects.each do |subject|
#student.marks.build(:subject=> subject)
end
Hope useful for you.

Updating a has_many :through association with a rails form

I'm trying to update the form for a Song model that has multiple Genres through a song_genre_relationships model. I'd prefer to update the genre id's for any given song through an array in the form field.
I suspect my controller is off, but I'm still pretty new to Rails and am unsure.
Here are my models, form and controller.
EDITED BASED ON meagar's RESPONSE BELOW.
song.rb
class Song < ActiveRecord::Base
has_many :song_genre_relationships
has_many :genres, :through => :song_genre_relationships
end
song_genre_relationships.rb
class SongGenreRelationship < ActiveRecord::Base
belongs_to :song
belongs_to :genre
end
genre.rb
class Genre < ActiveRecord::Base
has_many :song_genre_relationships
has_many :songs, :through => :song_genre_relationships
end
song_controller.rb
class SongController < ApplicationController
def update
#song = Song.find(params[:id])
relationships = #song.song_genre_relationships
#genres = Genre.all
#genres.each do |genre|
unless relationships.detect { |g| g.genre_id == genre.id }
relationships.build genre_id: genre.id
end
end
if #song.update_attributes(song_params)
flash[:notice] = "Song updated successfully."
redirect_to(:action => 'index')
else
render('edit')
end
end
private
def song_params
params.require(:song).permit(:name, :genre_ids => [])
end
_form.html.erb (song form)
<%= form_for(#song) do |f| %>
<%= f.label :name, 'Title' %>
<%= f.text_field :name %>
<%= f.label :genre_ids, 'Genres' %>
<%= f.text_area :genre_ids %>
<%= f.submit %>
<% end %>
This is wrong:
#genres = params[:song][:genre_ids]
Array(#genres).each do |genre|
unless relationships.detect { |g| g.genre_id == genre.id }
relationships.build genre_id: genre
end
end
params[:song][:genre_ids] is a string. It contains, in your example, "[<whatever id's input to the form>]".
You're wrapping it in an array with Array(#genres). Now, you have an array containing a single item, which is the string. Then you iterate over that single item, placing it into genre inside the loop
Then you test g.genre_id == genre.id. genre is that string, and you're invoking .id on it, hence your error.
It's impossible to tell what you're trying to do, but you definitely cannot invoke .id on a string and expect to get the ID of some database record back.

Add model/database association upon create

I have a model named Entry, which has many Categories. The page where I create/edit the Entry has checkboxes for every Category.
When I am editing an Entry, everything works okay. When I create the Entry, I get as an error for the #entry:
:entry_categories=>["is invalid"]
My thinking is that rails can't create the entry_categories because it doesn't know the id of the Entry ( which it shouldn't, it hasn't been assigned an id yet ).
I feel like this is a very common thing to try to do. I haven't been able to find an answer though. Here comes the code spamming, there must be something I am missing and hopefully some more experienced eyes can see it.
entry.rb
class Entry < ActiveRecord::Base
validates_presence_of :contents
validates_presence_of :title
has_many :entry_categories, dependent: :destroy
has_many :categories, through: :entry_categories
belongs_to :book
validates_presence_of :book
end
entry_category.rb
class EntryCategory < ActiveRecord::Base
belongs_to :entry
belongs_to :category
validates_presence_of :entry
validates_presence_of :category
end
category.rb
class Category < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :name
has_many :entry_categories, dependent: :destroy
has_many :entries, through: :entry_categories
end
entries_controller.rb
class EntriesController < ApplicationController
before_action :find_entry, only: [ :show, :edit, :update, :destroy ]
before_action :find_book, only: [ :new, :create, :index ]
before_action :authenticate_admin!, only: [:new, :create, :edit, :update, :destroy ]
def new
#entry = Entry.new
end
def create
#entry = #book.entries.new( entry_params )
if #entry.save
redirect_to entry_path( #entry ), notice: 'Entry Created'
else
render :new
end
end
def show
#categories = Category.joins( :entry_categories ).where( "entry_categories.entry_id = #{#entry.id} " ).select( "name, categories.id " )
#category_class = #categories.first.name.downcase.gsub( / /, '_' ) if #categories.any?
end
def index
#entries = #book ? #book.entries : Entry.all
end
def edit
end
def update
if #entry.update( entry_params )
redirect_to entry_path( #entry ), notice: 'Entry Updated'
else
render :edit
end
end
def destroy
#book = #entry.book
#entry.destroy
redirect_to book_path( #book ) , notice: 'Entry Destroyed'
end
protected
def entry_params
params.require(:entry).permit( :title, :contents, :year, :month, :day, category_ids: [] )
end
def find_entry
#entry = Entry.find( params[:id] )
end
def find_book
#book = Book.find( params[ :book_id ] )
rescue
#book = nil
end
end
_form.html.erb
<%= form_for [ #book, #entry ] do | form | %>
<%= content_tag :p, title %>
<%= form.text_field :title, placeholder: 'Title', required: true %>
<%= form.number_field :year, placeholder: 'Year' %>
<%= form.number_field :month, placeholder: 'Month' %>
<%= form.number_field :day, placeholder: 'Day' %>
<%= form.text_area :contents %>
<fieldset>
<legend>Categories</legend>
<%= form.collection_check_boxes(:category_ids, Category.all, :id, :name ) %>
</fieldset>
<%= form.submit %>
<% end %>
So again, the entry_categories are invalid in the create method, in the update they are fine. It's the same html file.
There must be some way to tell rails to save the Entry before trying to save the EntryCategory?
Thanks.
I managed to get this working by taking the validations out of EntryCategory:
class EntryCategory < ActiveRecord::Base
belongs_to :entry
belongs_to :category
validates_presence_of :category
end
I'm not particularity happy about this solution, and would still appreciate other thoughts.
I think you can use any of the following approach:
You can use autosave functionality of Active Record association. By this, when you will save EntryCategory, it will automatically save Entry as well.
class EntryCategory < ActiveRecord::Base
belongs_to :entry , autosave: true
#rest of the code
end
You can also use before_save callback of active record. by this, whenever you will save EntryCategory, it will first call a specified method, than proceed with saving.
class EntryCategory < ActiveRecord::Base
before_save :save_associated_entries
#rest of the code
def save_associated_entries
# code to save associated entries here
end
end
Try this:
replace this code:
<%= form.collection_check_boxes(:category_ids, Category.all, :id, :name ) %>
with:
<% Category.all.order(name: :asc).each do |category| %>
<div>
<%= check_box_tag "entry[category_ids][]", category.id %>
<%= category.name %>
</div>
you can format it with fieldset instead of div

Rails: Creating a Multiple Model Form over n association levels

Can anyone tell me why the form at the end of this question isn't working like it should?
Save doesn't work
The select-helper doesn't select the
value according to the object #kid
The whole thing is based on Rails 2.2.2 and no, upgrading to Rails 2.3 to solve this problem isn't an option. :-)
I used this recipe to build the multiple model form.
# CLASS GRANDPARENT
class Grandparent < ActiveRecord::Base
has_many :parents
end
# CLASS PARENT
class Parent < ActiveRecord::Base
belongs_to :grandparent, :class_name => "Grandparent", :foreign_key => "grandparent_id"
has_many :kids
end
# CLASS KID
class Kid < ActiveRecord::Base
belongs_to :parent, :class_name => "Parent", :foreign_key => "parent_id"
# Virtual attribute setter for new self.parent.grandparent (Grandparent) attributes
def new_grandparent_attributes=(_gp_attributes)
self.parent.build_grandparent(_gp_attributes)
end
# Virtual attribute setter for existing self.parent.grandparent (Grandparent) attributes
def existing_grandparent_attributes=(_gp_attributes)
unless self.parent.grandparent.new_record?
attributes = _gp_attributes[self.parent.grandparent.id.to_s]
if attributes
self.parent.grandparent.attributes = attributes
else
self.parent.grandparent.delete(grandparent)
end
end
end
end
# CONTROLLER KIDS
class KidsController < ApplicationController
def new
#kid = Kid.new
end
def edit
#kid = Kid.find(params[:id])
end
def create
params[:kid][:new_grandparent_attributes] ||= {}
#kid = Kid.new(params[:kid])
end
def update
params[:kid][:existing_grandparent_attributes] ||= {}
#kid = Kid.find(params[:id])
end
end
# THIS IS THE MULTI-MODEL FORM USED IN THE VIEW
<% form_for(#kid) do |f| %>
<p>
<% new_or_existing = #kid.parent.grandparent.new_record? ? 'new' : 'existing' %>
<% prefix = "kid[#{new_or_existing}_grandparent_attributes][]" %>
<% fields_for prefix, #kid.parent.grandparent do |g_f| -%>
<p>
<%= g_f.label :, 'Grandparent Name' %><br />
<!-- THE FOLLOWING FORM DOESN'T CHANGE ACCORDING TO EXISTING #child -->
<%= #grandparents = Entity.find(:all, :order => :name)
g_f.collection_select(:name ,#grandparents, :id, :name)
%>
</p>
<% end %>
</p>
<p>
<%= f.label :name, "Kid Name" %><br />
<%= f.text_field :name %>
</p>
<%= submit_tag 'Go' %>
<% end %>
Well, correct me if I am wrong but it doesn't appear that you are actually saving the object anywhere. In your create and update actions you are calling new and then not saving it.
To rectify this you could do:
def create
params[:kid][:new_grandparent_attributes] ||= {}
#kid = Kid.new(params[:kid])
if #kid.save
# successful save logic here
else
#failed save logic here
end
end
def update
params[:kid][:existing_grandparent_attributes] ||= {}
#kid = Kid.find(params[:id])
if #kid.update_attributes(params[:kid])
#successful save logic here
else
#failed save logic here
end
end
Then in your select box you are trying to find every record of Entity, not those fields of Entity that are related to #kid. In order to do this you'll have to set up a relationship between kid and grandparent.
# CLASS GRANDPARENT
class Grandparent < ActiveRecord::Base
has_many :parents
has_many :grand_kids, :through => :parents
end
# CLASS PARENT
class Parent < ActiveRecord::Base
belongs_to :grandparent, :class_name => "Grandparent", :foreign_key => "grandparent_id"
has_many :kids
end
# CLASS KID
class Kid < ActiveRecord::Base
belongs_to :parent, :class_name => "Parent", :foreign_key => "parent_id"
belongs_to :grandparent
# ...
This way you can access a kid's grandparents through by #kid.grandparents. Then you can generate the select field:
<%= g_f.collection_select(:name ,#kid.grandparents, :id, :name) %>

Resources