DataMapper Many-To-Many Association using Sinatra - ruby

I just stared learning Sinatra and DataMapper while building a simple blog. I seem to have encountered a problem getting my Many-To-Many association working. I'm trying to associate categories with posts. Upon creation of a post, the category association is not created.
Here are my models:
class Post
include DataMapper::Resource
has n, :categories, :through => Resource
property :id, Serial
property :title, String
property :slug, String
property :body, Text
property :description, Text
property :created_at, DateTime
property :updated_at, DateTime
property :posted_at, DateTime
end
class Category
include DataMapper::Resource
has n, :posts, :through => Resource
property :id, Serial
property :title, String
end
DataMapper successfully builds the category_posts table. I don't think the code is correct in my create form.
<form action="/post/create/" method="post">
<% #category = Category.all %>
<% #category.each_with_index do |cat,i| %>
<input id="category<%=i%>" type="checkbox" value="<%= cat.title %>" name="post.category.<%=cat.id%>" />
<label for="category<%=i%>"><%= cat.title%></label>
<% end %>
<p>
<input type="submit">
</p>
</form>
I have tried manually creating entries in the category_posts table and no records show up. Here is the part of my view related to categories. The count is for debugging, it always reads 0.
<%= #post.categories.count %>
<% #post.categories.each do |category| %>
<p>Test: <%= category.title %></p>
<% end %>
Any ideas what I am doing wrong?
Thanks

The documentation for Datamapper (section for "Has, and belongs to, many (Or Many-To-Many)") has some hints, as #Mika Tuupola points out, it looks like you've set up your models correctly, the problem might be in using your models:
post = Post.create
category = Category.create
# link them by adding to the relationship
post.categories << category
post.save
p post.categories # => [#<Category #id=1>]

I've got limited experience with datamapper, but do you need to define :key => true on both of the IDs?

Related

Unpermitted parameters in Rails 5 using has_many, through

I keep getting unpermitted parameters for my appointment model.
Here are my models
class Appointment < ApplicationRecord
belongs_to :client
belongs_to :trainer
end
class Trainer < ApplicationRecord
has_many :appointments
has_many :clients, through: :appointments
end
class Client < ApplicationRecord
has_many :appointments
has_many :trainers, through: :appointments
end
Here's my controller, I just listed my private method for sake of brevity.
def appt_params
params.require(:appointment).permit(:appointment_date, client_id: [],
trainer_id: [])
end
The error says unpermitted parameters for trainer, client.
Am I missing something in my strong parameters method?
Here is my appointments/new view
<%= form_for #appointment do |f| %>
<%= f.datetime_select :appointment_date %>
<%= f.collection_select :trainer, Trainer.all, :id, :first_name %>
<%= f.collection_select :client, Client.all, :id, :name %>
<%= f.submit %>
<% end %>
I added collection to my appt_params method and still getting the same error. I'm still getting the hang of Rails and any help would be appreciated, thanks!
Since you have used associations, only client_id and trainer_id is enough and those should of integer form not array.
So change your strong parameters method code to :
def appt_params
params.require(:appointment).permit(:appointment_date, :client_id,
:trainer_id)
end
And your applications/new view to:
<%= form_for #appointment do |f| %>
<%= f.datetime_select :appointment_date %>
<%= f.collection_select :trainer_id, Trainer.all, :id, :first_name %>
<%= f.collection_select :client_id, Client.all, :id, :name %>
<%= f.submit %>
<% end %>
Was running with the same issue as you and after hours of debugging I was able to solve it:
Was my first time trying associations in Rails with models similar to you, so most documentation online recommends to define the foreign key's as client_ids:[] assuming your ids were arrays, when definitely they are integers as my parameters where
Parameters: {"utf8"=>"✓", "authenticity_token"=>"LEcwQ56xYJGpq2zIs6Cz0YbU7B7mBKRa6rhspVIxo9vEB5/UoFUvHYiN0UC0krTiIp+d0tzhit6DZT1Z8PmYYg==", "califica"=>{"text"=>"hi", "grade"=>"5", "user_id"=>"1", "party_id"=>"1"}, "commit"=>"Create Calification"}
I think this is due to f.collection_select taking as expected one value. So after hours of using the permit of arrays such as :user_id => [] , user_id:[] I was getting always error of Unpermitted parameters.
Tried the answer of #Bharath (which is correct) but it was still not working, that's was when I realized that my old models weren't made with references (ActiveModel::UnknownAttributeError (unknown attribute 'user_id' for Calification.):) so I had to make a references migration to add a foreign key and then it was all working perfectly.

