Relacje, strony, podstrony i wywołanie akcji

Witajcie.
Mam takie modele:

region.rb
class Region < ActiveRecord::Base    
  has_many :elements, dependent: :destroy
end

element.rb
class Element < ActiveRecord::Base
  belongs_to :region
end

w route.rb zadeklarowałem:

Rails.application.routes.draw do
  
  resources :regions do
    resources :elements
  end

  resources :elements
end

dzięki temu mam takie ścieżki:


    region_elements GET    /regions/:region_id/elements(.:format)          elements#index
                    POST   /regions/:region_id/elements(.:format)          elements#create
 new_region_element GET    /regions/:region_id/elements/new(.:format)      elements#new
edit_region_element GET    /regions/:region_id/elements/:id/edit(.:format) elements#edit
     region_element GET    /regions/:region_id/elements/:id(.:format)      elements#show
                    PATCH  /regions/:region_id/elements/:id(.:format)      elements#update
                    PUT    /regions/:region_id/elements/:id(.:format)      elements#update
                    DELETE /regions/:region_id/elements/:id(.:format)      elements#destroy
            regions GET    /regions(.:format)                              regions#index
                    POST   /regions(.:format)                              regions#create
         new_region GET    /regions/new(.:format)                          regions#new
        edit_region GET    /regions/:id/edit(.:format)                     regions#edit
             region GET    /regions/:id(.:format)                          regions#show
                    PATCH  /regions/:id(.:format)                          regions#update
                    PUT    /regions/:id(.:format)                          regions#update
                    DELETE /regions/:id(.:format)                          regions#destroy
           elements GET    /elements(.:format)                             elements#index
                    POST   /elements(.:format)                             elements#create
        new_element GET    /elements/new(.:format)                         elements#new
       edit_element GET    /elements/:id/edit(.:format)                    elements#edit
            element GET    /elements/:id(.:format)                         elements#show
                    PATCH  /elements/:id(.:format)                         elements#update
                    PUT    /elements/:id(.:format)                         elements#update
                    DELETE /elements/:id(.:format)                         elements#destroy

regions_controller.rb:

