Rails/Paperclip object creation issue - ruby-on-rails

I am having a problem while validating on an image field in model on a rails4 app.
class ModelA < ActiveRecord::Base
validates :name, presence: true
validates :logo, presence: true
has_attached_file :logo, styles: {
thumb: '100x100>',
square: '200x200#'
}
In the migrations, a new instance of this model is to be created.
def migrate(direction)
super
if direction == :up
obj = Model1.create!(:name => "Test")
This is failing as the required field is not specified and If I am explicitly specifying a default image, then the table does not have the necessary column yet.
This migration runs if I am removing the image (in this case, logo) validation before migration and thereafter specify the image file and details like its name.
Is there a better way to setup this model?

I've figured this out. The problem was in the migrations - the logo migration (as well as validation) was added afterwards of this Object creation in the migration. I added the logo to the Model1.create! and moved this migration After these migrations to solve the error.
Hence, my migrations roughly are:
def change
create_table :... do |t|
t.string :name
t.timestamps
end
Followed by addition of paperclip column
def self.up
change_table :... do |t|
t.attachment :logo
end
end
And added the model in another migration that was coming after them.
def migrate(direction)
super
if direction == :up
logo_img = File.open('app/assets/images/logo-big.png', 'rb')
Model1.create!(:name => "TestObj", :logo => logo_img )

Related

Failure to run db:migrate in Rails

I'm trying to run db:migrate after installing image attachment via paperclip gem and it won't allow me to do migration. Could someone help please? Thanks a lot This is what it said on my terminal
This is my config file config/database.yml
This is my create item table:
class CreateTodoItems < ActiveRecord::Migration[5.0]
def change
create_table :todo_items do |t|
t.column :content
t.column :deadline
t.references :todo_list, foreign_key: true
t.timestamps
end
end
end
Item model
class TodoItem < ActiveRecord::Base
belongs_to :todo_list
has_attached_file :image, styles: { medium: "500x500>", thumb: "100x100#"}
validates_attachment_content_type :image, content_type: /\Aimage\/.*\z/
def completed?
!completed_at.blank?
end
end
You're adding a column to database that doesn't exist. You don't happen to have items table you have todo_items table your migration should look something like:
$ bin/rails generate migration AddAttachmentImageToTodoItems attachment_image:string

How to force user to attach file (file_field can not be blank) in rails

I am creating a podcast website with rails. When the user uploads a new episode to his/her account I want them to have to upload a podcast (mp3 file) and a thumbnail for that podcast (jpeg/png).
The problem is when I try to create the column for the mp3 and the thumbnail in SQLite it won't let me have a "not null" column with an initial value null.
I don't want default not null values, I want to FORCE them to attach files when they upload new episodes.
I am using paperclip to generate the migration for attachment columns "rails g paperclip user attachment"
This is the migration file for thumbnails, I have another similar file for MP3s.
class AddAttachmentThumbnailToEpisodes < ActiveRecord::Migration
def self.up
change_table :episodes do |t|
t.attachment :thumbnail, null: false
end
end
def self.down
remove_attachment :episodes, :thumbnail
end
end
It won't let me rake db:migrate this
You can handle it two ways
#1 Put a validation in the model
validates :attachment, presence: true
OR
#2 You can handle it in the form with required: true option
<%= f.file_field :attachment, required: true %>

Rails ActiveRecord not working with two tables

