I must create page with top100 photos by visits, by comments... but i dont know how to make it. Anybody help me please :)
Error:
undefined method `Visit' for <Photo::ActiveRecord
photos_controller
....
def top100
#photos = Photo.all
#photos.visit.count(limit: 10)
end
routes
get 'photos/top100'
schema
create_table "visits", force: true do |t|
t.integer "photo_id"
t.integer "user_id"
t.integer "count", default: 0
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "photos", force: true do |t|
t.string "name"
t.text "description"
t.integer "user_id"
t.string "image"
t.string "tags"
t.string "camera"
t.string "lens"
t.hstore "settings"
t.integer "im_rating", default: 0
t.integer "im_visits", default: 0
t.integer "im_votes", default: 0
t.boolean "is_ban_comments", default: false
t.datetime "created_at"
t.datetime "updated_at"
end
photo.rb
has_many :visits, dependent: :destroy
visit.rb
belongs_to :photo
belongs_to :user
Use SQL grouping and ordering:
# Top photos by visits
#top_photos = Visit.select("photo_id, COUNT(*) AS visit_count").includes(:photo).group(:photo_id).order("visit_count DESC").map(&:photo)
More on ordering by count in SQL: SQL Order By Count
Notice #includes(:photo) – that way the following #map(&:photo) call won't produce N queries (just a single one to fetch them all).
Additionally, it seems you didn't specify relationship in your models, so add this line to visit.rb:
belongs_to :photo
and this line to photo.rb
has_many :visits
unles you have them there already.
Related
I have a many-to-many association throught RoomsUsers model and in this model i have a role field, association works well but i can't access this field.
My schema looks like:
create_table "messages", force: :cascade do |t|
t.text "body"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.integer "room_id"
end
create_table "rooms", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "rooms_user_id"
end
create_table "rooms_users", force: :cascade do |t|
t.integer "user_id"
t.integer "room_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "role"
t.integer "last_checked"
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "password_digest"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "mail"
t.integer "rooms_user_id"
end
User model:
class User < ApplicationRecord
has_secure_password(validations: false)
has_many :messages
has_many :rooms_users
has_many :rooms, through: :rooms_users
accepts_nested_attributes_for :rooms_users
attr_accessor :register, :mail_confirmation, :login
end
Room model:
class Room < ApplicationRecord
has_many :rooms_users
has_many :users, through: :rooms_users
accepts_nested_attributes_for :rooms_users
has_many :message
end
RoomsUsers model:
class RoomsUsers < ApplicationRecord
belongs_to :user
belongs_to :room
end
And i am trying to get role field from first user's room.
User.first.rooms.first.role
It give's me NoMethodError (undefined method `role' for #). What's wrong?
You're trying to access role field in the rooms table, but it is in rooms_users table. Should be:
User.first.rooms_users.first.role
And remove rooms_user_id from rooms and users table, you don't need it
If you want to access "role" field through Rooms model, you will need to change the place of your "role" field from rooms_users table to rooms table. Doing it you can access "role" using User.first.rooms.first.role.
However if you want to keep "role" field in rooms_users table, so you will need to use User.first.rooms_users.first.role as Vasilisa has already mentioned.
t.integer "rooms_user_id" are not necessary in rooms and users tables. The has_many used in rooms and users are already linking rooms_users with them.
I am making a Band application where a Venue has many Events and Bands through Events.
I realized that in my form for creating an event can only hold one band_id
but I want to have many bands because it only makes sense to do so.
This is my Schema
ActiveRecord::Schema.define(version: 20170817180728) do
create_table "bands", force: :cascade do |t|
t.string "name"
t.string "genre"
t.string "image"
t.boolean "explicit_lyrics"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "events", force: :cascade do |t|
t.string "name"
t.text "date"
t.boolean "alcohol_served"
t.string "image"
t.integer "venue_id"
t.integer "band_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "venues", force: :cascade do |t|
t.string "name"
t.string "city"
t.string "state"
t.boolean "family_friendly"
t.string "image"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
These are my models
class Venue < ApplicationRecord
has_many :events
has_many :bands, through: :events
end
class Event < ApplicationRecord
belongs_to :venue
belongs_to :band
end
class Band < ApplicationRecord
has_many :events
end
I am fairly new to rails this is a practice web app. I want to be able to be able to show multiple band_ids to my event.
Would I just keep repeating t.band_id in my form??
You'll want to specify a foreign key relationship in your migration that reflects the Active Record associations you've set up in your models using belongs_to instead of a data type. This way, you'll get a references from one table to another, or in your case, from one table to two others, which is how one table with two one-to-many relationships is set up.
class CreateEvents < ActiveRecord::Migration
def change
create_table :venues do |t|
t.string :name
t.string :city
t.string :state
t.boolean :family_friendly
t.string :image
t.timestamps
end
create_table :bands do |t|
t.string :name
t.string :genre
t.string :image
t.boolean :explicit_lyrics
t.timestamps
end
create_table :events do |t|
t.belongs_to :venue, index: true # Look here!
t.belongs_to :band, index: true # and here!
t.string :name
t.text :date
t.boolean :alcohol_served
t.string :image
t.timestamps
end
end
end
I have a problem, I want to create an hashtags system, but when I run my code, and when I want to create a travel that contain hashtags I have this error :
ActiveRecord::StatementInvalid in TravelsController#create
Could not find table 'tags_travels'
Here is my travel.rb
class Travel < ApplicationRecord
has_many :posts
belongs_to :user
has_and_belongs_to_many :tags
#after / before create
after_create do
travel = Travel.find_by(id: self.id)
sh = self.hashtags.scan(/#\w+/)
sh.uniq.map do |s|
tag = Tag.find_or_create_by(name: s.downcase.delete('#'))
travel.tags << tag
end
end
before_update do
travel = Travel.find_by(id: self.id)
travel.tags.clear
sh = self.hashtags.scan(/#\w+/)
sh.uniq.map do |s|
tag = Tag.find_or_create_by(name: s.downcase.delete('#'))
travel.tags << tag
end
end
end
my tag.rb
class Tag < ApplicationRecord
has_and_belongs_to_many :travels
end
the schema.rb file (just table concerned) :
create_table "tags", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "topics", force: :cascade do |t|
t.string "title"
t.string "text"
t.string "end_date"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "post_id"
end
create_table "travels", force: :cascade do |t|
t.string "name"
t.string "trip_type"
t.string "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.string "hashtags"
end
create_table "travels_tags", id: false, force: :cascade do |t|
t.integer "travel_id"
t.integer "tag_id"
t.index ["tag_id"], name: "index_travels_tags_on_tag_id"
t.index
["travel_id"], name: "index_travels_tags_on_travel_id"
end
Someone has a solution ? Thank !
Rails looks for join tables in a specific syntax. Its trying to find tags_travles but uouve created it with travels_tags.
Change your model associations to specify the join table.
has_and_belongs_to_many :travels, :join_table => :travels_tags
And
has_and_belongs_to_many :tags, :join_table => :travels_tags
Heres some info from the docs to help explain the defsult behaviour for join table naming.
"By default, the name of the join table comes from the union of the first two arguments provided to create_join_table, in alphabetical order."
http://edgeguides.rubyonrails.org/active_record_migrations.html#creating-a-join-table
In this app I have three models - Players, Teams and Rounds.
class Player < ActiveRecord::Base
has_and_belongs_to_many :teams
end
class Team < ActiveRecord::Base
has_and_belongs_to_many :players
belongs_to :round
end
class Round < ActiveRecord::Base
has_many :teams
end
In each round, a different amount of teams will be generated based on the number of players that are sent in (Players are to be persistent, but in each round you can select the ones that will actually play this time around). Once the teams are created, they are populated with the list of players for this round.
In my RoundsController i've setup a method which gets called in the create action for a new Round.
def createTeams
totalPlayers = #players.length
noOfTeams = totalPlayers/5
while (noOfTeams > 0) do
team = Team.new
team.round = #round
team.save
noOfTeams -= 1
end
i = totalPlayers - 1
while (i >= 0) do
#round.teams.each do |team|
player = #players.at(i)
player.team = team
player.save
i -= 1
end
end
At the moment I can't actually set the team's round to the current round, I get a 'can't write unknown attribute round_id' error. Am I going about this the right way? Have I setup my relationships correctly? I thought I would be able to do something like #round.teams.build(team) or #round.teams.add(team)... Any help appreciated, sorry if this is a duplicate but I've been searching around for a while without any luck.
Here's the Schema:
ActiveRecord::Schema.define(version: 20150310124456) do
create_table "players", force: :cascade do |t|
t.string "first_name"
t.string "surname"
t.string "email"
t.string "phone"
t.integer "goals"
t.integer "clean_sheets"
t.integer "wins"
t.integer "draws"
t.integer "losses"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "players_teams", id: false, force: :cascade do |t|
t.integer "player_id"
t.integer "team_id"
end
add_index "players_teams", ["player_id"], name: "index_players_teams_on_player_id"
add_index "players_teams", ["team_id"], name: "index_players_teams_on_team_id"
create_table "rounds", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "teams", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
You are missing a round_id from Team.
In a migration you can simply use
add_column :teams, :round_id, :integer
to fix this. An id like this is required if you have a belongs_to in a model.
And to add to a round
#round.teams << team
I have to select only program schedule where it belongs to a channel with ctype = TV
but I get this error:
Program (Model)
class Program < ActiveRecord::Base
has_many :program_schedules, dependent: :delete_all
ProgramSchedule (Model)
class ProgramSchedule < ActiveRecord::Base
belongs_to :program
belongs_to :channel
default_scope { includes(:channel).where("program_schedules.channels.ctype NOT IN (?)", ['tv']) }
end
Channel (Model)
class Channel < ActiveRecord::Base
validates :name, :site, :ctype, :country, :presence => true
has_many :programs, :dependent => :delete_all
QUERY
#programs = Program.includes(:program_schedules).where(
"programs.ptype" => ["tv_show","movie"],
"programs.country" => #country).
select("programs.id").
group("programs.id, program_schedules.id, channels.id").
having("program_schedules.stop > ?", Time.current)
Schema.rb
create_table "channels", force: true do |t|
t.string "site"
t.string "name"
t.string "icon"
t.string "ctype"
t.string "country"
t.string "lang"
t.integer "ordertab"
t.datetime "created_at"
t.datetime "updated_at"
t.boolean "homepage"
end
create_table "programs", force: true do |t|
t.string "country"
t.string "title"
t.text "subtitle"
t.text "description"
t.float "official_rating", default: 0.0
t.float "rating", default: 0.0
t.string "ptype"
t.string "year"
t.string "imdb"
t.string "tmdb"
t.string "tvdb"
t.string "wiki"
t.string "poster"
t.string "backdrop"
t.string "trailer"
t.float "popularity", default: 0.0
end
create_table "program_schedules", force: true do |t|
t.integer "program_id"
t.datetime "start"
t.datetime "stop"
t.integer "channel_id"
end
Your includes should include the association not the table name in symbolic form. Try changing your default scope to the following:
default_scope { includes(:channel).where("channels.ctype NOT IN (?)", ['tv']) }
I'm posting this as an "answer", but it is intended as a comment. I am doing this so that I can illustrate my explanation better. Let me know if / when you'd like me to remove this non-answer.
I have tried to replicate your problem, and I simply cannot do so using the query and default_scope you've posted so far. Ultimately, the problem is that your LEFT OUTER JOIN condition is referencing a table that has not yet been joined. Even though it is joined later in the query, the interpreter cannot see that far ahead.
This is the part I'm referring to (formatted for readability):
...FROM "programs"
LEFT OUTER JOIN
"program_schedules"
ON "program_schedules"."program_id" = "programs"."id"
AND (channels.ctype NOT IN ...)
^^^
Problem is here. The channels table has not yet been joined.
Additionally, I have never seen a .where appending to the join condition. If you're using that method, ActiveRecord should be placing whatever you passed to the .where method in the WHERE clause, as you'd expect. For some reason, it is obviously appending your condition to the LEFT OUTER JOIN as an AND condition.
The only way I know to append conditions to the join, is by using the .joins method, something like this:
...joins(" AND program_schedules.channels.ctype NOT IN (?)", ['tv'])
That will indeed result in a query like this (truncated for brevity):
LEFT OUTER JOIN "program_schedules" ON ... AND program_schedules.channels......
In addition, the generated query in the error message states
(channels.ctype NOT IN ...)
as the failing join condition, whereas your .where condition states
program_schedules.channels.ctype
I don't see anywhere else in your question where you've written anything that looks like that part of the query in the error message. So again, it seems like we're not quite seeing everything.
It seems like we are still missing some pieces of information. Is it possible you do indeed have something like the .join condition I wrote above? Are there other default_scopes in other models that could be doing this?
I stumbled upon this StackOverflow question when I was researching a similar issue that I'm seeing in my application. I think that using default_scope with joins is just broken.
I boiled your code down to something simple that would fail, ran Program.joins(:program_schedules).to_sql and got the following sql:
SELECT "programs".*
FROM "programs"
INNER JOIN "program_schedules" ON
"program_schedules"."program_id" = "programs"."id" AND
("channels"."ctype" NOT IN ('tv'))
The problem isn't that it hasn't joined to channels yet, it is that it will never join to channels. The where clause from the default_scope got tacked into the INNER JOIN, but the join was discarded.
class Channel < ActiveRecord::Base
has_many :programs
end
class Program < ActiveRecord::Base
has_many :program_schedules
end
class ProgramSchedule < ActiveRecord::Base
belongs_to :program
belongs_to :channel
default_scope { joins(:channel).where.not(channels: { ctype: ['tv'] }) }
end
ActiveRecord::Schema.define(version: 20140331124327) do
create_table "channels", force: true do |t|
t.string "site"
t.string "name"
t.string "icon"
t.string "ctype"
t.string "country"
t.string "lang"
t.integer "ordertab"
t.datetime "created_at"
t.datetime "updated_at"
t.boolean "homepage"
end
create_table "programs", force: true do |t|
t.string "country"
t.string "title"
t.text "subtitle"
t.text "description"
t.float "official_rating", default: 0.0
t.float "rating", default: 0.0
t.string "ptype"
t.string "year"
t.string "imdb"
t.string "tmdb"
t.string "tvdb"
t.string "wiki"
t.string "poster"
t.string "backdrop"
t.string "trailer"
t.float "popularity", default: 0.0
end
create_table "program_schedules", force: true do |t|
t.integer "program_id"
t.datetime "start"
t.datetime "stop"
t.integer "channel_id"
end
end
Please take a look at: https://github.com/rails/rails/issues/12896
In activerecord-4.1.7/lib/active_record/scoping/default.rb there's a comment with the next:
# If you need to do more complex things with a default scope, you can
# alternatively define it as a class method:
#
class Article < ActiveRecord::Base
def self.default_scope
# Should return a scope, you can call 'super' here etc.
end
end
Which worked for me.