My project:
class User < ActiveRecord::Base
attr_accessor :password
attr_accessible :email,
:password,
:password_confirmation,
:first_name,
:last_name,
:birth_date,
:residence,
:user_role,
:show_email,
:avatar
as_attached_file :avatar,
:default_url => '/images/system/user_avatars/default/default_avatar.png',
:url => "/public/images/system/user_avatars/:id_:style.:extension",
:path => "/public/system/user_avatars/:id_:style.:extension"
def update_profile(user_id, params) #params has :category and :user params
#user = User.find(user_id)
#user.update_attributes(params[:user])
return params[:category]
end
end
So, from my controller i call this method and i get no error. Paperclip shows attachment saved. The database is updated, but the image file is not saved. I have an registration made from scratch, so that's why i have the "attr_accessor :password"
I checked:
Have :multipart => true in form
Have attr_accessible :avatar in user model
Can any one give me some lead, cos i cant figure, why paperclip dosnt save the file.
Set attr_accessible :avatar_file_name as well, and you also need a paperclip.rb initializer:
require "paperclip"
Paperclip.options[:command_path] = "/ImageMagick"
And, of course, have ImageMagick installed.
Related
I am using the following Gems:
'paperclip'
'aws-sdk', '~> 2.3'
I would like to save images to S3, but am unable to get them to save.
model/user.rb
class User < ApplicationRecord
# This method associates the attribute ":avatar" with a file attachment
has_attached_file :avatar, styles: {
thumb: '100x100>',
square: '200x200#',
medium: '300x300>'
}
# Validate the attached image is image/jpg, image/png, etc
validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
end
migration
class AddAvatarToUsers < ActiveRecord::Migration[5.1]
def self.up
add_attachment :users, :avatar
end
def self.down
remove_attachment :users, :avatar
end
end
controllers/users_controller.rb
class UsersController < ApplicationController
def edit
#user = User.find_by(id: params[:id])
end
def update
#user = User.find(params[:id])
if #user.update(user_params)
flash[:notice] = "Edit successfully"
redirect_to("/users/#{#user.id}")
end
end
private
def user_params
params.require(:user).permit(:avatar, :name, :email, :phone_number, :description, :college_name)
end
end
Why is the image avatar not being stored in the database?
Should I add any database columns? What should I do? I would appreciate any input to help me solve this problem.
Paperclip must be configured to use S3 to store the objects (images). It will store metadata associated with these in the database, but not the images.
See Paperclip Documentation
You will also need to set an access policy for your S3 bucket, and define on the User model how they are to be addressed from S3.
I'm following this tutorial on how to nest other Models in my Devise registration form. I'm getting an error in my New controller:
'NoMethodError in Users::RegistrationsController#new undefined method `languages_user=' for #'.
Languages_Users is a join table, and I'm wondering if this is the reason it isn't working, but I don't understand what the solution is. I want to add 2 different records of Languages_Users when the user signs up.
Models:
User.rb
class User < ActiveRecord::Base
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable
belongs_to :role
has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100#" }, :default_url => "/images/:style/missing.png"
validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
validates_presence_of :first_name, :last_name, :location, :nationality, :bio
before_save :assign_role
def assign_role
self.role = Role.find_by name: "user" if self.role.nil?
end
has_many :languages_users
has_many :languages, :through => :languages_users
accepts_nested_attributes_for :languages_users
Language.rb
class Language < ActiveRecord::Base
has_many :languages_users
has_many :users, :through => :languages_users
end
Langauges_user.rb
class LanguagesUser < ActiveRecord::Base
belongs_to :user
belongs_to :language
validates_presence_of :user_id, :language_id, :level
validates :user_id, :uniqueness => {:scope => :language_id, :message => 'can only delcare each language once. Please change the level of the language in Manage Languages.'}
end
Controllers:
registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
def new
build_resource({})
self.resource.languages_user = LanguagesUser.new[sign_up_params]
respond_with self.resource
end
def create
#user_id = current_user.id
super
end
def sign_up_params
allow = [:email, :password, :password_confirmation, [languages_user_attributes: [:language_id, :user_id, :level]]]
end
end
Relevant sections of User's new.html.erb
<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %>
<%= devise_error_messages! %>
<%= f.fields_for :langauges_user do |lu| %>
<%#= lu.text_field :language_id %>
<%= lu.collection_select(:language_id, Language.order('language ASC').all, :id, :language) %><br>
<%= lu.hidden_field languages_user[level], value: 1 %>
<% end %>
<%= f.submit "Sign up" %>
<% end %>
Relevant routes
Rails.application.routes.draw do
resources :languages_users
devise_for :users, controllers: { registrations: "users/registrations" }
resources :users
get 'languages_users/:id/sign_up', to: 'languages_users#sign_up', as: 'sign_up'
end
I'm still learning - so please let me know if you need to see anything else. Thanks!
I'm not that up to speed on Devise as I only recently started using it myself, but if I understand correctly it's not a Devise related problem - just harder to get a fix on because of Devise's self.resource abstraction.
You've deviated from your tutorial in an important respect: in the tutorial a User creates a Company, but the Company has_many :users. In your case the User creates a LanguagesUser, but here, the User has_many :languages_users. This means new syntax. This line, that's causing your error currently:
self.resource.languages_user = LanguagesUser.new[sign_up_params]
Needs to be along the lines of:
self.resource.languages_users.build(sign_up_params) #but see below re sign_up_params
Or if you want to save the associated resource right off the bat (I assume not, since you're not at the moment), you can use create or create! instead of build.
Aside
You may run into different trouble with your sign_up_params method, which also appears to have deviated from the tutorial - it doesn't actually use the allow array to whitelist any params, at least as written in your question. In any case, note they didn't use it when instantiating the Company, so it may not be fit for purpose when building your LanguagesUser, either.
A simple call to sign_up_params[:languages_user_attributes] should get you over the line, once you've fixed the sign_up_params method. Or you can set the nested object up with its own params whitelist.
The idea is simple. I require a nested attribute tag for user registration. tag requires a user_id.
the view
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: {role: :form}) do |f| %>
<%= f.fields_for :tags, resource.tags.build do |a| %>
<%= a.text_field :tagname %>
<% end %>
<% end %>
tag migration
class CreateTags < ActiveRecord::Migration
def change
create_table :tags do |t|
t.string :tagname
t.references :user, index: true
t.timestamps
end
add_index :tags, :tagname, unique: true
end
end
the tag model
class Tag < ActiveRecord::Base
belongs_to :user
validates_presence_of :user_id
validates_uniqueness_of :tagname
end
the user model
class User < ActiveRecord::Base
has_many :tags, autosave: true, dependent: :destroy
accepts_nested_attributes_for :tags
end
strong parameters
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(
:tags_attributes => [:id, :user_id, :tagname]
) }
The save feature is untouched at the moment. The form passes the nested attribute :tagname just fine. But I've been unable to get the "would be" user_id from resource.
I've already looked for hours online for any answer to this. None has appeared, but the idea that the nested attribute should be saved after the initial user object is saved sounds like a workable solution. But then it's no longer handled as a nested attribute.
Help is appreciated! Thanks!
Solution
Alright I solved it. I bastardised my devise registrations controller a bit, but it works.
First I removed user_id verification from my tag model.
class Tag < ActiveRecord::Base
belongs_to :user
validates_uniqueness_of :tagname
end
I then proceeded with modifying the devise registration controller.
def create
build_resource(sign_up_params)
# added code begins here
#tag = nil
#tag.define_singleton_method(:valid?) {false}
if params["user"].has_key? "tags_attributes"
#tag = Tag.new(params["user"].delete("tags_attributes").values.first)
if #tag.valid?
resource_saved = resource.save # original line of code
else
resource.errors.add(*#tag.errors.first)
flash.delete :tagname_error
set_flash_message :alert, :tag_taken if is_flashing_format?
end
else
set_flash_message :alert, :need_tag if is_flashing_format?
end
# end added code
yield resource if block_given?
if resource_saved
if resource.active_for_authentication?
set_flash_message :notice, :signed_up if is_flashing_format?
sign_up(resource_name, resource)
# This is where current_user has been created and resource.id == current_user.id
# begin added code
#tag.user_id= resource.id
# end added code
respond_with resource, location: after_sign_up_path_for(resource)
else
set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_flashing_format?
expire_data_after_sign_in!
respond_with resource, location: after_inactive_sign_up_path_for(resource)
end
else
clean_up_passwords resource
#validatable = devise_mapping.validatable?
if #validatable
#minimum_password_length = resource_class.password_length.min
end
respond_with resource
end
end
I've defined :tag_taken and :need_tag in my devise locales file for translations of the error string.
And everything works!
For the record this is in addition to the existing code in the question.
I'm following this video tutorial and learning how to create authentication from scratch:
http://www.youtube.com/watch?v=O5RDisWr_7Q
Here is my migration file for User:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :email
t.string :password_hash
t.string :password_salt
t.timestamps
end
end
end
And my controller:
class UsersController < ApplicationController
def new
#user = User.new
end
def create
#user = User.new(params[:users])
if #user.save
redirect_to root_url, :notice => "Signed up!"
else
render "new"
end
end
end
And finally my Model:
class User < ActiveRecord::Base
attr_accessible :email, :password_hash, :password_salt
before_save :encrypt_password
validates_confirmation_of :password
validates :password, presence: true
validates :email, presence: true
def encrypt_password
if password.present?
self.password_salt = BCrypt::Engine.generate_salt
self.password_hash = BCrypt::Engine.hash_secret(password, password_salt)
end
end
end
Now, I think I know why this error is firing; obviously the #user.save call is trying to save the value in password to the password field in the User table, but that field doesn't exist in the database. In the video he mentions that to fix this bug I should just add: attr_accessible :password to my model and it should work, but I get the following error:
NoMethodError in UsersController#create
undefined method `password' for #
app/controllers/users_controller.rb:8:in `create'
Any suggestions? I just would like to take advantage of the validations that come with using a strongly typed model instead of loose html fields.
You have attr_accessible :password_hash, :password_salt, but I think it should be attr_accessible :password together with attr_accessor :password since you need a virtual attribute password on which you work in your encrypt_password method. So:
class User < ActiveRecord::Base
attr_accessible :email, :password
attr_accessor :password
end
attr_accessor creates the virtual attribute which is not available as a database field (therefore virtual).
attr_accessible is a security mechanism to white-list attributes which are allowed to be set through mass-assignment like you do with User.new(params[:users])
I am new with Ruby on Rails. I just build web application on the existing database. I use rails to generate 2 scaffolds for restaurant and location tables. After that I set relationship for these two tables:
class Restaurant < ActiveRecord::Base
attr_accessible :created, :cuisine_fk, :dish_keywords, :dish_names, :factual_id, :first_name, :last_name, :name, :status
has_many :locations
end
class Location < ActiveRecord::Base
attr_accessible :address1, :address2, :city, :created, :latitude, :longitude, :phone, :restaurant_fk, :state, :status, :url, :zip
belongs_to :restaurant
end
I didn't use "rake db:migrate" after I set up this relationship for these tables, because I was afraid that this action would make changes the existing tables.
When I run this command line
<%= restaurant.location.address1%>
it shows error:
undefined method `location'
" NoMethodError in Restaurants#index
Showing C:/Sites/Dishclips/app/views/restaurants/index.html.erb where line #52 raised:
undefined method `location' for #<Restaurant:0x5853bb8> "
After that I tried to set foreign key for the file:
class Location < ActiveRecord::Base
attr_accessible :address1, :address2, :city, :created, :latitude, :longitude, :phone, :restaurant_fk, :state, :status, :url, :zip
belongs_to :restaurant, :class_name => "Restaurant", :foreign_key => 'restaurant_fk'
end
but it still doen't work.
Is there any way that we can set foreign keys in stead of using "rails db:migrate" after we set up the relationships for tables ? I appreciate your help a lot.
The problem is that you are using location wrongly.
Since the restaurant has_many locations you can't use it the way you mentioned. Because you have an array of locations, actually is a ActiveRecord relationship, so in order to access one of the items assciated you'll have to execute the query and get one of the elements. Here is an example of how to get the first element.
restaurant.locations.first.address1
If the restaurant have only one location, than you should change your model to
class Restaurant < ActiveRecord::Base
attr_accessible :created, :cuisine_fk, :dish_keywords, :dish_names, :factual_id, :first_name, :last_name, :name, :status
has_one :locations
end
and access the property as you are doing:
restaurant.location.address1
Also I'm assuming that your database have the columns you specified, otherwise you'll have to run the migrations.
Regards!
Rails associations are covered very well here in the Rails Guides.
I'll walk you through a basic setup here.
$ rails generate model Restaurant name owner ...
$ rails generate model Location restaurant_id:integer city ...
You then need to migrate your database with rake db:migrate for the database table changes to become effective.
The restaurant_id allows us to set the associations in our models as follows
class Restaurant < ActiveRecord::Base
has_many :locations, dependent: :destroy
attr_accessible :name, :owner
end
class Location < ActiveRecord::Base
belongs_to :restaurant
attr_accessible :city # no restaurant_id here
end
Now you can access your restaurants location as follows.
r = Restaurant.create!(name: '...')
l = Location.create!(city: '...')
# Add association
r.locations << l
r.locations will now return an Array with l in it
l.restaurant will return r
Try to play a little with the different styles of associations, for example by creating new Rails apps quickly and just trying some kind of associations, also some that require a join model.
Now I try this way, then it works. Thank you very much.
<td>
<% restaurant.locations.search(params[:restaurant_fk]).each do |location| %>
<!--address = <%= location.address1 %> + " " + <%= location.address2 %>-->
<%= location.address1 %>
<%= location.address2 %> ,
<%= location.city %> ,
<%= location.state %> ,
<%= location.zip %>
<% end %>
</td>