Rails Validation of Association IDs

I am building a Rails 4.2.4 app where I have Units and Medics. When I edit each unit I have two spots for the medics, incharge and attendant. I want some way to validate that both the incharge_id and attendant_id are not the same. That way I can't assign myself as both positions on the unit.
Here is what my model and form view looks like.
unit.rb
class Unit < ActiveRecord::Base
belongs_to :attendant, :foreign_key => :attendant_id, :class_name => 'Medic'
belongs_to :incharge, :foreign_key => :incharge_id, :class_name => 'Medic'
belongs_to :unit_status
end
medic.rb
class Medic < ActiveRecord::Base
has_many :units
end
units/_form.html.erb
<%= form_for(#unit) do |f| %>
<%= f.label 'Attendant'%>
<%= f.collection_select(:attendant_id, Medic.order('name ASC'), :id, :name, {}) %>
<%= f.label 'In Charge'%>
<%= f.collection_select(:incharge_id, Medic.order('name ASC'), :id, :name, {}) %>
<%= f.label 'Unit Status'%>
<%= f.collection_select(:unit_status_id, UnitStatus.order("status ASC"), :id, :status, {})%>
<%= f.submit "Update" %>
<% end %>
So in summary if I edit a unit and I accidentally assign the id of "1" to the unit, I want to error out and give some sort of message, "Cannot assign the same medic to both positions". Something like that.
The only thing I can think of, is to somehow filter the params in the controller saying if the params of attendant_id and incharge_id are are == then redirect to the edit_unit_path and display a flash message, "You cannot assign the same medic to both positions".
It seems like it would be better to do validations on the model side instead of stuffing logic in the controller, but I'm not sure how to simultaneous validate the two different columns for uniqueness.
I came up with this on the Unit model.
validate :attendant_and_incharge
def attendant_and_incharge
errors.add(:attendant_id, "can't be the same as the incharge") if attendant_id == incharge_id
end
This will not allow me to save the same id to the unit model for attendant_id and incharge_id. It fails silently and directs to the units_path. Just need to put some conditional in the controller to redirect to the edit path on failure. (ThumbsUp)

How to associate intermediary model's extra column with value from a form

I have a many to many relationship between Plays and Users with the intermediary WorksOn.
class Play < ActiveRecord::Base
has_many :works_on
has_many :users, :through => :works_on
end
class User < ActiveRecord::Base
has_many :works_on
has_many :plays, :through => :works_on
end
#The WorksOn model also has a :student_role field
class WorksOn < ActiveRecord::Base
belongs_to :user, :foreign_key => 'user_id' #these columns were added via a migration
belongs_to :play, :foreign_key => 'play_id'
end
And in Play's form I have a checkbox to select users
<div class="field" id="checkbox-list">
<%= f.label "Cast and Crew"%> <br />
<%= collection_check_boxes(:user_id, :user_ids, #users, :id, :simple_to_s) %> #simple_to_s defined in Play's model. #users defined in play controller's 'show' form
</div>
In Play's controller I manually create the associations using the checkbox id's.
#...
def update
#grab user_id from params and then the user_ids values
works_on = params[:user_id]
works_on = works_on[:user_ids].select{|id|id.length!=0} #rails includes hidden empty string field for checkboxes, must filter it out by removing the 0 length string
WorksOn.where(:play_id=>#play.id).destroy_all #remove previous associations
works_on.each do |user_id|
WorksOn.create(:play_id=>#play.id, :student_role => "aaaaaaaaa",:user_id=>user_id) #create new associations to user
end
#other update stuff -> respond_to do |format| ...
end
#...
The controller currently works but I want to have a text field associated with each check box, and when the admin hits submit the :student_role field uses that value instead of "aaaaaaaaa". Any ideas on how to get this started?
Also, bonus question. Why is :user_id a part of params and not play_params?
Thanks so much!
This ended up being more simple than I expected!
Play's new form
<div class="col-md-6 field checkbox-list">
<%= f.label "Cast and Crew"%> <br />
<%= collection_check_boxes(:user_id, :user_ids, #users, :id, :simple_to_s) %>
</div>
<div class="col-md-6 field user-roles-text-list">
<%= f.label "Roles and Responsibilities (eg Actor: Hamlet)"%> <br />
<% #users.each do |user| %> #add a text field for each checkbox
<%= text_field_tag "user_role[#{user.id}]", nil, placeholder: user.simple_to_s %>
<% end %>
</div>
Play controller
def associate_play_with_users( params )
user_ids = params[:user_id] #grab user_id hash from params
user_roles = params[:user_role]
user_ids = user_ids[:user_ids] #select the :user_ids key's values from the hash
user_ids = user_ids.select{|id|id.length!=0}#rails includes hidden empty string field for checkboxes, must filter it out by removing the 0 length string
WorksOn.where(:play_id=>#play.id).destroy_all #remove previous associations
user_ids.each do |user_id|
WorksOn.create(:play_id=>#play.id, :student_role => user_roles[user_id],:user_id=>user_id) #create new associations to user
end
end
Parameters
Parameters: {"utf8"=>"✓", "authenticity_token"=>"+7rIGuD59ld5lQHfTtRY130TkGCNnnHm4GJfBQJkP3C0IKmz4YStDxOcuxc2pKsS8nodB/UV1fR5XwWNXXbJ4w==",
"play"=>{"title"=>"As You Like It", "description"=>"a dope play faaao sho", "date_of_play(1i)"=>"3000", "date_of_play(2i)"=>"2", "date_of_play(3i)"=>"2", "date_of_play(4i)"=>"00", "date_of_play(5i)"=>"00"},
"user_id"=>{"user_ids"=>["69", "70", ""]},
"user_role"=>{"69"=>"ss", "70"=>"sssasasasas", "71"=>"", "72"=>"", "73"=>"", "74"=>""}, #only the ids from user_id are used, 69 and 70
"commit"=>"Update Play", "id"=>"17"}

Rails 3 creates duplicate form object for nested records when updating them

There are two models
class Category < ActiveRecord::Base
has_many :products
accepts_nested_attributes_for :products, allow_destroy: true
validates_associated :products
end
class Product < ActiveRecord::Base
belongs_to :category
validate_uniqueness_of :name, scope: :category_id
end
And a form for category
<%= simple_form_for #category do |f| %>
<%= f.simple_fields_for :products do |p| %>
<%= render :partial => "product_fields", :locals => { :f => p } %>
<% end %>
<% end %>
As you can see there is a uniqueness validation which makes sure that products are uniq within category. The problem starts when validation is not passed.
Calling #category.update_attributes(params[:category]) results in duplicate form object. When rendering the form with errors rails creates additional associated product with the id of duplicate record but different name.
For example:
Given we have two products within category: Bread and Butter. If set Butter to Bread when editing a category's products a new form object will be created and the form will be rendered with Bread, Butter, Bread, saying last entry has a duplicate name.
How can i prevent rails from creating those duplicate records? I'm using rails 3.2.11 if it matters.

How to output a specific category in my view?

i have a rails 3 blog application which has article and category "category belongs_to :article" Article has_many :category, now I have different category which have many article for instance sport category which contain all sport article, what i want in my application layout i want to show only sport article in on div please tell me how to go about it. thank you ...
class Article < ActiveRecord::Base
attr_accessible :category_id, :content, :excerpt, :title, :image, :remote_image_url
mount_uploader :image, ImageUploader
belongs_to :category
validates :title, :content, :excerpt, :category_id, presence: true
validates :title, uniqueness: true
extend FriendlyId
friendly_id :title, use: [:slugged, :history]
def long_title
" #{title} - #{created_at} "
end
end
Updated according to your comments:
First step, get all the categories, let's say you do this in categories#index action:
def index
#categories = Category.all
end
Now we're going to make a link to each category with that category's name in the categories#index view. When users click on a category they will be taken to that category's show page where we'll list all related articles:
<% #categories.each do |category| %>
<%= link_to category.name, category %>
<% end %>
We add the following in the categories#show action:
def show
#category = Category.includes(:articles).find(params[:id])
#articles = #category.articles
end
Then you can show all those articles by iterating over the #articles variable in the corresponding categories#show view. For example adding them as links in a some div:
<div class="articles">
<% #articles.each do |article| %>
<%= link_to article.title, article %><br>
<% end %>
</div>
Now do the same in the articles controller for the show action:
def show
#article = Article.find(params[:id])
end
So basically a user will see a link that says "Sports", when he clicks on it he will be taken to a page such as yoursite.com/categories/4 or yoursite.com/categories/sports if you're using friendly_id. Under this page all related sports articles will be listed and when they are clicked on the user will be taken to that article's show page.

Resources