3
votes

QUESTION - is there a way to pass/use params hash data to the associated model?

Essentials of my app:

  1. Image model belongs_to User model, User model has_many Image instances.
  2. Image schema:

    create_table "images", force: :cascade do |t| t.string "filename" t.string "mime_type" t.binary "content" t.integer "user_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false end

    add_index "images", ["user_id"], name: "index_images_on_user_id"

  3. In root_url (which is StaticPagesController#home) I have image upload form:

  4. in Image model I do:

    def uploaded_file=(initial_data)
      self.filename = initial_data.original_filename
      self.mime_type = initial_data.content_type
      self.content = initial_data.read
      initial_data.rewind
    end
    
  5. Also a custom validation:

    validate :mime_type
    

with

def mime_type
    correct_mime = ["image/jpeg", "image/png", "image/gif"]
    unless correct_mime.include? params[:image][:uploaded_file].content_type.chomp
        errors.add(:base, 'must be .jpeg, .png or .gif')
    end
end
  1. As it is known all query from upload form goes to params[:image][:uploaded_file]

6. Is it possible to pass params[:image][:uploaded_file] to Image model as a hash with original hash structure?

What I have tried and none of them works:

  • pass explicit params hash right in Image model - NO GO
  • define instance @params_hash = params[:image][:uploaded_file] in create action (separately or from inside the custom class method) - NO GO
  • Define constant in controller#action - NO GO

WHAT works?? Global variable - $variable

Is it a rails way to do that? -> query data to model

3
Why can't you use mime_type instead of params[:image][:uploaded_file].content_type? Unless I'm missing something obvious?j-dexx
The minor problem (this case) is how to extract uploaded file content_type from params hash in model, the major problem is - how to use correctly params hash in appropriate model for future apps.Andrius
@japed - could you please present it as an answer with what exactly you are suggesting. Thanks!Andrius

3 Answers

4
votes

In a good MVC app your models should not be aware of the parameters, the request or any direct user input. The model just takes data from the controller and enforces the business logic and persists the data.

class Image < ActiveRecord::Base
  belongs_to :user
  validates_inclusion_of :mime_type,
      in: ["image/jpeg", "image/png", "image/gif"],
      message: 'must be .jpeg, .png or .gif'
end

class ImagesController < ApplicationController

  def new
    @image = Image.new
  end

  def create
    @image = Image.new(
      filename: image_params.original_filename
      mime_type: image_params.content_type,
      content: image_params.read
    )
    if @image.save
      redirect_to root_path
    else
      render :new
    end
  end

  private
    def image_params
      params.require(:image).require(:uploaded_image)
    end
end
2
votes

The model code seems to be correct, it's just the way you're attempting to get the image data to the model that is against convention. Usually you would create a new Image instance in your controller and then set the image data, like so:

image = user.images.new # If "user" is set to the correct user for the image
image.uploaded_file = params[:image][:uploaded_image]
# ...whatever else you need to do with the image here
image.save

That's a barebones controller action, so you're going to want to make sure the Image validates and handle validation errors and whatnot, but hopefully that will get you started.

1
votes

Given you have

def uploaded_file=(initial_data)
  self.filename = initial_data.original_filename
  self.mime_type = initial_data.content_type
  self.content = initial_data.read
  initial_data.rewind
end

So you're setting the mime_type to the content_type of the file.

Why can't you do

def mime_type
  correct_mime = ["image/jpeg", "image/png", "image/gif"]
  unless correct_mime.include? mime_type.chomp
    errors.add(:base, 'must be .jpeg, .png or .gif')
  end
end