Rails: использование jquery tokeninput (railscast # 258) для создания новых записей - PullRequest
6 голосов
/ 18 июля 2011

Мне удалось реализовать добавление новых записей об исполнителях, которые не были включены в 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 в форме публикации, и я также добавил опцию«Добавить {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

Я добавил дополнительный код, чтобы проанализировать «новые» записи по идентификатору, а затем создать нового исполнителя с ними.Затем 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

Все отлично работает, кроме возможности сузить результаты json только записями current_user.Как бы я поступил так?Нужно ли хранить 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 db), ссылающиеся на: 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

Ответы [ 2 ]

3 голосов
/ 24 июля 2011

Если вы не хотите что-либо изменять в своих моделях, то вы можете сделать это следующим образом:

def index
  @artists = current_user.posts.join("artisianships").join("artists").
               where("artisianships.post_id = posts.id").
               where("artists.name like ?", "#{params[:q]}").
               select("artists.name as name, artists.id as id")
  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

Обратите внимание, что здесь происходит много объединений, поэтому не рекомендуется.

Для отладки, почему Artist.create!(:name => $1, :user_id => self.user_id) не будет работать, было бы здорово, если бы мы увидели еще немного кода, в частности action для создания Post

ОБНОВЛЕНИЕ: Вы пытались изменитьPostController#create к следующему, хотя я чувствую, что текущий код должен работать, как есть,

@post = current_user.posts.build
if @post.update_attributes(params[:post])
   # do something
else
   # do something else
end

Я не специалист по рельсам и не понимаю alias_method_chain, поэтому не могу комментировать, почему он не сделалне работает

2 голосов
/ 23 июля 2011

если вы используете какое-либо решение для аутентификации, такое как devise, clearance или authlogic, у вас, вероятно, уже есть метод current_user. Поэтому добавьте столбец user_id: integer в модель Artist и сделайте следующее:

#User model
has_many :artists

#Artist model
belongs_to :user

#Artists controller
def index
    @artists = current_user.artists.where("name like ?", "%#{params[:q]}%") #this answers your question!
    ... blah blah
end

И, конечно, не забудьте установить столбец user_id при создании исполнителя.

...