开发者

Nested Resource is loaded out of order

开发者 https://www.devze.com 2023-04-13 03:54 出处:网络
I have a tutorial class and a step class. A tutorial has many steps and each step belongs to a tutorial. I have this setup in my models and in the routes file. On the show action of the tutorial class

I have a tutorial class and a step class. A tutorial has many steps and each step belongs to a tutorial. I have this setup in my models and in the routes file. On the show action of the tutorial class, all steps belonging to that tutorial are loaded as well. The problem is after creating several steps, 4-6, they will get out of order. Example, the first step loaded is step 7 but after that the steps are in order. I'm using postgresql for the database and I include t开发者_Python百科he pg gem in my gemfile. Tutorial model:

class Tutorial < ActiveRecord::Base
  attr_accessible :name, :summary, :permalink   
  has_many :steps

  validates :name, :presence => true,
               :length => { :maximum => 50 },
               :uniqueness => { :case_sensitive => false }  

  validates :summary, :presence => true,
          :length => { :maximum => 2000 }

  before_create :set_up_permalink

  def to_param
permalink
  end

  private

  def set_up_permalink
    permalink = self.name.gsub(' ', '-').gsub(/[^a-zA-Z0-9\_\-\.]/, '')
  self.permalink = permalink
end

Step model:

class Step < ActiveRecord::Base
  attr_accessible :tutorial_id, :body, :position
  belongs_to :tutorial

  validates :body, :presence => true

  before_create :assign_position

  private

  def assign_position
@tutorial = self.tutorial
@position = @tutorial.steps.size
    @position = @position + 1
    self.position = @position
  end
end

Routes:

resources :tutorials do
  resources :steps
end

def show
  @tutorial = Tutorial.find_by_permalink(params[:id])
  @steps = @tutorial.steps
  @next = @steps[0]
  @title = "#{@tutorial.name} - A Ruby on Rails tutorial"
  respond_to do |format|
    format.html # show.html.erb
    format.xml  { render :xml => @tutorial }
  end
end

Tutorial show view

<%= render :partial => @tutorial.steps %>


Where are you setting the order? You could do it like this:

@steps = @tutorial.steps.order('position')

Or even better, since I can't think of any case where you would want the steps out of order:

In your Step model:

default_scope order('position')

Alternatively, you can define the order on the association definition:

In your Tutorial model:

has_many :steps, :order => 'position'

edit and just for the hell of it, here's a much more concise way to write your assign_position method:

  def assign_position
    self.position = tutorial.steps.size + 1
  end


Your logic for assigning position is flawed if steps can be arbitrarily added and removed.

E.g.

  • I create a tutorial and 3 steps, with positions [1,2,3].
  • Then I remove the step with position 2.
  • Now there are 2 steps left with positions [1,3].
  • Later I create another step, which is assigned position 3.
  • Now there are three steps, but their positions are [1,3,3]. That's not right!

There are a couple approaches I would consider to rectify the situation:

First way

You can keep your code for assigning position to a new step, but when a step is removed from a tutorial then updated the positions to make sure that they are sequential. Here's a sketch of how that would work.

class Tutorial
  def rectify_step_positions
    # reassign positions so that all the steps form a sequence
    # E.g. step positions [1,3,4] become [1,2,3]
  end
end

class Step
  after_destroy :trigger_rectify_positions

  def trigger_rectify_positions
    tutorial.rectify_step_positions
  end
end

Second way

When you create a new step you can assign its position as 1 greater than the maximum position among all the existing steps.

def assign_position
  self.position = tutorial.steps.order("position DESC").first.position + 1
end

This method is a little bit simpler to implement than the first. Of course now the positions don't have as much meaning since they are not necessarily sequential. The step with position 10 might be the 2nd step of the tutorial.

Conclusion

Once you have fixed your position problem then you can simply order the steps. Bricker has shown several ways to do that. Personally I like has_many :steps, :order => 'position'

0

精彩评论

暂无评论...
验证码 换一张
取 消