I'm trying to set up a model (client) that contains some general attributes about a company but also has a company logo attached. I'm reluctant to use a plugin because I want to grasp this aspect of rails if possible.
I've created a clients model and an image model and I can create a new client (scaffold code) and upload an accompanying image ok (using has_one :image, and belongs_to :client).
I used the following code (taken straight from Agile Rails 3rd ed)
class Client < ActiveRecord::Base
has_one :image
def uploaded_image=(image_file)
self.image = Image.new
self.image.name = base_part_of(image_file.original_filename)
self.image.content_type = image_file.content_type
self.image.data = image_file.read
end
def base_part_of(filename)
File.basename(filename).gsub(/^\w_-/,'')
end
end
when editing the client object however, the new file is uploaded but the changes aren't reflected in the db. Do I need to explicitly call update-attributes on #client.image? At the moment my controller update method is as follows:
def update
#client = Client.find(params[:id])
respond_to do |format|
if #client.update_attributes(params[:client])
flash[:notice] = 'Client was successfully updated.'
format.html { redirect_to(#client) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #client.errors, :status => :unprocessable_entity }
end
end
end
Thanks in advance for any tips, apologies for the noob question
I would strongly recommend to use Paperclip instead. It just works.
Related
I've tried to implement many of the proposed solutions in the relevant questions, but haven't yet found an answer ideal for what I'm trying to achieve in my Rails 4 application.
Basically my app has three models. Users, Hooks (embeddable pop-up widgets) and Contacts. Users can create Hooks and Contacts within their interface. And any visitor can create a new contact by filling out the Contact create form placed within a Hook's view, and that contact is associated with the user who created that hook.
That works fine, however when a contact is created by filling out a Hook's form, there's no connection to the specific Hook they completed the form in.
The next set of features I would like to add to my app requires not only associating each contact with a user, but also with the specific Hook it was created from.
I've read a bit into polymorphic associations (model belongs to multiple models) and I understand that's probably the way to go. After a couple of failed attempts, I'm not sure how to implement it though.
How would I associate Contacts with Hooks, so users can know which hook a contact was created from?
Here is what I currently have in the Hooks controller and model...
def create
#hook = hook.new(hook_params)
#hook.user = current_user
#contact = Contact.new(contact_params)
respond_to do |format|
if #hook.save
format.html { redirect_to #hook, notice: 'Hook was successfully created.' }
format.json { render :show, status: :created, location: #hook }
format.js
else
format.html { render :new }
format.json { render json: #hook.errors, status: :unprocessable_entity }
format.js
end
end
end
class Hook < ActiveRecord::Base
belongs_to :user
has_attached_file :image, :styles => { :medium => "300x300>", :thumb => "100x100>" }, :default_url => "https://s3.amazonaws.com/app/assets/leadmagnet.png"
validates_attachment_content_type :image, :content_type => /\Aimage\/.*\Z/
end
And here is the contacts controller and model...
def create
#contact = Contact.new(contact_params)
#contact.user = current_user
respond_to do |format|
if #contact.save
if user_signed_in?
format.html { redirect_to #contact, notice: 'Contact was successfully created.' }
else
format.html { redirect_to #contact, notice: 'Contact was successfully created.' }
end
format.json { render :show, status: :created, location: #contact }
else
format.html { render :new }
format.json { render json: #contact.errors, status: :unprocessable_entity }
end
end
end
class Contact < ActiveRecord::Base
belongs_to :owner, :class_name => 'User'
belongs_to :user
validates :email, :presence => {:message => 'Email cannot be blank'}
end
First off, you should never ever ever create 2 unrelated models on the same controller action. It breaks conventions and will only lead to problems.
You do not need to directly associate Contacts to Users. You should associate Contacts to Hooks and then associate Contacts through Hooks
class User < ActiveRecord::Base
has_many :hooks
has_many :contacts, through: :hooks
end
class Hook < ActiveRecord::Base
belongs_to :user
has_many :contacts
accepts_nested_attributes_for :contacts
end
class Contact < ActiveRecord::Base
belongs_to :hook
end
Now on the create action of the ContactsController, you can first get the Hook either by URL param or passed via post body. You can first find the Hook and create the Contact on it via:
hook = Hook.find(hook_id)
#contact = hook.contacts.new(contacts_param)
If you want to create contacts when creating a new Hook, you need to add :contacts_attributes on the strong_params, then pass an array of contact attributes via the POST. Adding accepts_nested_attributes_for to the Hook model allows you to easily create Contacts while creating Hooks by simply entering:
#hook = Hook.new(hook_params)
If I understand correctly, you want to create both a Hook and a Contact, and associate both to current_user. In your code you create both, but you only associate #hook with the current_user, and only save it, while ignoring the #contact. Simply associate it and save it as well:
def create
#hook = hook.new(hook_params)
#hook.user = current_user
#contact = Contact.new(contact_params)
#contact.user = current_user
respond_to do |format|
if #hook.save && #contact.save
format.html { redirect_to #hook, notice: 'Hook was successfully created.' }
format.json { render :show, status: :created, location: #hook }
format.js
else
format.html { render :new }
format.json { render json: #hook.errors + #contact.errors, status: :unprocessable_entity }
format.js
end
end
end
I have a new app, and I want share the common part with another ror app.
For example, I have a MVC named showcase for showing images in the previous app, I want have the same one in my new app too.
Now I have connect my new app with the previous app's database, create a model with same name"showcase", and could get its columns like image_file_name; image_content_type;; image_file_size.. but in the view page for showcase in the new app, it can not recognize imagewhich is paperclip type attribute.
Can anyone tell me how can I use the images from the previous app?
Thanks a lot!
update:
class Showcase < ActiveResource::Base
self.site = "http://localhost:3000"
end
controller:
class ShowcasesController < ApplicationController
def index
#showcases = Showcase.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #showcases }
format.xml { render :xml => #showcases }
end
end
def show
#showcase = Showcase.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #showcase }
format.xml { render :xml => #showcases }
end
end
end
view:
<%= image_tag #showcase.image.url %
the error:
undefined method"image" for #
how could my view recognize image?
by the way in the original app, it didn't render xml, so I manually append render xml is that ok?
format.html # show.html.erb
format.json { render json: #showcase }
format.xml { render :xml => #showcases }
If you have a proper restful API to access the showcase resource in your first app(app1), then you can use the showcase model from the other ROR app(app2) ActiveResource. ActiveResource abstracts the communication between the two applications and it looks as though the model belonged to the same application.
If the restful url for showcase model in app1 is www.app1.com/showcases, then in the app2, all you have to do is create a model named showcase like below.
class ShowCase < ActiveResource:Base
self.site="www.app1.com"
end
Check out rails cast episode http://railscasts.com/episodes/94-activeresource-basics for more details.
So I've been holding off putting a question on here because I don't want to bother the community with stupid questions, but I'm going to ask for help now anyway.
I'm quite new to Ruby on Rails, and as you've probably read from the title, I'm having trouble with my subform. More specifically, with assigning the parent object to a client object. I'm building a system for my work in where employees can register repairs (mobile phones) and keep track of them. I'm building the client object with #repair = Repair.new, which works fine, but when I try to set the Client with #repair = Client.new, the :client_id on the repair stays null.
Here's my repair.rb: (some fields are in Dutch, please ignore that)
class Repair < ActiveRecord::Base
attr_accessible :imei, :klantnaam, :telefoon, :intake, :branch_id, :id, :client_id
attr_accessible :merk, :type, :batterij, :lader, :headset, :batterijklep, :carkit, :schade_toestel, :schade_scherm, :bon, :datum_bon, :klacht, :prijsindicatie
belongs_to :branch
belongs_to :client
accepts_nested_attributes_for :client
end
client.rb:
class Client < ActiveRecord::Base
attr_accessible :email, :firstname, :lastname, :number, :phone, :postalcode
has_many :repairs
end
repairs_controller.rb: (I've left the irrelevant methods out, I was getting tired of the 4 spaces :P)
class RepairsController < ApplicationController
# GET /repairs/new
# GET /repairs/new.json
def new
#repair = Repair.new
#repair.client = Client.new
if request.remote_ip == "xx.xx.xx.xx"
#repair.branch = Branch.where(:name => "Xxxxxxx").first
end
#repair.intake = Time.now
respond_to do |format|
format.html # new.html.erb
format.json { render json: #repair }
end
end
# POST /repairs
# POST /repairs.json
def create
#repair = Repair.new(params[:repair])
respond_to do |format|
if #repair.save
format.html { redirect_to #repair, notice: 'Repair was successfully created.' }
format.json { render json: #repair, status: :created, location: #repair }
else
format.html { render action: "new" }
format.json { render json: #repair.errors, status: :unprocessable_entity }
end
end
end
end
And this is the JSON I get from /repair/new.json:
{"batterij":null,"batterijklep":null,"bon":null,"branch_id":null,"carkit":null,"client_id":null,"created_at":null,"datum_bon":null,"headset":null,"id":null,"imei":null,"intake":"2013-02-01T23:29:10Z","klacht":null,"klantnaam":null,"lader":null,"merk":null,"pickup":null,"prijsindicatie":null,"schade_scherm":null,"schade_toestel":null,"telefoon":null,"updated_at":null}
By the way, the branch assignment works flawlessly... (It's null now because I'm not on the IP I specified in the new method)
Please help me out... :-(
Robin
Solved it!!
The code above all works flawlessly, the problem was a <% instead of <%= in my view, which made my subform not show up. Duhh.
My update method of a Product controller is defined as follows:
def update
#product = Product.find(params[:id])
if params[:product][:image_path]
# Check if this product already has an image
File.delete(#product.full_image_path) if File.exist?(#product.full_image_path)
# Upload the new image
uploaded_img = params[:product][:image]
#product.image_path = Time.now.to_i.to_s + File.extname(uploaded_img.original_filename)
File.open(#product.full_image_path, 'w') do |file|
file.write(uploaded_img.read)
end
end
#product.name = params[:product][:name]
#product.description = params[:product][:description]
respond_to do |format|
if #product.errors.count == 0
format.html { redirect_to products_path, :notice => t(:product_updated) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #product.errors, :status => :unprocessable_entity }
end
end
end
This simply deletes the old image if already present, and uploads the new one. It also updates the Product attributes
How can I use #product.update_attributes(params[:product]) to avoid updating name and description attributes as I've done here?
If I do #product.update_attributes(params[:product]) I get an error because the params hash contains a value named "image" which is not an attribute of the object.
Thanks in advance
You could create an attribute setter for the image in the Product model called image=:
def image=(uploaded_img)
# Check if this product already has an image
File.delete(full_image_path) if File.exist?(full_image_path)
# Upload the new image
image_path = Time.now.to_i.to_s + File.extname(uploaded_img.original_filename)
File.open(full_image_path, 'w') do |file|
file.write(uploaded_img.read)
end
end
After that, remove the rest of the code in the controller and use #product.update_attributes(params[:product]).
I didn't tried it but I think it should work.
Do you know that you have some gems that allow to manage easily file upload like https://github.com/jnicklas/carrierwave or https://github.com/thoughtbot/paperclip
You should try to refactor your controller a bit, a controller should not be running any other tasks other than directing the traffic of your models and views. Try to move all your file operations to a separate helper.
I have two tables:
Client(id,name,...)
Purchase(id,item,date,client_id,...)
They have their respective Model, with their validations. What I need is to create a new client with a new purchase, all into the create method of Client controller. Something like this:
def create
#client = Client.new(params[:client])
respond_to do |format|
if #client.save
# Add purchase
#sell = Purchase.new
#sell.client_id = #client.id
#sell.date = params[:date]
# Fill another fields
if #sell.save
# Do another stuff...
else
format.html { render :action => "new" }
format.xml { render :xml => #client.errors, :status => :unprocessable_entity }
end
flash[:notice] = 'You have a new client!'
format.html { redirect_to(:action => :show, :id => #evento.id) }
format.xml { render :xml => #client, :status => :created, :location => #client }
else
format.html { render :action => "new" }
format.xml { render :xml => #evento.client, :status => :unprocessable_entity }
end
end
end
In Purchase's model I have:
belongs_to :client
validates_format_of :date, :with => /^20[0-9]{2}[-][0-9]{2}[-][0-9]{2}$/, :message => 'not valid'
validates_presence_of :date
And there is my problem: how can I validate the date input, through validations into the model, from Client controller? And, how can I rollback the new client created when errors?
Yes, I can do the check as the very first instruction in the method, with a regular expression, but I think it's ugly. I feel like might exist a conventional method for doing this validation or even doing all the stuff in another way (i.e. calling create method for Purchase from Client controller).
Can you put me back in the right way?
Thank you in advance.
Take a look at the following page on working with associations.
Rails provides you with a bunch of handy methods on your objects.
Like the following:
Client.purchases.empty?
Client.purchases.size,
Client.purchases
Client.purchases<<(purchase)
Client.purchases.delete(purchase)
Client.purchases.find(purchases_id)
Client.purchases.find_all(conditions)
Client.purchases.build
Client.purchases.create
When using these methods, you're taking advantage of the validations on each of the models.
Hop into your Rails console and create a new client and try any of the above methods. You'll quickly learn how powerful they are and you'll be on your way in no time.
Edit: Here's a much better guide on Rails associations!
Depends a little on the situation, but you can use validates_associated to run the validations on associated objects. Then you can create the user (but don't save), create the purchase (but don't save) and try to save the user. If you've done it right the user will fail to save with a validation error on the associated object.