I am not sure if my title is properly using the vocabulary. I found a few SO posts where people had similar issues (I couldn't find a solution in those posts), so I used a similar title to those. I am trying to allow users to create clubs and automatically assign the user as a member of the club. However, it seems like I have something out of order when I tried to create the club.
I have three models:
###Models###
#user
has_many :club_memberships, :class_name => 'ClubMembership', :foreign_key => :member_id, :primary_key => :id
has_many :clubs, :through => :club_memberships
#club
attr_accessor :club_name, :club_type
has_many :club_memberships
has_many :members, :through => :club_memberships
#clubmembership
attr_accessor :club_id, :member_id, :membership_type
belongs_to :club
belongs_to :member, :class_name => "User", :foreign_key => :member_id, :primary_key => :id
Here are the relevant parts of the controllers
###Controllers###
#Clubs
def new
#club = Club.new
end
def create
#club = Club.new(club_params)
if #club.save
#club_membership = ClubMembership.create(
member_id: current_user.id,
club_id: #club.id,
membership_type: 'founder'
)
flash[:success] = "Club Created!"
redirect_to root_url
else
render #club.errors.full_messages
end
end
private
def club_params
params.require(:club).permit(:club_name, :club_type)
end
#ClubMemberships
def create
#club_membership = ClubMembership.new(club_membership_params)
if #club_membership.save
render #club_membership
else
render #club_membership.errors.full_messages
end
end
private
def club_membership_params
params.require(:club_membership).permit(:member_id, :club_id, :membership_type)
end
My form_for
###View###
#club#new
= form_for(#club) do |f|
.field.center-align
= f.label :club_name
= f.text_field :club_name, :class => "form-control fieldbox", autofocus: true
.field.center-align
= f.label :club_type
= f.text_field :club_type, :class => 'form-control fieldbox', autofocus: true
.actions.center-align
= f.submit "Create Club!", :class => "btn hoverable padtop"
And finally, here is what the log shows on post
#log
Started POST "/clubs" for at 2015-09-03 22:32:41 +0000
Cannot render console from ! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
Processing by ClubsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"Ar2dv41/Tqk9EVjwfLLeD8bnpLoVWQIdDxG3Ju1GO3stLLvPd/FFgoFF9YuHobWbgb2byqkgAMiWRJAg5YcGKQ==", "club"=>{"club_name"=>"Test Club", "club_type"=>"Test Type"}, "commit"=>"Create Club!"}
(0.2ms) BEGIN
Club Exists (0.4ms) SELECT 1 AS one FROM `clubs` WHERE `clubs`.`club_name` = BINARY 'Test Club' LIMIT 1
SQL (0.3ms) INSERT INTO `clubs` (`created_at`, `updated_at`) VALUES ('2015-09-03 22:32:41', '2015-09-03 22:32:41')
(3.4ms) COMMIT
User Load (0.1ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 56 LIMIT 1
(0.1ms) BEGIN
ClubMembership Exists (0.3ms) SELECT 1 AS one FROM `club_memberships` WHERE (`club_memberships`.`member_id` = BINARY 56 AND `club_memberships`.`club_id` IS NULL) LIMIT 1
SQL (1.6ms) INSERT INTO `club_memberships` (`created_at`, `updated_at`) VALUES ('2015-09-03 22:32:41', '2015-09-03 22:32:41')
(3.1ms) COMMIT
Redirected to c9
Completed 302 Found in 71ms (ActiveRecord: 11.1ms)
I'll be honest. I have no clue what is happening in that POST or where to tackle this from next. It seems like the parameters I want are going through, but then they aren't. Any help would be appreciated.
I figured it out. I tried two things at once and it worked, so I can't say for sure what helped, but I am pretty sure the second thing was most important.
First, I removed the attr_accessor from both model's (Club and ClubMembership). Why did I put it in there in the first place? No idea. I probably saw it somewhere and thought it was cool.
Second, also in the models, I had validators that I didn't post because I didn't think they were important. I had:
validates :club_name, :club_type, :presence => true
validates :club_name, :uniqueness => true
I changed this to:
validates :club_name, presence: true
validates :club_type, presence: true
Turns out that was important and everything is working fine now.
Related
Using Rails 4.1.13 and Ruby 2.0.0 (although I had the same problem with Ralis 4.0 and Ruby 1.9.3. I have read numerous articles about this particular issue and cannot understand why my solution (which seems exactly like this) does not work, so please help me out.
I have two models BlogPost and Tag. A BlogPost can have many Tags and one Tag can have many BlogPosts. I connect them through a third model BlogPostRelation. Thus, this is my basic setup:
# blog_post.rb
has_many :blog_post_tag_relations, dependent: :destroy
has_many :tags, :through => :blog_post_tag_relations
accepts_nested_attributes_for :blog_post_tag_relations, :tags
# tag.rb
has_many :blog_post_tag_relations, dependent: :destroy
has_many :blog_posts, :through => :blog_post_tag_relations
# blog_post_tag_relation.rb
belongs_to :tag
belongs_to :blog_post
validates_uniqueness_of :tag_id, :scope => [:blog_post_id]
validates :blog_post_id, :presence => true
validates :tag_id, :presence => true
accepts_nested_attributes_for :tag, :blog_post
I have a form for BlogPost, using Formtastic, where I create checkboxes for the BlogPost using:
<%= f.input :blog_title %>
<%= f.input :tags, as: :check_boxes, :collection => tags.order(:name) %>
The problem I have is that BlogPost is not saved before the Tags are added which causes an validation failure of blog_post_id not being present (which it isn't):
Tag Load (1.6ms) SELECT "tags".* FROM "tags" WHERE "tags"."id" IN (678, 56)
(0.9ms) BEGIN
BlogPost Exists (1.6ms) SELECT 1 AS one FROM "blog_posts" WHERE ("blog_posts"."id" IS NOT NULL) AND "blog_posts"."slug" = 'de22323' LIMIT 1
BlogPostTagRelation Exists (1.2ms) SELECT 1 AS one FROM "blog_post_tag_relations" WHERE ("blog_post_tag_relations"."tag_id" = 678 AND "blog_post_tag_relations"."blog_post_id" IS NULL) LIMIT 1
CACHE (0.0ms) SELECT 1 AS one FROM "blog_posts" WHERE ("blog_posts"."id" IS NOT NULL) AND "blog_posts"."slug" = 'de22323' LIMIT 1
BlogPostTagRelation Exists (1.1ms) SELECT 1 AS one FROM "blog_post_tag_relations" WHERE ("blog_post_tag_relations"."tag_id" = 56 AND "blog_post_tag_relations"."blog_post_id" IS NULL) LIMIT 1
CACHE (0.0ms) SELECT 1 AS one FROM "blog_posts" WHERE ("blog_posts"."id" IS NOT NULL) AND "blog_posts"."slug" = 'de22323' LIMIT 1
(0.8ms) ROLLBACK
It seems like the solution should be to use inverse_of, which I frankly don't understand to 100%. It should also be mentioned that I am not 100% sure on how to use accepts_nested_attributes_for either for this type of issue. I have tried all different setups but as far as I understand the only place they should be is in the join model, BlogPostRelation, like this:
# blog_post_tag_relation.rb
belongs_to :tag, :inverse_of => :blog_post_tag_relations
belongs_to :blog_post, :inverse_of => :blog_post_tag_relations
validates_uniqueness_of :tag_id, :scope => [:blog_post_id]
validates :blog_post_id, :presence => true
validates :tag_id, :presence => true
accepts_nested_attributes_for :tag, :blog_post
This doesn't work either and I am completely lost now in what to do.
Most important: What should I do?
Is inverse_of the solution to this problem? If so, how should I use it?
Am I using accepts_nested_attributes_for correctly?
Does it have to do with the naming of BlogPostTagRelation (should it have been called BlogPostTag instead?
Part of the problem here is that you are validating on ids. Rails cannot validate that blog_post_id is present if the id is not known, but it can validate that blog_post is present.
So part of the answer at least is to validate the presence of the associated instance, not the id.
Change the validations to:
validates :blog_post, :presence => true
validates :tag , :presence => true
I would always specify inverse_of as well, but I'm not sure it is part of this problem.
Your model structure is okay.
There's one nifty way you can add tags to your posts after the post is created. You just need to use a model method for doing that.You do not need inverse_of . Here is how:
In your view, add a custom attribute (all_tags).
<%= f.text_field :all_tags, placeholder: "Tags separated with comma" %>
You need to permit the parameter in the controller.
In your Post model add these three methods:
def all_tags=(names)
self.tags = names.split(",").map do |name|
Tag.where(name: name.strip).first_or_create!
end
end
#used when the post is being created. Splits the tags and creates entries of them if they do not exist. `self.tags` ensures that they tags will be related to the post.
def all_tags
self.tags.map(&:name).join(", ")
end
#Returns all the post tags separated by comma
def self.tagged_with(name)
Tag.find_by_name!(name).posts
end
#Returns all the posts who also contain the tag from the current post.
Here's a full implementation
You are using nested_attributes_for correctly, but in this case, you are having models who just have a name and a belongs_to column, so using this is an overkill.
You can call it a tagging, although there's no convention for naming. If you and other can understand it, it is fine.
What you really want to use is a has_and_belongs_to_many (HABTM) association: http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association
This however assumes you do not want to do anything with the Relationship Model (blog_post_tag_relations in your case)
You would need only the following models and associations:
class BlogPost < ActiveRecord::Base
has_and_belongs_to_many :tags
end
class Tag < ActiveRecord::Base
has_and_belongs_to_many :blog_posts
end
You would then have to rename your join table blog_post_tag_relations to blog_posts_tags, the alphabetical plural combination of the two models. Rails automagically looks up/uses that table seamlessly in the background. It will only have the relationship's foreign_keys:
create_table :blog_posts_tags, id: false do |t|
t.belongs_to :blog_post, index: true
t.belongs_to :tag, index: true
end
Then your form just works:
<%= f.input :blog_title %>
<%= f.input :tags, as: :check_boxes, :collection => tags.order(:name) %>
try
validates :blog_post, :presence => true
validates :blog_post_id, :presence => true, allow_nil: true #can ignore this
My Create controller passes in the for the child of a nested resource as null instead of passing the values i've just inputed into the form.
Here is my code
Routes:
resources :trips do
resources :pilgrims
end
Models:
Trip:
class Trip < ActiveRecord::Base
has_many :pilgrims
accepts_nested_attributes_for :pilgrims, :allow_destroy => true
attr_accessible :end_date, :leader_id, :name, :start_date, :pilgrim_attributes
end
Pilgrim:
class Pilgrim < ActiveRecord::Base
belongs_to :trip
attr_accessible :pilgrim_id, :surname, :name, :middle, :aka, :prefix, :address, :city, :state, :zip, :email, :telephone, :nationality, :date_of_birth, :passport_number, :expiration, :jordan, :room, :price, :status, :trip_id
end
My Pilgrim controller:
def new
#trip = Trip.find(params[:trip_id])
#pilgrim = Pilgrim.new
end
def create
#trip = Trip.find(params[:trip_id])
#pilgrim = #trip.pilgrims.build(params[:pilgrim])
if #pilgrim.save
flash[:notice] = "The <b>#{ #pilgrim.name }</b> has been created successfully."
redirect_to(trip_pilgrims_path, :notice => "The <b>#{ #pilgrim.name }</b> ship has been saved successfully.")
else
render(:new, :error => #pilgrim.errors)
end
end
Link to a gist with my form code Form
The routes seem to be correct, when i click on new_trip_pilgrim_path(#trip) it does point to trips/:trip_id/pilgrims/new and loads the new pilgrim form.
However when i click save on the form it redirects me to the route trips/3/pilgrims but shows the new pilgrim form saying all required fields were left blank.
This is what displays in the log.
Started POST "/trips/3/pilgrims" for 127.0.0.1 at 2013-01-19 22:12:06 -0800
Processing by PilgrimsController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"kOE06m3DNax43BOLYZ6t1lS7/T4wOWb2xM8m/mlQzvA=", "commit"=>"Create Pilgrim", "trip_id"=>"3"}
Trip Load (0.3ms) SELECT `trips`.* FROM `trips` WHERE `trips`.`id` = 3 LIMIT 1
(0.2ms) BEGIN
(0.2ms) ROLLBACK
Pilgrim Load (0.4ms) SELECT `pilgrims`.* FROM `pilgrims` WHERE `pilgrims`.`trip_id` = 3
Rendered pilgrims/_form.html.erb (36.4ms)
Rendered pilgrims/new.html.erb within layouts/application (37.2ms)
User Load (0.2ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 LIMIT 1
Trip Load (0.3ms) SELECT `trips`.* FROM `trips`
Completed 200 OK in 244ms (Views: 102.5ms | ActiveRecord: 11.2ms)
What is going on with the Pilgrim create controller?
You are trying to create a new pilgrim object by calling
#pilgrim = #trip.pilgrims.build(params[:pilgrim])
in the PilgrimsController. But Parameters: {"utf8"=>"✓", "authenticity_token"=>"kOE06m3DNax43BOLYZ6t1lS7/T4wOWb2xM8m/mlQzvA=", "commit"=>"Create Pilgrim", "trip_id"=>"3"} Doesn't contain any pilgrim paramters. So the problem is with the form used for creating a new pilgrim
Following is the problem puzzling me:
I have a model Word, and it has an attribute article_count
however, if I use:
w = Word.first
w.article_count = 1
w.save
the the first word's article_count will not change, the log shows:
Word Exists (0.4ms) ...
but I can use
w.update_attributes :article_count => 1
Maybe I misunderstood the function of save, can anyone explain to me?
======================= Update =======================
Here is my article model:
class Article < ActiveRecord::Base
attr_accessible :content, :title, :source, :url
has_many :article_wordships, :dependent => :destroy
has_many :words, :through => :article_wordships
has_many :paragraphs, :dependent => :destroy
alias_attribute :word_count, :article_wordships_count
validates_uniqueness_of :title
end
Here is my code:
Article.select(:title).all.each do |a|
a.title = a.title.gsub(/\A.+?;\s(.+)/, '\1')
a.save
end
And after run the code, nothing changes in the articles.
Here is the console info:
1.9.3p194 :003 > a.save
(0.2ms) BEGIN
Article Exists (0.6ms) SELECT 1 AS one FROM `articles` WHERE (`articles`.`title` = BINARY 'A Shared State of Defeat' AND `articles`.`id` != 178) LIMIT 1
(0.9ms) UPDATE `articles` SET `title` = 'A Shared State of Defeat', `updated_at` = '2012-09-08 02:58:33' WHERE `articles`.`id` = 178
(2.2ms) COMMIT
=> true
Both save and save! return true.
Strangely, SOMETIMES IT WORKS, SOMETIMES IT DOES NOT WORK!
I'm getting mad...
I am not sure what you mean "SOMETIMES IT WORKS, SOMETIMES IT DOES NOT WORK!" It seems to work in the example you give. If by not work you mean it does not always issue updates, it might be that this line does not change the title. ActiveRecord does dirty checking and if the value of title does not change, it will not issue an update.
a.title = a.title.gsub(/\A.+?;\s(.+)/, '\1')
I have 2 types of users, one is called list_owner and one is called subscriber. These are both users made with devise. Subscribers should be able to follow the list_owner. I have another model called Relationship to keep track who the subscriber follows.
I used http://ruby.railstutorial.org/ mostly as an example.
These are the relations
class Relationship < ActiveRecord::Base
attr_accessible :list_owner_id, :subscriber_id
belongs_to :list_owner
belongs_to :subscriber
end
class ListOwner < ActiveRecord::Base
has_many :messages
has_many :relationships, :dependent => :destroy, :foreign_key => "list_owner_id"
end
class Subscriber < ActiveRecord::Base
has_many :relationships, :foreign_key => "subscriber_id",
:dependent => :destroy
def follow!(list_owner)
puts "-----------------------"
relationships.create!(:subscriber_id => subscriber.id, :list_owner_id => list_owner.id)
end
end
I made a list with list owners with a button where the subscriber can subscribe to.
<%= form_for current_subscriber.relationships.build(:list_owner_id => l.id, :subscriber_id => #subscriber.id),
:remote => true do |f| %>
<%= f.hidden_field :list_owner_id %>
<%= f.hidden_field :subscriber_id %>
<%= f.submit "Subscribe", :class => "button btn" %>
<% end %>
class RelationshipsController < ApplicationController
def create
puts "---------------------relationships CREATE---------------------"
#list_owner = ListOwner.find(params[:relationship][:list_owner_id])
current_subscriber.follow!(#list_owner)
redirect_to root_path
end
end
This is the error i get. What am i doing wrong?
Started POST "/relationships" for 127.0.0.1 at Wed Sep 07 15:37:17 +0200 2011
Processing by RelationshipsController#create as JS
Parameters: {"commit"=>"Subscribe", "relationship"=>{"list_owner_id"=>"2", "subscriber_id"=>"1"}, "authenticity_token"=>"m5plsJLdzuwNt1yKfDJFKD28GcR138V+pezbfbECCPk=", "utf8"=>"✓", "_"=>""}
ListOwner Load (0.2ms) SELECT "list_owners".* FROM "list_owners" WHERE "list_owners"."id" = 2 LIMIT 1
Subscriber Load (0.2ms) SELECT "subscribers".* FROM "subscribers" WHERE "subscribers"."id" = 1 LIMIT 1
Completed 500 Internal Server Error in 34ms
NameError (undefined local variable or method `subscriber' for #<Subscriber:0x102f140c0>):
app/models/subscriber.rb:21:in `follow!'
app/controllers/relationships_controller.rb:8:in `create'
There's no such 'subscriber' object defined, but you don't need one, since you're calling the create!() method on the association collection, which will pre-populate the parent object. That is, calling subscriber.relationships.create! will set subscriber_id = subscriber.id.
def follow!(list_owner)
relationships.create!(:list_owner_id => list_owner.id)
end
I've created three classes to represent Books, People, and BookLoans. While I am able to show the association of People to Books through BookLoans I've been seeding my database.
I now need to save a checkout of a book. It was my intention to do this action through the book controller. Specifically, creating a loan action in the BooksController. While this makes sense to me in theory I am having a terrible time implementing the appropriate syntax.
I've added the ability to loan a book from the show view of a book. This view now contains a form which uses the loan action of the book controller to record the loan.
I've added what I believe are the appropriate methods to my Book model. With the help of theIV I have captured the appropriate information in the Controller. Unfortunately, when I press Loan on the book show view a book_loan record is no being recorded.
What am I missing?
Book Model
class Book < ActiveRecord::Base
has_many :book_loans
has_many :borrowers, :through => :book_loans, :source => :person
accepts_nested_attributes_for :book_loans, :allow_destroy => true
def loaned?
book_loans.exists?(:return_date => nil)
end
def current_borrower
if loaned?
book_loans.first(:order => "out_date desc").person
end
end
def add_loan (person_id)
book_loans.create(:book_id => id,
:person_id => person_id,
:out_date => Date.current)
end
end
Loan Method from BooksController
def loan
#book.add_loan(params[:book_loan][:person_id])
redirect_to :action => 'book', :id => params[:id]
end
Book Show View w/ Loan Form
<p>
<b>Title:</b>
<%=h #book.title %>
</p>
<p>
<b>Isbn10:</b>
<%=h #book.isbn10 %>
</p>
<p>
Currently loaned to:
<%=h borrower_name(#book) %>
</p>
<% form_for(#book) do |x| %>
<p>
<%= x.label :loan_person_id %><br/>
<%= collection_select(:book_loan, :person_id,
Person.find(:all, :order => 'name ASC'), :id, :name) %>
<%= x.submit 'Loan', :action => 'loan' %>
</p>
<% end %>
BookLoan Model
class BookLoan < ActiveRecord::Base
belongs_to :book
belongs_to :person
end
Person Model
class Person < ActiveRecord::Base
has_many :book_loans
has_many :books, :through => :book_loans
end
Development Log
Processing BooksController#update (for 127.0.0.1 at 2009-09-24 13:43:05) [PUT]
Parameters: {"commit"=>"Loan", "authenticity_token"=>"XskHLuco7Q7aoEnDfVIiYwVrMEh5uwidvJZdrMbYYWs=", "id"=>"1", "book_loan"=>{"person_id"=>"3"}}
[4;35;1mBook Columns (3.0ms)[0m [0mSHOW FIELDS FROM `books`[0m
[4;36;1mBook Load (4.0ms)[0m [0;1mSELECT * FROM `books` WHERE (`books`.`id` = 1) [0m
[4;35;1mSQL (0.0ms)[0m [0mBEGIN[0m
[4;36;1mBook Load (1.0ms)[0m [0;1mSELECT `books`.id FROM `books` WHERE (`books`.`title` = BINARY 'Dreaming in Code: Two Dozen Programmers, Three Years, 4,732 Bugs, and One Quest for Transcendent Software' AND `books`.id <> 1) LIMIT 1[0m
[4;35;1mSQL (1.0ms)[0m [0mCOMMIT[0m
Redirected to http://localhost:3000/books/1
Completed in 19ms (DB: 10) | 302 Found [http://localhost/books/1]
[4;36;1mSQL (0.0ms)[0m [0;1mSET NAMES 'utf8'[0m
[4;35;1mSQL (0.0ms)[0m [0mSET SQL_AUTO_IS_NULL=0[0m
When you has_many a model, you get access to a few extra methods, two in particular are collection.build and collection.create— build is like new, create is like create :). Have a look at the has many documentation. In this case, you could rewrite add_loan as
def add_loan (person_id)
book_loans.create(:book_id => id,
:person_id => person_id,
:out_date => Date.current)
end
or something similar.
To answer your question about what the view will be sending to the controller, it will send the params hash as usual, but if you just want to pass the person_id to add_loan, you can extract that. Assuming that the part of the view you have above is wrapped in a form for a book loan, you can access the person by params[:book_loan][:person_id]. You'll also want to do a find in that action first, or else #book is going to be nil.
Hope this helps. Cheers.
EDIT: I'm not sure if the way you have it right now works, but I think you want to change your Person model to read
class Person < ActiveRecord::Base
has_many :book_loans
has_many :books, :through => :book_loans
end
EDIT 2: Your development log says you aren't actually hitting loan, you're hitting update. A few things you could do: check to make sure you have loan as a listed resource in your routes; merge the loan action into the update action—could start getting kind of messy so I don't know if this is the best approach. I'm sure there are more, but those are the first things that pop to mind. Also, I don't know if you can add :action => 'loan' to a submit tag. I think it will just look at that as if it were an html option. You might want to change your form_for to read
<% form_for(#book), :url => { :action => 'loan' } do |x| %>
once you've made sure that the routes are in order. But as I said earlier, I'm pretty sure you will be thrown an error on that action because you haven't defined a Book.find for it.