Forum: Ruby on Rails Easy Question, I Think

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Ian H. (Guest)
on 2006-01-11 20:27
(Received via mailing list)
I am just getting started with Rails, don't know any Ruby, and don't
quite even get object oriented programming yet.  I have tweaked my
schema to The Rails Way and have generated a bit of scaffolding.

I am trying to create a new Part, which references a PartName and a
PartNumber.  I can create a PartNumber on the fly, but the PartName
has to exist.  I have an inelegant solution that works, but does not
properly exploit the validation because I don't get back the
appropriate errors when a PartName is not existent or a PartNumber
fails to be created.  All I get is that the Part was missing the key
values.

Is there a simpler way to design the :create such that the referenced
records will be created if they don't exist (or fail if they don't
exist in the case of PartName) and return relevant error information
to the user?

I know this is all wrong, but would love a few pointers on the right
way to do it!!

Thank you in advance...

- Ian

-----------------Schema ------------------

CREATE TABLE part_names (
    id serial primary key,
    part_name character varying NOT NULL,
    group_id integer not null,
    description character varying,
    idx_part_name tsvector
);

ALTER TABLE part_names ADD CONSTRAINT pn_group FOREIGN KEY (group_id)
REFERENCES groups(id) DEFERRABLE INITIALLY DEFERRED;

CREATE TABLE part_numbers (
    id serial primary key,
    part_number integer NOT NULL,
    constraint chk_part_number check (part_number ~'[0-9]*')
);

CREATE TABLE parts (
    id serial primary key,
    part_number_id integer NOT NULL,
    part_name_id integer NOT NULL,
    quantity integer DEFAULT 1 NOT NULL,
    side character varying,
    size character varying,
    "comment" character varying,
    constraint chk_side check (side in ('L.H.', 'R.H.') or side is null)
);

ALTER TABLE parts ADD CONSTRAINT parts_pnumber_fkey FOREIGN KEY
(part_number_id) REFERENCES part_numbers(id) DEFERRABLE INITIALLY
DEFERRED;
ALTER TABLE parts ADD CONSTRAINT parts_pname_fkey FOREIGN KEY
(part_name_id) REFERENCES part_names(id) DEFERRABLE INITIALLY
DEFERRED;

---------------------Models--------------------

class Part < ActiveRecord::Base
        belongs_to      :part_name
        belongs_to      :part_number
        has_many        :part_years
        has_many        :part_details
        has_many        :part_comments
        has_many        :car_job_parts
        validates_inclusion_of  :side,
                                :allow_nil => true,
                                :in => %w{ L.H. R.H. }
        validates_inclusion_of  :quantity,
                                :in => 1..99
        validates_presence_of   :part_number_id, :part_name_id
        validates_associated    :part_number, :part_name
        validates_uniqueness_of :part_number_id, :scope =>
"part_name_id"
end

class PartNumber < ActiveRecord::Base
        has_many :parts
        has_many :part_number_images
        has_many :part_number_illustrations
        has_and_belongs_to_many :vendor_parts
        validates_numericality_of :part_number
        validates_inclusion_of :part_number, :in => 111111..99999999
        validates_uniqueness_of :part_number
end

class PartName < ActiveRecord::Base
        belongs_to      :group
        has_and_belongs_to_many :jobs
        has_many :parts
        validates_presence_of :part_name, :group_id
        # validates_associated :groups <-- causes infinite loop!
        validates_associated :parts
        validates_uniqueness_of :part_name
end

---------------------View ---------------------
_form.html

<%= error_messages_for 'part' %>

<!--[form:part]-->


<p><label for="part_name">Part Name</label><br/>
<%= text_field_with_auto_complete :part_name, :part_name %>

<p><label for="part_number">Part Number</label><br/>
<%= text_field_with_auto_complete :part_number, :part_number %>

<p><label for="part_quantity">Quantity</label><br/>
<%= text_field 'part', 'quantity'  %></p>

<p><label for="part_side">Side</label><br/>
<%= text_field 'part', 'side'  %></p>

<p><label for="part_size">Size</label><br/>
<%= text_field 'part', 'size'  %></p>

<p><label for="part_comment">Comment</label><br/>
<%= text_field_with_auto_complete :part, :comment %>
<!--[eoform:part]-->

-------------------Controller---------------------------

class PartsController < ApplicationController

  auto_complete_for :part, :comment
  auto_complete_for :part_number, :part_number
  auto_complete_for :part_name, :part_name

  def index
    list
    render :action => 'list'
  end

  def list
    @part_pages, @parts = paginate :parts, :order => "part_name_id",
:per_page => 10
  end

  def show
    @part = Part.find(params[:id])
  end

  def new
    @part = Part.new
    @part_name = PartName.new
    @part_number = PartNumber.new
  end

  def create
    @part = Part.new(params[:part])
    @part_name = PartName.new(params[:part_name])
    @part_number = PartNumber.new(params[:part_number])

    # I dont want to create a part name because there is more to it than
this
    # but I want creation to fail.  It does, but the field is not
highlighted
    # because the object is not specifically invalidated?

    @part.part_name =
PartName.find_by_part_name(params[:part_name][:part_name])

    # The find portion of this seems to try to find the record,
regardless of whether
    # the input is of the wrong type.  I suppose that makes sense, but
there must
    # be an easier way than explicitly calling .valid?

    if @part_number.valid?
      @part.part_number =
PartNumber.find_or_create_by_part_number(params[:part_number][:part_number])
    end

    if @part.save
      flash[:notice] = 'Part was successfully created.'
      redirect_to :action => 'list'
    else
      render :action => 'new'
    end
  end

  def edit
    @part = Part.find(params[:id])
  end

  def update
    @part = Part.find(params[:id])
    if @part.update_attributes(params[:part])
      flash[:notice] = 'Part was successfully updated.'
      redirect_to :action => 'show', :id => @part
    else
      render :action => 'edit'
    end
  end

  def destroy
    Part.find(params[:id]).destroy
    redirect_to :action => 'list'
  end
end
This topic is locked and can not be replied to.