0

我在使用 Shrine 的 Rails 6 上,我试图让用户上传多张照片。我想在属性页面上上传多张照片。我的问题是我得到“未经许可的参数:照片”

我正在关注此人的示例并使用本指南进行上传。我在这里做错了什么?

我在用着 - gem "uppy-s3_multipart", "~> 0.3"

错误正在返回

NoMethodError (undefined method `image_data' for # Did you mean? image_attacher):

property_controller.rb

class PropertiesController < ApplicationController
  authorize_resource :except => [:show]
  before_action :set_property, only: [:show, :edit, :update, :destroy]
  before_action :set_user_id

  # GET /properties
  # GET /properties.json
  def index
    if current_user.admin?
      @properties = Property.all
    elsif current_user.provider?
      @properties = Property.where(user_id:(current_user.id))
    end
  end

  # GET /properties/1
  # GET /properties/1.json
  def show
  end

  # GET /properties/new
  def new
    @property = Property.new
  end

  # GET /properties/1/edit
  def edit
  end

  # POST /properties
  # POST /properties.json
  def create
    @property = Property.new(property_params)
    @property.user_id = params[:property][:user_id]

    puts "==========="
    puts @property.image.present?
    puts "==========="

    respond_to do |format|
      if current_user.admin? && @property.save
        format.html { redirect_to admin_dashboard_path, notice: 'Property was successfully created by admin.' }
        format.json { render :show, status: :created, location: @property }
      elsif current_user.provider? && @property.save
        format.html { redirect_to authenticated_root_path, notice: 'Property was successfully created.' }
        format.json { render :show, status: :created, location: @property }
      else
        format.html { render :new }
        format.json { render json: @property.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /properties/1
  # PATCH/PUT /properties/1.json
  def update
    respond_to do |format|
      if current_user.admin? && @property.update(property_params)
        format.html { redirect_to @property, notice: 'Property was successfully updated by admin.' }
        format.json { render :show, status: :ok, location: @property }
      elsif @property.update(property_params)
        format.html { redirect_to @property, notice: 'Property was successfully updated.' }
        format.json { render :show, status: :ok, location: @property }
      else
        format.html { render :edit }
        format.json { render json: @property.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /properties/1
  # DELETE /properties/1.json
  def destroy
    @property.destroy
    respond_to do |format|
      if current_user.admin?
        format.html { redirect_to properties_url, notice: 'Property was successfully destroyed.' }
        format.json { head :no_content }
      elsif current_user.provider?
        format.html { redirect_to authenticated_root_path, notice: 'Property was successfully destroyed.' }
        format.json { head :no_content }
      end
    end
  end

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

    def set_user_id
      # Get user id
      @user_id = params[:user_id]
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def property_params
      params.require(:property).permit(:display, 
      :longitude,
      :latitude,
      :access_24_7,
      :administration_support,
      :air_conditioned,
      :balcony_or_outdoor_breakout_area,
      :boardroom,
      :boardroom_cost,
      :business_lounge,
      :cleaning_included_in_rent,
      :community_rooms,
      :electricity_included_in_rent,
      :floor_or_level,
      :furnished,
      :industry_type,
      :internal_breakout_areas,
      :internet_grade,
      :internet_supplied,
      :kitchen,
      :mail_handling,
      :meeting_room,
      :meeting_room_cost,
      :month_to_month,
      :no_of_floors_occupied,
      :no_of_meeting_rooms,
      :pet_friendly,
      :printing_facilities,
      :reception_services,
      :signage,
      :space_imperial_or_metric_measurement,
      :storage_facilities,
      :telephone_answering,
      :total_floorplan,
      :after_hours_security,
      :bike_racks,
      :building_grade,
      :building_name,
      :concierge_in_foyer,
      :disabled_access,
      :lift,
      :no_of_floors,
      :other_parking_options,
      :parking_cost_per_month,
      :parking_in_building,
      :parking_spaces,
      :retail_or_cafe_in_building,
      :showers,
      :address,
      :city,
      :country,
      :handy_inspection_tips,
      :hide_street_number,
      :state_or_region_or_province_or_territory,
      :street_name,
      :street_number,
      :sub_location,
      :suite_or_unit_or_building,
      :suite_or_unit_or_building_number,
      :zip_or_postal_code,
      :company_name,
      :legal_entity_name,
      :onboarding_notes,
      :parent_account,
      :property_opportunity_stage,
      :property_record_type_name,
      :property_type,
      :lead_referral,
      :tour_consultant,
      :description,
      :workspace_manager,
      :user_id, 
      photos_attributes: {})
    end
end


照片.rb

class Photo < ActiveRecord::Base
  include ImageUploader::Attachment(:image)  # ImageUploader will attach and manage `image`

  validates_presence_of :image
end

属性.rb

class Property < ApplicationRecord
  has_many :listings, dependent: :destroy
  belongs_to :user
  validates :building_name, :street_name, :street_number, :city, :state_or_region_or_province_or_territory, :zip_or_postal_code, :country, :property_type, presence: true
  include ImageUploader::Attachment(:image)
  has_many :photos, dependent: :destroy
  accepts_nested_attributes_for :photos, allow_destroy: true

  STATUS = %w{ on off }
  OPTIONS = %w{ yes no }
  INTERNET_GRADE = %w{ DSL Cable Fibre-Optics }
  SIGNAGE = %w{ Reception Foyer Both }
  BUILDING_GRADE = %w{ A B C N/A }
  OTHER_PARKING_OPTIONS = %w{ Street Public }
end

属性/_form.html.erb

 <div>
  <%= form.label :photos, "Select property photos" %>
  <%= form.file_field :photos,
      multiple: true,
       data: {
         upload_server: upload_server,
        },
      class: "upload-file" %>
 </div>

初始化/shrine.rb

require "shrine"

# By default use S3 for production and local file for other environments
case Rails.configuration.upload_server
when :s3, :s3_multipart
  require "shrine/storage/s3"

  s3_options = Rails.application.credentials.s3

  # both `cache` and `store` storages are needed
  Shrine.storages = {
    cache: Shrine::Storage::S3.new(prefix: "cache", **s3_options),
    store: Shrine::Storage::S3.new(**s3_options),
  }
when :app
  require "shrine/storage/file_system"

  # both `cache` and `store` storages are needed
  Shrine.storages = {
    cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"),
    store: Shrine::Storage::FileSystem.new("public", prefix: "uploads"),
  }
end

Shrine.plugin :activerecord
Shrine.plugin :instrumentation
Shrine.plugin :determine_mime_type, analyzer: :marcel, log_subscriber: nil
Shrine.plugin :cached_attachment_data
Shrine.plugin :restore_cached_data
Shrine.plugin :derivatives          # up front processing
Shrine.plugin :derivation_endpoint, # on-the-fly processing
  secret_key: Rails.application.secret_key_base

case Rails.configuration.upload_server
when :s3
  Shrine.plugin :presign_endpoint, presign_options: -> (request) {
    # Uppy will send the "filename" and "type" query parameters
    filename = request.params["filename"]
    type     = request.params["type"]

    {
      content_disposition:    ContentDisposition.inline(filename), # set download filename
      content_type:           type,                                # set content type
      content_length_range:   0..(10*1024*1024),                   # limit upload size to 10 MB
    }
  }
when :s3_multipart
  Shrine.plugin :uppy_s3_multipart
when :app
  Shrine.plugin :upload_endpoint
end

# delay promoting and deleting files to a background job (`backgrounding` plugin)
Shrine.plugin :backgrounding
Shrine::Attacher.promote_block { Attachment::PromoteJob.perform_later(record, name, file_data) }
Shrine::Attacher.destroy_block { Attachment::DestroyJob.perform_later(data) }

图式

create_table "photos", force: :cascade do |t|
 t.bigint "properties_id"
 t.text "image_data"
 t.index ["properties_id"], name: "index_photos_on_properties_id"
end

日志

   "photos"=>
    [#<ActionDispatch::Http::UploadedFile:0x00007fcd326efc60
      @content_type="image/jpeg",
      @headers="Content-Disposition: form-data; name=\"property[photos][]\"; filename=\"pp.jpg\"\r\n" + "Content-Type: image/jpeg\r\n",
      @original_filename="pp.jpg",
      @tempfile=#<File:/var/folders/m6/0qzd4shd4ydg241cfl7_125m0000gn/T/RackMultipart20200211-7976-1exgkgw.jpg>>,
     #<ActionDispatch::Http::UploadedFile:0x00007fcd326efbe8
      @content_type="image/png",
      @headers="Content-Disposition: form-data; name=\"property[photos][]\"; filename=\"Screen Shot 2019-11-12 at 11.19.18 am.png\"\r\n" + "Content-Type: image/png\r\n",
      @original_filename="Screen Shot 2019-11-12 at 11.19.18 am.png",
      @tempfile=#<File:/var/folders/m6/0qzd4shd4ydg241cfl7_125m0000gn/T/RackMultipart20200211-7976-aecrp9.png>>]},
 "commit"=>"Submit Property Details"}
4

1 回答 1

0

In your case photos is a nested model, so to permit it in strong parameters you should use a hash:

params.require(...).permit(
  ...
  photos_attributes: {
    :id,
    :image, # look into format which comes, may be here will be image: {...}
  }
)

also in your form that will come not as a model, but as an array of files, arrays are permitted via photos: []

于 2020-02-10T10:48:59.530 回答