Rails 3: Problem understanding the MVC of this request
If you're familiar with railscasts I followed #258 token field to initially set this up, where ryan bates books has_many authors through authorships, I'm using posts has_many artists through artisanships. (and posts belong_to users) http://asciicasts.com/episodes/258-token-fields
So in other words:
I have user's creating posts with the fields title and artist, the post form contains a virtual attribute for the artist field called artist_tokens, this virtual attribute has an associated setter method called artist_tokens=(ids).
This is the method that is giving me problems understanding. The method does 2 things. It takes the ids entered into the post form and calls Artist.create! using the ids as the :name. Then it calls .id on whatever artist object is left. That is the resulting artist_id which is used to create the artisanship association.
1) What is happening when I call Artist.create(:name => $1) from inside the post model?
2) Does that request then go to the create method inside the artist controller?
I understand a normal post request would go to the postscontroller#create method and then write to the database and then redirect to whatever path is in the in the create method such as post_path, so it hits the postscontroller#show and then to the view. Correct?
3) What is the sequen开发者_运维百科ce of request from controller to model to view that I'm doing when I call Artist.create! from the artist_tokens=(ids) method inside the post model?
# app/models/user.rb
class User < ActiveRecord::Base
has_many :posts, :dependent => :destroy
has_many :artists, :through => :posts
attr_accessible :email, :password, :password_confirmation,
:remember_me, :name, :avatar, :username, :bio
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
mount_uploader :avatar, AvatarUploader
end
# app/models/post.rb
class Post < ActiveRecord::Base
belongs_to :user
has_many :artisanships
has_many :artists, :through => :artisanships
attr_accessible :title, artist_tokens
attr_reader :artist_tokens
def artist_tokens=(ids)
ids.gsub!(/CREATE_(.+?)_END/) do
Artist.create!(:name => $1).id
end
self.artist_ids = ids.split(",")
end
end
# all/models/artist.rb
class Artist < ActiveRecord::Base
has_many :artisanships
has_many :users, :through => :posts
has_many :posts, :through => :artisanships
attr_accessible :name
end
# app/models/artisanship.rb
class Artisanships < ActiveRecord::Base
belongs_to :post
belongs_to :artist
has_one :user, :through => :post
attr_accessible :post_id, :artist_id
end
# app/views/shared/_post_form.html.erb
<%= form_for @post, :validate => true, :html => {:multipart => true} do |f| %>
<%= render 'shared/error_messages', :object => f.object %>
<div class="field">
<%= f.label :title, 'Title:' %><br />
<%= f.text_field :title %><br />
<%= f.label :artist_tokens, "Artists" %><br />
<%= f.text_field :artist_tokens, "data-pre" =>
@post.artists.map(&:attributes).to_json %> # html 5 data attribute for prepopulate
</div>
<div class="actions">
<%= f.submit "Submit" %>
</div>
<% end %>
# app/controllers/pages_controller.rb
class PagesController < ApplicationController
def home
@title = "Home"
@featured_posts = Post.featured.limit(10)
if user_signed_in?
@user = current_user
@post = current_user.posts.build
@feed_items = current_user.feed.paginate(:per_page => "10", :page => params[:page])
else
#render :layout => 'special_layout'
end
end
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
before_filter :authenticate_user!, :only => [:create, :edit, :update, :destroy]
before_filter :authorized_user, :only => [:destroy, :edit, :update]
def create
@user = current_user
@post = current_user.posts.build(params[:post])
if @post.save
flash[:success] = "Post created!"
redirect_to root_path
else
@feed_items = current_user.feed.paginate(:per_page => "10", :page => params[:page])
render 'pages/home'
end
end
def index
@posts = Post.paginate(:page => params[:page])
end
def show
@post = Post.find(params[:id])
end
def edit
@post = Post.find(params[:id])
end
def update
@post = Post.find(params[:id])
respond_to do |format|
if @post.update_attributes(params[:post])
format.html { redirect_to(post_path(@post), :notice => 'Post was successfully updated.') }
else
format.html { render :action => "edit" }
end
end
end
def destroy
@post.destroy
redirect_to root_path
end
def likers
@title = "Likers"
@post = Post.find(params[:id])
@likers = @post.likers.paginate(:page => params[:page])
render 'show_likers'
end
def search
if params[:q]
query = params[:q]
@search = Post.search do
keywords query
end
@posts = @search.results
end
end
private
def authorized_user
@post = Post.find(params[:id])
redirect_to root_path unless current_user?(@post.user)
end
end
# app/controller/artists_controller.rb
class ArtistsController < ApplicationController
def index
@artists = Artist.where("name like ?", "%#{params[:q]}%")
results = @artists.map(&:attributes)
results << {:name => "Add: #{params[:q]}", :id => "CREATE_#{params[:q]}_END"}
respond_to do |format|
format.html
format.json { render :json => results }
end
end
def show
@artist = Artist.find(params[:id])
end
def new
@artist = Artist.new
end
def create
@artist = Artist.build(params[:artist])
if @artist.save
redirect_to @artist, :notice => "Successfully created artist."
else
render :action => 'new'
end
end
def edit
@artist = Artist.find(params[:id])
end
def update
@artist = Artist.find(params[:id])
if @artist.update_attributes(params[:artist])
redirect_to @artist, :notice => "Successfully updated artist."
else
render :action => 'edit'
end
end
def destroy
@artist = Artist.find(params[:id])
@artist.destroy
redirect_to authors_url, :notice => "Successfully destroyed artist."
end
end
1) Artist is a class that inherits from ActiveRecord. When you call the class method create, what it does is accept the various fields you have setup in the database, and attempts to save the record. It returns the resulting object. Note that if you have validations in your model, and the object does not pass them, the create will not save the record.
2) No, the controller is not touched at all. The controller is used for a place to specify how you want the application to react to a url request. The artist#create responds to a POST request to the url /artists. In the create action, you simple specify what you want to do when that request comes in. In this case, you wish to save the posted data as an artist into the database.
3) Kind of answered this in #2, but here's how it works. When a url hits the server, Rails checks your routes and figures out where its supposed to go. It then calls the code inside your controller. If any models are invoked, it will execute the code there. Provided you do not do a redirect or something, it will then execute the appropriate view, having available all instance variables declared inside the action.
I hope this clears it up for you. :)
精彩评论