I've been trying to have my rails project only update the user table with the users unique facebook data. However, I can't get the facebook data to populate. I've tried multiple approaches but the end code seems to be hacky and using brute force to update the columns (as well as creating duplicate records)
Here are my examples:
User
class User < ActiveRecord::Base
has_one :facebook
def self.create_with_omniauth(auth)
create! do |user|
user.email = auth['email']
end
end
end
Facebook
class Facebook < ActiveRecord::Base
belongs_to :user
def self.create_with_omniauth(auth)
create! do |fb|
if auth['info']
fb.profile_link = auth['info']['profile_link'] || "test"
end
end
end
Migrations:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :email
t.timestamps null: false
end
end
end
class Facebooks < ActiveRecord::Migration
create_table :facebooks do |f|
f.belongs_to :user, index: true, :unique => true
f.string :profile_link
f.timestamps null: false
end
end
While creating the user:
SessionController (When calling create for user)
def create
auth = request.env["omniauth.auth"]
user = User.where(:provider => auth['provider'],
:uid => auth['uid'].to_s).first || User.create_with_omniauth(auth)
Facebook.create_with_omniauth(auth)
My understanding of Rails ActiveRecord so far... is that if I use "has_one" and "belongs_to" then it should automatically create records in the facebook table if a user table was created?
My expected Data would be:
SELECT * FROM users where id = 1;
id email
1 email#email.com
SELECT * FROM facebooks where user_id = 1;
id user_id profile_link
1 1 facebook.com/profile_link
facebook has no record created at all.
Not sure where I went wrong, I've followed tons of tutorials and hope I can master the active record.
Thanks!
Side Question for #val
def self.facebook_handler(user, auth)
if Facebook.exists?(user_id: id)
user = Facebook.find_by(user_id: id)
user.update(name: me['name'])
user.update(first_name: me['first_name'])
else
create! do |fb|
if me
fb.name = me['name']
fb.user_id = user.id
fb.first_name = me['first_name']
end
end
end
end
--- otherwise it kept inserting new records each time I logged in.
So many moving pieces in activerecord and in Rails. I think you have to go back to your migration and address a few things to set a solid model foundation for the view and controller parts of your MVC.
I see model-type function in the migration you posted, which is not going to serve you well. Migrations should be as flexible as possible, the constraints should be placed on the model.rb.
Migration: Flexible. Basic relationship indices set up.
Model: The
model.rb defines constraints (has_one, belongs_to, etc) and further
embellishes and validates data relationships (:dependent,:required,
etc.)
Your users model looks fine.
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :email
t.timestamps null: false
end
end
end
Your facebooks migration should have looked more like this. Create a t.reference and add the index.
class Facebooks < ActiveRecord::Migration
create_table :facebooks do |f|
t.references :user, index: true
f.string :profile_link
f.timestamps null: false
end
add_index :facebooks, [:user_id]
end
Then in your Facebook model you can apply restraints and requirements
facebook.rb
belongs_to :user,
validates :user_id, presence: true, :unique => true
Your user model.rb should include:
has_one :facebook
There are some other questions about your higher level actions in the controller, but I think setting up your model will help you make progress towards your goal.
The model constraints below, along with the index setup looks like it would cause ActiveRecord to ROLLBACK and not add a duplicate facebook record for a given user. But it sounds like duplicates are being added to the facebook table. So, how?
facebook.rb
belongs_to :user,
validates :user_id, presence: true, :unique => true
...
user.rb
has_one :facebook
The 'if' clause you wrote looks to me as if it would be unnecessary if the relationship between user / facebook are set up and working in the model and database table, which makes me think there's a missing validation somewhere.
There's something to try, a model migration (change) on Facebook data description to add a :unique validator to the user_id field of the db table itself. (There's no change_index command, you have to remove and then add.)
remove_index :facebooks, [:user_d]
add_index :facebooks, [:user_id], :unique => true
Try taking your 'if' logic out and see if you're getting dupes. The relationships need to be properly setup before proceeding to the logic in the controller or you will break your head trying to unwind it.
And to your question in the comment, scopes are beautiful for creating collections based on parameters. So, in your user.rb model:
scope :important_thing_is_true, -> { where(:provider => auth['provider'],:uid => auth['uid'].to_s).first) }
Which is referenced by user.important_thing_is_true returns the collection or nil, which then you can test or use in other logic or display, etc. But, if you don't have the dupe records problem, maybe this scope isn't needed.

Rails4 : My hstore attribute is being converted to String

I have a Hash attribute in my model that uses Postgres hstore extention. The problem is that this attribute is converted to String by Rails4. This prevents me to make basic operations such as .each or .map to treat my hash attribute.
Using the Rails console, the Hash is not converted. Typing:
#device.data
#device.data.class
Gives in Rails console:
{"city"=>"London", "owner_name"=>"John"}
Hash
And in the application itself (using the navigator):
"\"city\"=>\"London\","\"owner_name\"=>\"John\"
String
Do you have any idea?
Update:
Here is the model:
class Device < ActiveRecord::Base
belongs_to :company
has_many :records
validates :name, presence: true
end
And the corresponding migration file:
class CreateDevices < ActiveRecord::Migration
def change
create_table :devices do |t|
t.string :name
t.hstore :data
t.integer :company_id
t.timestamps
end
add_index :devices, :name
end
end
Try deleting your tmp folder and restarting all your servers.
rm -rf tmp/*

ActiveRecord adding rating range in migration file

class AddRatingToBooks < ActiveRecord::Migration
def up
add_column :books, :rating, :integer
end
def down
remove_column :books, :rating
end
I have the following snippet of code in my db/migrate/, I'm trying to add ratings to my books table, where it would be in a range from 0-100, but I'm not sure how to add that here, all i could find was querying with ranges. I'm sure it's simple I'm just not there yet.
You don't need to specify the range of integer values in your migration file. The migration file is simply used to add the database column to store the rating. This is not the place to add validations.
You should use your Book model to specify a validation that ensures your ratings fall within a certain range. Something like this:
class Book < ActiveRecord::Base
validates :rating, :inclusion => { :in => 0..100 }
end
I would highly recommend reading the Rails guides on both migrations and validations.
Probably I'm too late with the answer. But it's possible to define validation on db level with Migration Validators project: https://github.com/vprokopchuk256/mv-core
As example, in your migration:
def change
change_table :books do |t|
t.integer :rating, inclusion: 0..100
end
end
and then in your model:
class Book < ActiveRecord::Base
enforce_migration_validations
end
As result your validation will be defined both in db ( as statement inside trigger or check constraint, depending on your db) and on your model
SQL ( PostgreSQL ):
=# insert into books(rating) values(10);
INSERT 0 1
=# insert into books(rating) values(200);
ERROR: new row for relation "books" violates check constraint "chk_mv_books_rating"
Rails console:
Book.new(title: 10).valid?
=> true
Book.new(title: 200).valid?
=> false

Resources