0

Hi i'm having a little trouble with a has_many through relationship for my app and was hoping to find some help. So i've got Users & Lectures. Lectures are created by one user but then other users can then "join" the Lectures that have been created. Users have their own profile feed of the Lectures they have created & also have a feed of Lectures friends have created. This question however is not about creating a lecture but rather "Joining" a lecture that has been created already. I've created a "lecturerelationships" model & controller to handle this relationship between Lectures & the Users who have Joined (which i call "actives"). Users also then MUST "Exit" the Lecture (either by clicking "Exit" or navigating to one of the header navigation links). I'm grateful if anyone can work through some of this with me...

I've got: Users.rb model Lectures.rb model Users_controller Lectures_controller

then the following model

lecturerelationship.rb

class lecturerelationship < ActiveRecord::Base
  attr_accessible :active_id, :joinedlecture_id

  belongs_to :active, :class_name => "User"
  belongs_to :joinedlecture, :class_name => "Lecture"

  validates :active_id, :presence => true
  validates :joinedlecture_id, :presence => true

end

lecturerelationships_controller.rb

class LecturerelationshipsController < ApplicationController
  before_filter :signed_in_user

  def create
   @lecture = Lecture.find(params[:lecturerelationship][:joinedlecture_id])
   current_user.join!(@lecture)
   redirect_to @lecture
  end

 def destroy
  @lecture = Lecturerelationship.find(params[:id]).joinedlecture
  current_user.exit!(@user)
  redirect_to @user
 end

end

Lectures that have been created (by friends) show up on a users feed in the following file

_activity_item.html.erb

<li id="<%= activity_item.id %>">
  <%= link_to gravatar_for(activity_item.user, :size => 200), activity_item.user %><br   clear="all">
  <%= render :partial => 'shared/join', :locals => {:activity_item => activity_item} %>
  <span class="title"><%= link_to activity_item.title, lecture_url(activity_item)  %></span><br clear="all">
  <span class="user">
   Joined by <%= link_to activity_item.user.name, activity_item.user %>
  </span><br clear="all">
  <span class="timestamp">
   <%= time_ago_in_words(activity_item.created_at) %> ago.
  </span>
  <% if current_user?(activity_item.user) %>
    <%= link_to "delete", activity_item, :method => :delete,
                                     :confirm => "Are you sure?",
                                     :title => activity_item.content %>
  <% end %>
</li>

Then you see I link to the the 'shared/join' partial above which can be seen in the file below

_join.html.erb

<%= form_for(current_user.lecturerelationships.build(:joinedlecture_id =>  activity_item.id)) do |f| %>
  <div>
   <%= f.hidden_field :joinedlecture_id %>
  </div>
  <%= f.submit "Join", :class => "btn btn-large btn-info" %>
<% end %>

Some more files that might be needed:

config/routes.rb

SampleApp::Application.routes.draw do
  resources :users do
    member do
    get :following, :followers, :joined_lectures
  end
end

resources :sessions, :only => [:new, :create, :destroy]
resources :lectures, :only  => [:create, :destroy, :show]
resources :relationships, :only => [:create, :destroy] #for users following each other
resources :lecturerelationships, :only => [:create, :destroy] #users joining existing lectures

