I'm working through a Rails tutorial, and have hit a wall I'm not sure if I missed a step. I have two models, both with a boolean called 'visible', but I can only select using it on one of the models. For reference here are my migrations
class CreateSubjects < ActiveRecord::Migration
def up
create_table :subjects do |t|
t.string "name"
t.integer "position"
t.boolean "visible", :default => false
t.timestamps null: false
end
end
def down
drop_table :subjects
end
end
And
class CreatePages < ActiveRecord::Migration
def up
create_table :pages do |t|
t.integer "subject_id"
t.string "name"
t.string "permalink"
t.integer "position"
t.boolean "visible", :default => false
t.timestamps null: false
end
add_index("pages", "subject_id")
add_index("pages", "permalink")
end
def down
drop_table :pages
end
end
Both have a t.boolean "visible", :default => false in them. And I can edit them and change that value fine. But if I say pull up a rails console and try...
This works fine:
Subject.visible
This gives me a NoMethodError: undefined method `visible':
Page.visible
During one point in the tutorial I had to change the line
#page.sections.visible.sorted.each
to
#page.sections.where(:visible => true).sorted.each
Which did work, but I have no idea WHY I had to do so
Link to project on GitHub if it helps
https://github.com/TaylorHuston/Rails_LyndaCMS
The visible function is an instance function, so you have to use it on an instance of Page:
Page.new.visible
You were able to use it on Subject because you created a scope :visible, so you have one function for the instance and one for the Relation.
Related
I have a little problem with my db schema.
I have create a migration named messages but it was no good so I deleted them for replace by the good messages migration.
But in my schema I have the last messages migration yet.
How it's possible?
rails db:migrate:status:
up 20160924085640 Create conversations
up 20160924090519 Create messages
schema.rb:
create_table "conversations", force: :cascade do |t|
t.integer "sender_id"
t.integer "recipient_id"
end
create_table "messages", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "subject"
t.string "body"
t.integer "recipient_id"
t.integer "sender_id"
end
migration:
class CreateMessages < ActiveRecord::Migration[5.0]
def change
create_table :messages do |t|
t.text :body
t.references :conversation, index: true
t.references :user, index: true
t.boolean :read, :default => false
t.timestamps
end
end
end
routes
resources :conversations do
resources :messages
end
When I try to access to /conversations
I have this error: uninitialized constant ConversationsController
First You should read about rails routes and their correspond action in controller
For Every Route
You should have one controller
one action in controller for desired route
and one view(erb) filw in views
Like for your query
In controller
Create index action
than in views => conversations => index.html.erb
My models
CAR BRANDS MODEL
class CarBrand < ActiveRecord::Base
has_many :car_ads
end
CAR ADVERTISEMENTS MODEL
class CarAd < ActiveRecord::Base
has_one :car_brand
end
my controller:
def index
#car_ads = CarAd.all.order("car_ads.created_at DESC")
end
car ads migrations:
class CreateCarAds < ActiveRecord::Migration
def up
create_table :car_ads do |t|
t.integer "user_id"
t.integer "car_brand_id"
t.integer "car_model_id"
t.integer "state_id", :limit => 2
t.integer "vin_id"
t.integer "year_manufac", :precision => 4
t.integer "km_age"
t.integer "price_usd", :limit => 7
t.integer "car_tel_number", :precision => 8
t.float "motor_volume", :limit => 10
t.string "transmission"
t.integer "horse_power", :limit => 3
t.text "description"
t.boolean "visible", :default => true
t.boolean "active", :default => true
t.string "photo_file_name"
t.string "photo_content_type"
t.integer "photo_file_size"
t.datetime "photo_updated_at"
t.timestamps null: false
end
add_index :car_ads, :user_id
add_index :car_ads, :car_brand_id
add_index :car_ads, :car_model_id
add_index :car_ads, :state_id
add_index :car_ads, :vin_id
end
def down
drop_table :car_ads
end
end
Car brands migratiions
class CreateCarBrands < ActiveRecord::Migration
def up
create_table :car_brands do |t|
t.string "brand", :limit => 20
t.timestamps null: false
end
end
def down
drop_table :car_brands
end
end
so the problem is that i cant get car brand form car ads, please help,
i wanted to get that like
iterating
<% #car_ads.each do |carad|%>
<%= carad.car_brand %>
<%end%>
Modify CAR ADVERTISEMENTS MODEL
class CarAd < ActiveRecord::Base
belongs_to :car_brand
end
Modify your controller:
def index
#car_ads = CarAd.all.order("created_at DESC")
end
You didn't add any reference to CarBrand in CarAd table, just add the car_ad_id column with a migration like this
class AddCarBradIdToCarAd < ActiveRecord::Migration
def change
add_column :car_ads, :car_brand_id, :integer
end
end
So rails would be able to get the corresponding CarBrand from a CarAd
I just set it up so that when a user signs up for my blog it gives them a gravatar in the users index. That works fine but I was thinking of making it so that when that user makes a post it will display their gravatar from the user. I just made a user_id colum to posts through a migration.
here is a copy of my schema
ActiveRecord::Schema.define(version: 20131114141804) do
create_table "comments", force: true do |t|
t.text "content"
t.datetime "created_at"
t.datetime "updated_at"
t.string "post_id"
end
create_table "posts", force: true do |t|
t.string "title"
t.text "content"
t.datetime "created_at"
t.datetime "updated_at"
t.string "user_id"
end
create_table "users", force: true do |t|
t.string "email"
t.string "password_digest"
t.datetime "created_at"
t.datetime "updated_at"
t.string "auth_token"
t.string "password_reset_token"
t.datetime "password_reset_sent_at"
t.string "avatar_url"
end
end
models:
user:
class User < ActiveRecord::Base
has_secure_password
validates_uniqueness_of :email
has_many :posts
validates_presence_of :password, :on => :create
before_create { generate_token(:auth_token) }
def send_password_reset
generate_token(:password_reset_token)
self.password_reset_sent_at = Time.zone.now
save!
UserMailer.password_reset(self).deliver
end
def generate_token(column)
begin
self[column] = SecureRandom.urlsafe_base64
end while User.exists?(column => self[column])
end
end
Post:
class Post < ActiveRecord::Base
belongs_to :user
has_many :comments
end
class Comment < ActiveRecord::Base
has_many :posts
end
application_helper.rb
module ApplicationHelper
def avatar_url(user)
gravatar_id = Digest::MD5::hexdigest(user.email).downcase
"http://gravatar.com/avatar/#{gravatar_id}.png?s=200"
end
end
was trying to do something new, could anyone help me out and or point me in the right direction?
I'd recommend taking a good look at using paperclip for the attachment process rather than trying to re-invent the wheel. Paperclip's documentation actually uses a user avatar as an example, so it'd be perfect for your use case.
You may want to use a Rails plugin to integrate with Gravatar rather than doing it yourself:
gravtastic
Gravatar Rails plugin
I'm trying to use a :has_many :through type association, but I'm getting the following error:
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: work_units.developer_id:
Many other posts about this sort of thing have just had spelling mistakes, but I've checked mine.
class Developer < ActiveRecord::Base
attr_accessible :skype_name, :language_ids, :user_attributes
has_many :work_units
has_many :projects, :through => :work_units
...
end
class Project < ActiveRecord::Base
attr_accessible :complete, :description, :finalised, :price
has_many :work_units
has_many :developers, :through => :work_units
...
end
class WorkUnit < ActiveRecord::Base
attr_accessible :hours_worked
belongs_to :project
belongs_to :developer
end
I've run db:migrate and it didn't complain. I did make a mistake and had to rollback the db then re-migrate, but I think I did it right. I use the annotate gem and it doesn't show any of the relationship ids I'd expect. So, do I need to create a WorkUnits table or am I missing something? The rails guide didn't mention manually making tables.
Edit
Here's the migration I used to create the WorkUnit model and stuff:
class CreateWorkUnits < ActiveRecord::Migration
def change
create_table :work_units do |t|
t.integer :hours_worked, :default => 0
t.timestamps
end
end
end
Edit 2
Snippets from my schema.rb:
create_table "work_units", :force => true do |t|
t.integer "hours_worked", :default => 0
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "projects", :force => true do |t|
t.string "description"
t.decimal "price", :precision => 8, :scale => 2
t.boolean "complete", :default => false
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
Similarly for :developers. So, why doesn't my migration add the association information for me?
Your WorkUnit migration should look like this:
class CreateWorkUnits < ActiveRecord::Migration
def change
create_table :work_units do |t|
t.integer :hours_worked, :default => 0
t.references :developer
t.references :project
t.timestamps
end
add_index :work_units, :developer_id
add_index :work_units, :project_id
end
end
You need to add the foreign keys to your work_units table.
class CreateWorkUnits < ActiveRecord::Migration
def change
create_table :work_units do |t|
t.integer :hours_worked, :default => 0
t.integer :project_id, null: false
t.integer :developer_id, null: false
t.timestamps
end
add_index :work_units, :project_id
add_index :work_units, :developer_id
end
end
Another way:
class CreateWorkUnits < ActiveRecord::Migration
def change
create_table :work_units do |t|
t.integer :hours_worked, :default => 0
t.belongs_to :project
t.belongs_to :developer
t.timestamps
end
add_index :work_units, :project_id
add_index :work_units, :developer_id
end
end
You can also define these fields when generating your model, then they'll be added to the migration automatically as show in the second snippet.
$ rails g model WorkUnit hours_worked:integer project:belongs_to developer:belongs_to
Hope that helps.
A table for WorkUnit needs to exist, whether that means it migration was automatically generated via scaffolding or if the migration was manually written by you.
If you don't have a migration yet that creates that table, you'll need to create that migration because the table does need to exist.
You do need a work_units table with a project_id and developer_id column.
Have a look at http://xyzpub.com/en/ruby-on-rails/3.2/activerecord_datenbank_anlegen.html if you don't know how to create a table.
here is my migration in rails 3.2.2:
class CreateStatistics < ActiveRecord::Migration
def change
create_table :statistics do |t|
t.string :name
t.integer :item_id
t.integer :value
t.text :desc
t.timestamps
t.index [:name, :item_id]
end
end
end
and here is the migrate error:
== CreateStatistics: migrating ===============================================
-- create_table(:statistics)
ActiveRecord::ConnectionAdapters::TableDefinition
rake aborted!
An error has occurred, all later migrations canceled:
undefined method `index' for #<ActiveRecord::ConnectionAdapters::TableDefinition:0xbd16888>
Tasks: TOP => db:migrate
(See full trace by running task with --trace)
what is the right way to create a index?
You can still add an index as a part of a "change" migration. You just have to do it outside of the call to create_table:
class CreateStatistics < ActiveRecord::Migration
def change
create_table :statistics do |t|
t.string :name
t.integer :item_id
t.integer :value
t.text :desc
t.timestamps
end
add_index :statistics, [:name, :item_id]
end
end
This correctly creates the table and then the index on an "up" migration and drops the index and then the table on a "down" migration.
so I change it to the old way, and it works.
and I think there is a new way doing this by using change method.
class CreateStatistics < ActiveRecord::Migration
def up
create_table :statistics do |t|
t.string :name
t.integer :item_id
t.integer :value
t.text :desc
t.timestamps
end
add_index :statistics, [:name, :item_id]
end
def down
drop_table :statistics
end
end
If you have more than one index and don't want to repeat the table name several times in individual add_index calls, you can use a change_table block that follows the create_table.
create_table :user_states do |t|
t.references :user, :null => false
t.integer :rank
t.integer :status_code
end
change_table :user_states do |t|
t.index [:rank, :status_code]
end
class CreateTempPfp < ActiveRecord::Migration
def change
create_table :temp_ptps do |t|
t.string :owner
t.integer :source_id
t.string :source_type
t.integer :year
t.string :pcb_type
t.float :january
t.float :february
t.float :march
t.float :april
t.float :may
t.float :june
t.float :july
t.float :august
t.float :september
t.float :october
t.float :november
t.float :december
t.float :dollar_per_sqft
t.float :dollar_per_unit
t.integer :rp_acc_code
t.integer :rp_property_id
t.integer :real_estate_property_id
t.timestamps
end
add_index :temp_ptps, [:source_id, :source_type]
end
end
It looks like create_table yields an ActiveRecord::ConnectionAdapters::TableDefinition class. This class does not contain the method index. Instead, change_table appears to yield an ActiveRecord::ConnectionAdapters::Table class which includes this index method.
If you want to add an index during a create_table migration, try this:
class CreateStatistics < ActiveRecord::Migration
def self.up
create_table :statistics do |t|
t.string :name
t.integer :item_id
t.integer :value
t.text :desc
t.timestamps
end
add_index :statistics, :name
add_index :statistics, :item_id
end
def self.down
drop_table :statistics
end
end