Rails:使用 jquery tokeninput (railscast #258) 创建新条目
我已经成功地添加了未包含在 Ryan Bates Railscast #258 http://railscasts.com/episodes/258-token-fields
换句话说,用户可以输入艺术家姓名,该姓名将使用 jquery tokinput 自动完成。但是,我希望自动完成结果仅显示由该单个用户创建的艺术家姓名。
这有道理吗?一个更好、更容易理解的例子是 collection.rb,用户在其中创建帖子并指定帖子所属的“集合”,但他们应该只能将帖子添加到他们自己创建的集合中。
这是以 Artist_tokens 作为虚拟属性的帖子表单:
<%= 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 %>
</div>
<div class="actions">
<%= f.submit "Submit" %>
</div>
<% end %>
这通过在帖子表单上的artist_tokens 字段中输入的值来查找艺术家,我还添加了选项“Add {params[:q]}”来添加新条目。
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
我添加了额外的代码来通过 id 解析“新”条目,然后用它们创建一个新的艺术家。然后再次分配artist_ids。
post.rb
def artist_tokens=(ids)
ids.gsub!(/CREATE_(.+?)_END/) do
Artist.create!(:name => $1).id
end
self.artist_ids = ids.split(",")
end
除了能够仅通过 current_user 的条目缩小 json 结果之外,一切都运行良好。我该怎么做呢?我需要在表中存储条目创建者的 user_id 吗?我该怎么做?
编辑:模型关联
# app/models/user.rb
class User < ActiveRecord::Base
has_many :posts
has_many :artists, :through => :posts
end
# app/models/post.rb
class Post < ActiveRecord::Base
belongs_to :user
has_many :artisanships
has_many :artists, :through => :artisanships
end
# all/models/artist.rb
class Artist < ActiveRecord::Base
has_many :artisanships
has_many :users, :through => :artisanships
has_many :posts, :through => :artisanships
end
# app/models/artisanship.rb
class Artisanships < ActiveRecord::Base
belongs_to :post
belongs_to :artist
has_one :user, :through => :post
end
编辑: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
编辑:尝试使用alias_method_chain 首先设置帖子的user_id 属性。 (无法修复 NULL 数据库条目) 引用自:Rails 3:alias_method_chain 仍在使用吗?
def attributes_with_user_id_first=(attributes = {})
# Make sure not to accidentally blank out the important_attribute when none is passed in
if attributes.include?(:user_id)
self.user_id = attributes.delete(:user_id)
end
self.attributes_without_user_id_first = attributes
end
alias_method_chain :attributes=, :user_id_first
I have successfully been able to implement the addition of new artist entries which was not included in Ryan Bates railscast #258 http://railscasts.com/episodes/258-token-fields
So in other words, a user can enter an artist name which will autocomplete using jquery tokinput. However, I'd like the autocomplete results to only display the artist names which were created by that individual user.
Does this make sense? An better and more understandable example would be for a collection.rb, where users create posts and specify a 'collection' for the post to belong to, but they should only be able to add posts to the collections which they created themselves.
This is the post form with artist_tokens as a virtual attribute:
<%= 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 %>
</div>
<div class="actions">
<%= f.submit "Submit" %>
</div>
<% end %>
This finds the artist by the value entered into the artist_tokens field on the post form, and I also added the option "Add {params[:q]}" to add new entries.
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
I added the additional code to parse the 'new' entries by id and then create a new artist with them. Then the artist_ids are assigned again.
post.rb
def artist_tokens=(ids)
ids.gsub!(/CREATE_(.+?)_END/) do
Artist.create!(:name => $1).id
end
self.artist_ids = ids.split(",")
end
Everything works great except the ability to narrow the json results by only the current_user's entries. How would I go about doing this? Do I need to store the user_id of the entries creator in the table? How can I do this?
EDIT: associations for models
# app/models/user.rb
class User < ActiveRecord::Base
has_many :posts
has_many :artists, :through => :posts
end
# app/models/post.rb
class Post < ActiveRecord::Base
belongs_to :user
has_many :artisanships
has_many :artists, :through => :artisanships
end
# all/models/artist.rb
class Artist < ActiveRecord::Base
has_many :artisanships
has_many :users, :through => :artisanships
has_many :posts, :through => :artisanships
end
# app/models/artisanship.rb
class Artisanships < ActiveRecord::Base
belongs_to :post
belongs_to :artist
has_one :user, :through => :post
end
EDIT: 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
Edit: Attempted alias_method_chain to set post's user_id attribute first. (didn't work to fix the NULL db entries)
referneced from: Rails 3: alias_method_chain still used?
def attributes_with_user_id_first=(attributes = {})
# Make sure not to accidentally blank out the important_attribute when none is passed in
if attributes.include?(:user_id)
self.user_id = attributes.delete(:user_id)
end
self.attributes_without_user_id_first = attributes
end
alias_method_chain :attributes=, :user_id_first
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
如果您不想修改模型中的任何内容,那么您可以这样做:
请注意,这里有很多连接,因此不推荐。
为了调试为什么
Artist.create!(:name => $1, :user_id => self.user_id)
不起作用,如果我们能看到更多代码,特别是 < code>action 创建一个Post
更新:您是否尝试将
PostController#create
更改为以下内容,尽管我觉得当前的代码应该按原样工作,但我是不是 Rails 专业人士并且不理解
alias_method_chain
因此无法评论它为什么不起作用。If you don't want to modify anything in your models, then you can do it like this:
Notice that there are a lot of joins going on here, so not recommended.
To debug why
Artist.create!(:name => $1, :user_id => self.user_id)
would not work, it would be great if we can see some more code, particularly theaction
to create aPost
UPDATE: Did you try changing
PostController#create
to following, although I feel curent code should work as it is,I am not a rails pro and don't understand the
alias_method_chain
so can't comment on why it didn't work.如果您正在使用一些身份验证解决方案,例如 devise、clearance 或 authlogic,您可能已经有了 current_user 方法。
因此,向 Artist 模型添加一列 user_id:integer 并执行以下操作:
当然,不要忘记在创建艺术家时设置 user_id 列。
if you're using some authentication solution like devise, clearance or authlogic, you probably already have current_user method.
Therefore add a column user_id:integer to the Artist model and do the following:
And of course do not forget to set the user_id column when you create an artist.