1

I'm trying to allocate an address a custom id so it's not easy to guess the record (for people who want to add and change other people's addresses). For some reason, whilst I can create a record with the correct path being created, the edit path from form_with seems to be not using the custom resource param.

routes.rb

resources :deladdresses, param: :delid

_form.html.erb

<%= form_with model: deladdress, class: "mt-4" do |f| %>
  <%= render "shared/error_messages", resource: f.object %>

  .. lots removed for clarity ...

  <%= f.hidden_field :country, value: "Australia" %>
  <%= f.hidden_field :ordernum, name: :ordernum, value: @order %>
  <%= link_to order_path(@order.ordernum) do %>
    <button class="btn btn-secondary">Back</button>
  <% end %>
  <%= f.button "Save", id: 'submit', class: "btn btn-primary" %>
<% end %>

Which is coming from in my new.html.erb and edit.html.erb:

<%= render "form", deladdress: @deladdress %>

deladdress.rb

class Deladdress < ApplicationRecord

  before_save :add_del_id

  belongs_to :user
  has_many :orders

  def add_del_id
    randchars = ("a".."z").to_a.sample(8).join
    time = DateTime.now.strftime("%H%M%S")
    self.delid = "#{time}-#{randchars.upcase}"
  end

end

deladdress_controller.rb

class DeladdressesController < ApplicationController
  before_action :find_my_order

  def show
    # Collect the delivery address for the order
    # Do we want to collect and store these per customer?
  end

  def new
    if current_user
      @savedaddresses = Deladdress.where(user: current_user)
    end
    @deladdress = Deladdress.new
  end

  def edit
    @deladdress = Deladdress.where(delid: params[:delid]).first
  end

  def create
    @deladdress = Deladdress.create(deladdress_params)
    @deladdress.user = current_user
    if @deladdress
      @order&.update(deladdress: @deladdress)
      redirect_to order_path(@order.ordernum), notice: "Address added"
    else
      render :new
    end
  end

  def update
    @deladdress = Deladdress.where(delid: params[:delid]).first
    if @deladdress.update(deladdress_params)
      @order.update(deladdress: @deladdress)
      redirect_to order_path(@order.ordernum), notice: t(".updated")
    else
      render :edit
    end
  end

  private

  def deladdress_params
    attributes = [:first_name, :last_name, :address, :apartment, :city, :state, :country, :postcode, :ordernum, :delid]
    params.require(:deladdress).permit(*attributes)
  end

  def find_my_order
    @order = find_order
  end

end

When I go to the following url http://localhost:5000/deladdresses/112750-UBTYOJGK/edit, I can see the delid is there. But, when I look at the form which it is going to try and submit I have the following. The id is 5 and not 112750-UBTYOJGK.

<form class="mt-4" action="/deladdresses/5" accept-charset="UTF-8" data-remote="true" method="post">
  <input type="hidden" name="_method" value="patch">
  <input type="hidden" name="authenticity_token" value="XXXXXXXXXXXX">
  <button name="button" type="submit" id="submit" class="btn btn-primary">Save</button>
</form>

I am obviously missing something but I really don't know what.

Thanks in advance for any help you might be able to give to have this work.

Darren
  • 1,682
  • 1
  • 15
  • 33
  • If you're only purpose is to protect the records (" so it's not easy to guess the record") you should look into an authorization gem like pundit or cancancan. – Clara Sep 30 '20 at 03:10
  • 1
    @Clara That will be my next trick. Steep learning curve with what I'm trying to do and I'm trying to get it working and then make it more complex :) – Darren Sep 30 '20 at 03:28
  • Very good! You cam up with a good idea of obscuring the ids, but technically, a user who can guess the new delid could still submit the form and change another person's address. That's where the powerful gems come into play! Anyway keep up the good work. – Clara Sep 30 '20 at 03:30

1 Answers1

3

You can pass the url for the form to the form helper like so:

  1. for where you call the edit form:
<%= render "form", deladdress: @deladdress, url: deladress_path(@deladress.delid) %>
  1. for where you call the new form:
<%= render "form", deladdress: @deladdress, url: deladresses_path %>

and then in the form itself:

<%= form_with model: deladdress, url: url, class: "mt-4" do |f| %>
...
Clara
  • 2,677
  • 4
  • 16
  • 31
  • Thanks for your answer. I've tried that and it's not working when I am trying to do a new address. It works for edit, but not for new. – Darren Sep 30 '20 at 03:27
  • You see how I added the url part to render form? You have to add this too (with the respective path) to where you render the form for creating a new address :) – Clara Sep 30 '20 at 03:28
  • I believe I have ... I've added it to the new.html.erb and edit.html.erb. The edit will work, but the new isn't. I've added the url to the form and it's still complaining. This is the message when I try and do a new: `No route matches {:action=>"show", :controller=>"deladdresses", :delid=>nil}, missing required keys: [:delid]` – Darren Sep 30 '20 at 03:38
  • Did you change the path for the new? It should be: `<%= render "form", deladdress: @deladdress, url: new_deladress_path %>` – Clara Sep 30 '20 at 03:49
  • Sadly not. The new form is rendering but it's not saving. The form is being set up with: `
    `. When I click Save, I get the following message: `ActionController::RoutingError (No route matches [POST] "/deladdresses/new"):` which seems to suggest it's not directing to the update method.
    – Darren Sep 30 '20 at 04:41
  • 1
    Sorry, that's my bad. Of course it has to be `deliaddresses_path`, not `new_deliaddress_path`. I'll update once more. You can always check the prefix for the path helper with `rails routes` in the temrinal – Clara Sep 30 '20 at 04:44