So what happens is the lecture comes in my activity_feed with a Join button option at the bottom...which should create a lecturerelationship of an "active" & "joinedlecture" (which obviously are supposed to be coming from the user & lecture classes. But the error i get when i click the join button is as follows:

ActiveRecord::StatementInvalid in LecturerelationshipsController#create

SQLite3::ConstraintException: constraint failed: INSERT INTO "lecturerelationships"     ("active_id", "created_at", "joinedlecture_id", "updated_at") VALUES (?, ?, ?, ?)

Also i've included my user model (seems the error is referring to it) user.rb

class User < ActiveRecord::Base
  attr_accessible :email, :name, :password, :password_confirmation
  has_secure_password
  has_many :lectures, :dependent => :destroy


  has_many :lecturerelationships, :foreign_key => "active_id", :dependent => :destroy
  has_many :joined_lectures, :through => :lecturerelationships, :source =>    :joinedlecture

  before_save { |user| user.email = email.downcase }
  before_save :create_remember_token

  validates :name, :presence => true, :length => { :maximum => 50 }
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  validates :email, :presence => true,
        :format => { :with => VALID_EMAIL_REGEX },
        :uniqueness => { :case_sensitive => false }
  validates :password, :presence => true, :length => { :minimum => 6 }
  validates :password_confirmation, :presence => true

  def activity
    # This feed is for "My Activity" - basically lectures i've started
    Lecture.where("user_id = ?", id)
  end

  def friendactivity
     Lecture.from_users_followed_by(self)
  end

  # lECTURE TO USER (JOINING) RELATIONSHIPS
  def joined?(selected_lecture)
    lecturerelationships.find_by_joinedlecture_id(selected_lecture.id)
  end

  def join!(selected_lecture)
    lecturerelationships.create!(:joinedlecture_id => selected_lecture.id)
  end

  def exit!(selected_lecture)
   lecturerelationships.find_by_joinedlecture_id(selected_lecture.id).destroy
  end


end

Thanks for any and all help - i'll be on here for a while so as mentioned i'd GREATLY appreciate someone who may have the time to work through my issues with me...

tereško
  • 58,060
  • 25
  • 98
  • 150
BennyB
  • 155
  • 4
  • 19
  • 1
    For one, you might have a much more enjoyable rails experience with shorter variable names. One big issue is that your create action should retrieve data from the Lecture table, not the User table. – cdesrosiers Jun 08 '12 at 22:09
  • Thanks cdesrosiers - that was def something i totally overlooked. I've updated that portion above. but now i get a new error that i'm adding now – BennyB Jun 08 '12 at 22:30
  • correct jordan, i'm trying to create a joinedlecture_id (based of the regular lecture class) that has a lot of "actives_id"'s (Users who have joined the lecture). – BennyB Jun 08 '12 at 22:53

2 Answers2

2

I would start with the following models, using Enrollment for the membership table.
Class naming is critical for this.

class User < ActiveRecord::Base
  has_many :enrollments
  has_many :users, :through => :enrollments
end

class Lecture < ActiveRecord::Base
  has_many enrollments
  has_many users, :through => :enrollments
end

class Enrollment < ActiveRecord::Base
  belongs_to :user
  belongs_to :lecture
end

The main trick to rails is using the right names and following the conventions and letting the framework do most of the work for you. Step off the path and the errors can be endless and the code gets messy quickly :)

Michael Durrant
  • 93,410
  • 97
  • 333
  • 497
  • Thx Michael - but isn't that what i've currently got? only I call it "Lecturerelationships" instead of "Enrollment" – BennyB Jun 08 '12 at 22:43
  • The reason i try to set up "Actives" and "JoinedLectures" as seen in the "Lecturerelationship.rb" file is because i'll need to do a lot with them within a lecture and therefore am trying to separate them from normal "users". I know i have a lot to learn though. – BennyB Jun 08 '12 at 22:48
  • 1
    Start with these relationships and names. Yes the names are important, as cdesrosiers also noted above. Trust us we've learned from hard experience. Once you can use these relationships, do the other parts. Little things like source and foreign_key may contribute to errors or give strange error messages. For instance yuor routes stuff looks like it would just be doable through relationships with a lot fo the code that is there. Strongly recommend you look for a railscast that is close to what you need. They really show the minimal code and best practice way. – Michael Durrant Jun 08 '12 at 22:53
  • I would do qualifers though scopes rather than modified relationships. – Michael Durrant Jun 08 '12 at 22:54
  • Michael - i hunted around for a railscast but didn't find one that was similar. Do you know of any for creating chat rooms in rails? Thats the only thing i can think of that's similar (because these users are all joining a Lecture all at the same time..similar to a chatroom). If you know of any examples that would be huge...thanks man – BennyB Jun 08 '12 at 23:03
  • 1
    Oh, a rails chatroom, sure - http://stackoverflow.com/questions/790905/how-do-i-write-a-rails-chatroom – Michael Durrant Jun 09 '12 at 02:59
1

It looks like your _join.html.erb uses build to form a new lecturerelationships association and then the join! call uses create to make another one on the same object. My guess is that the second call interrupts a transaction started by the first one and that's why you are getting the database is locked: commit transaction error.

I think it is considered good practice to keep this sort of code out of your views.

jordanpg
  • 6,386
  • 4
  • 46
  • 70
  • thanks jordan - i fixed something cdrosiers pointed out and now i'm getting a new error that i tossed in place of the old error message. Any ideas? thx! – BennyB Jun 08 '12 at 22:33
  • 1
    What I said is not strictly true. You can `.build` one relationship and then `.create!` another one, and the second one will be created successfully. I think your code is building one with certain attributes and then createing one with the same attributes (perhaps `joinedlecture_id`?), and this is causing SQLite to throw a constraint exception. I'm not sure why, though, because I thought that `build` does not hit the DB. – jordanpg Jun 08 '12 at 22:53
  • (posted this on the wrong comment)...correct jordan, i'm trying to create a joinedlecture_id (based of the regular lecture class) that has a lot of "actives_id"'s (Users who have joined the lecture). – BennyB Jun 08 '12 at 22:57
  • In any case, it seems clear that whatever is being saved is being rejected by the DB because of some sort of constraint. Put in some debugging output or use `debugger` to see what the lecturerelationships contain before the saves. – jordanpg Jun 08 '12 at 23:11