Question

I was wondering whether anybody felt kind enough to help me figure out why this isn't working.

I have a Model lets call it Task which belongs to a Project Model. I basically want each Task to have a unique name per project (Project1 could have a task called task1 and so could Project2 but both could only have one called task1) . This seems to be what the :scope option is for but it doesn't seem to be working for me.

The task model is a nested resource within project and as such I call the create action via project_tasks_path(@project). It works fine creating tasks and assigning them to projects but the scope of the uniqueness validation is not taking hold. If I create a task task1 in Project1 I can't create one with the same name in task 2.

This is my setup:

Task.rb

class Task < ActiveRecord::Base
  belongs_to :project
  validates :name, presence: true, uniqueness: {scope: :project_id}

tasks_controller.rb

def create
    @project = Project.find_by(id: params[:project_id])
    @task = Task.new(model_params)
    #print task to stdout
    puts "@task"
    ap @task

    respond_to do |format|
      if @task.save
        flash[:notice] = "Successfully created task"
        format.js
      else
        # no flash as form handles errors
        format.js { render action: 'new' }
        format.html { render action: 'new' }
      end
    end
  end

for some reason when I output the contents of the newly created task, I get the following

#<Task:0x007ff7c7c3b178> {
            :id => nil,
          :name => "test",
    :project_id => nil,
    :created_at => nil,
    :updated_at => nil
}

It seems that because project_id hasn't been set at this point it's using 'nil' as the value.

What's the best way to get around this? would it just be a custom validator?

Edit 1

def model_params
  params.require(:model).permit(:name, :project_id)
end
Was it helpful?

Solution

Right, having been playing around with this, it seems that the way to make this type of validation is pretty straight forward. All it requires is that the nested resource be built in relation to it's project, this forces the :parent_id to be passed through to the validation as expected.

In the case of this toy example, that means that the create action has to look something like:

@project = Project.find_by(id: params[:project_id])
@task = @project.tasks.build(model_params)

It should be noted that because of Rails not supporting generation of nested resources from the command line, the way that the scaffold generated controllers handle creation is by Model.new(model_params) and then saving, this doesn't seem to pick up the :parent_id in time for the validation and so will need changing as above (in terms of the parent).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top