我在使用 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"}