class RegionsController < ApplicationController
  before_action :set_region, only: [:show, :edit, :update, :destroy]

  # GET /regions
  # GET /regions.json
  def index
    @regions = Region.all.order(:nazwa)
  end

  # GET /regions/1
  # GET /regions/1.json
  def show
    @region = Region.find(params[:id])
    @elements = @region.elements.order(:nazwa)
  end

  # GET /regions/new
  def new
    @region = Region.new
  end

  # GET /regions/1/edit
  def edit
  end

  # POST /regions
  # POST /regions.json
  def create
    @region = Region.new(region_params)
    title = @region.nazwa

    respond_to do |format|
      if @region.save
        format.html { redirect_to @region, notice: "\"#{title}\" was successfully created." }
        format.json { render :show, status: :created, location: @region }
      else
        format.html { render :new }
        format.json { render json: @region.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /regions/1
  # PATCH/PUT /regions/1.json
  def update
    respond_to do |format|
      if @region.update(region_params)
        format.html { redirect_to @region, notice: "\"#{title}\" was successfully updated." }
        format.json { render :show, status: :ok, location: @region }
      else
        format.html { render :edit }
        format.json { render json: @region.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /regions/1
  # DELETE /regions/1.json
  def destroy
    if @region.destroy
      flash[:notice] = "Region was successfully destroyed."
      redirect_to regions_url
    else 
      flash[:error] = 'There was an error destroyed this position'
      render :show
    end      
  end
  #def destroy
  #  @region.destroy
  #  respond_to do |format|
  #    format.html { redirect_to regions_url, notice: 'Region was successfully destroyed.' }
  #    format.json { head :no_content }
  #  end
  #end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_region
      @region = Region.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def region_params
      params.require(:region).permit(:nazwa, :skrot, :img_carousel, :comment_carousel, :img_index, :img_female, :img_male, :pokazuj)
    end
end

elements_controller.rb:

class ElementsController < ApplicationController
  before_action :set_element, only: [:show, :edit, :update, :destroy]

  # GET /elements
  # GET /elements.json
  def index
    @elements = Element.all.order(:nazwa)
  end

  # GET /elements/1
  # GET /elements/1.json
  def show
    @element = Element.find(params[:id])
  end

  # GET /elements/new
  def new
    @element = Element.new
  end

  # GET /elements/1/edit
  def edit
  end

  # POST /elements
  # POST /elements.json
  def create
    @element = Element.new(element_params)
    title = @element.nazwa

    respond_to do |format|
      if @element.save
        format.html { redirect_to @element, notice: "\"#{title}\" was successfully created." }
        format.json { render :show, status: :created, location: @element }
      else
        format.html { render :new }
        format.json { render json: @element.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /elements/1
  # PATCH/PUT /elements/1.json
  def update
    title = @element.nazwa

    respond_to do |format|
      if @element.update(element_params)
        format.html { redirect_to @element, notice: "\"#{title}\" was successfully updated." }
        format.json { render :show, status: :ok, location: @element }
      else
        format.html { render :edit }
        format.json { render json: @element.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /elements/1
  # DELETE /elements/1.json
  def destroy
    title = @element.nazwa

    if @element.destroy
      flash[:notice] = "\"#{title}\" was successfully destroyed."
      redirect_to elements_url
    else 
      flash[:error] = 'There was an error destroyed this position'
      render :show
    end      
  end



  private
    # Use callbacks to share common setup or constraints between actions.
    def set_element
      @element = Element.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def element_params
      params.require(:element).permit(:nazwa, :region_id, :opis, :dla, :img_index, :pokazuj)
    end
end

czyli typowo.
/app/view/elements/_form.html.erb

    <%= form_for [@region, @element], :html => { :class => "form-horizontal form-inline element" } do |f| %>
......
......

  <%= f.submit nil, :class => 'btn btn-primary' %>
  <%= link_to t('.cancel', :default => t("helpers.links.cancel")),
            elements_path, :class => 'btn btn-default' %>
end

I teraz mam
/app/view/elements/_sub_index.html.erb

  <table>
    <thead>
      <tr>
        <th>#</th>
        <th>Nazwa elementu</th>
      </tr>
    </thead>
    <tbody>
      <% @elements.each do |element| %>
        <tr>
          <td><%= element.id %></td>
          <td><%= element.nazwa %></td>
        </tr>
      <% end %>
    </tbody>
  </table>

  **<%if @region.nil? %>
    <%= link_to 'New', new_element_path %> 
  <% else %>
    <%= link_to 'New', new_region_element_path(@region) %> 
  <% end %>**

i jest wywołuję jako:

<%= render partial: "sub_index" %>

w /app/view/elements/index.html.erb oraz

<%= render partial: "elements/sub_index" %>

w /app/view/regions/show.html.erb czyli
Elementy są listowane dla wywołania

http://127.0.0.1:3000/elements

ale także jako elementy związane z konkretnym rodzice dla akcji “Show” jakiegoś Regionu np.:

http://127.0.0.1:3000/region/6

Mam teraz pytania:

  1. Czy słusznie, że chcę używać jednego formularza “_Form” do edycji elementu dla akcji Edit i New zarówno dla wywołania:

    http://127.0.0.1:3000/elements/new
    http://127.0.0.1:3000/elements/21/edit

jak i dla:

http://127.0.0.1:3000/region/6/elements/new
http://127.0.0.1:3000/region/6/elements/21/edit ?

a jeżeli tak, to
2) Czy właściwie zrobiłem wywołanie:

  <%if @region.nil? %>
    <%= link_to 'New', new_element_path %> 
  <% else %>
    <%= link_to 'New', new_region_element_path(@region) %> 
  <% end %>

w tym współdzielonym app/view/elements/_sub_index.html.erb?
Czy jest to jednak kategoria radosnego rękodzieła, a nie pisania w RoR. :frowning:

A może bardziej elegancko i zgodnie z duchem Railsów byłoby w elements_controller.erb

wstawić to
if @region.nil?
i wywoływać jednak różne layouts?
Jeden dla wywołania z bezpośredniej listy
/elements a inny dla
/region/6/elements ?

Idąc tropem if @region.nil? przebudowałem controller

class ElementsController < ApplicationController
  before_action :set_element, only: [:show, :edit, :update, :destroy]

  # GET /elements
  # GET /elements.json
  def index
    @elements = Element.all.order(:nazwa)
    #@elements = Element.joins(:region).order("regions.skrot, elements.nazwa")
  end

  # GET /elements/1
  # GET /elements/1.json
  def show
    @element = Element.find(params[:id])
  end

  # GET /elements/new
  def new
    @region = Region.find(params[:region_id]) if !params[:region_id].nil?
    @element = Element.new
    @element.region = @region if !@region.nil?
  end

  # GET /elements/1/edit
  def edit
  end

  # POST /elements
  # POST /elements.json
  def create
    @region = Region.find(params[:region_id]) if !params[:region_id].nil?
    @element = Element.new(element_params)
    title = @element.nazwa

    respond_to do |format|
      if @element.save
        if @region.nil?
          format.html { redirect_to element_path(@element), notice: "\"#{title}\" was successfully created." }
          format.json { render :show, status: :created, location: @element }
        else
          format.html { redirect_to region_element_path(@region, @element), notice: "\"#{title}\" was successfully created." }
          format.json { render :show, status: :created, location: @region }
        end
      else
        format.html { render :new }
        format.json { render json: @element.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /elements/1
  # PATCH/PUT /elements/1.json
  def update
    @region = Region.find(params[:region_id]) if !params[:region_id].nil?
    title = @element.nazwa

    respond_to do |format|
      if @element.update(element_params)
        if @region.nil?
          format.html { redirect_to element_path(@element), notice: "\"#{title}\" was successfully updated." }
          format.json { render :show, status: :ok, location: @element }
        else
          format.html { redirect_to region_element_path(@region, @element), notice: "\"#{title}\" was successfully updated." }
          format.json { render :show, status: :ok, location: @region }
        end
      else
        format.html { render :edit }
        format.json { render json: @element.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /elements/1
  # DELETE /elements/1.json
  def destroy
    @region = Region.find(params[:region_id]) if !params[:region_id].nil?
    title = @element.nazwa

    if @element.destroy
      flash[:notice] = "\"#{title}\" was successfully destroyed."
      if @region.nil?
        redirect_to elements_url
      else
        redirect_to region_path(@region)
      end
    else 
      flash[:error] = 'There was an error destroyed this position'
      render :show
    end      
  end



  private
    # Use callbacks to share common setup or constraints between actions.
    def set_element
      @element = Element.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def element_params
      params.require(:element).permit(:nazwa, :region_id, :opis, :dla, :img_index, :pokazuj)
    end
end

I nawet nieźle to działa! :smile:
Gdy wywołuję “New” dla Element jako

`http://127.0.0.1:3000/elements/new` 

to po zapisaniu wraca na listę

http://127.0.0.1:3000/elements

a gdy wywoałam

http://127.0.0.1:3000/region/6/elements/new

to elegancko wraca do regionu (i pokazuje listę elementów związanych z tym konkretnym regionem ) czyli

http://127.0.0.1:3000/region/6

Efekt jest właściwy, jednak nadal nie wiem, czy to jest chałturzenie i sięganie prawą ręką do lewej kieszeni, czy też prawidłowo to programuję.
A może powinienem zmienić:

route.rb
  resources :regions do
    resources :elements, controller: 'reg_elements'
  end

  resources :elements

czy jeden kontroler dla akcji wywoływanych bezpośrednio z listy elementów i drugi, dodatkowy dla operacji na elementach wykonywanych “spod” rodzica-Regionu?

Podpowie mi ktoś, jak to robią profesjonaliści? :slight_smile:

Proszę, niech ktoś mi powie, którymi ścieżkami podążać, bo nie chcę brnąć dalej nie wiedząc, czy robię dobrze, czy źle.