A miniature may have many contents.
class Miniature < ActiveRecord::Base
has_many :contents, foreign_key: "setmini_id", dependent: :destroy
has_many :minisets, :through => :contents, source: :miniset
has_many :reverse_contents, foreign_key: "miniset_id", class_name: "Content", dependent: :destroy
has_many :setminis, :through => :reverse_contents, source: :set mini
On a miniature's show view I currently list it's contents. What I want to do is add a default sort scope to my Contents model so that it sorts by name.
class Content < ActiveRecord::Base
default_scope { order('name ASC') }
belongs_to :miniset, class_name: "Miniature"
belongs_to :setmini, class_name: "Miniature"
My attempt here fails and complains "No such column name:".
With a normal has_many_through relationship this would work but I'm guessing because I'm using the join table in two directions and declaring class_name: "Miniature" this doesn't work here.
Is there a way I can get this list to default sort by name?
I've tried "content.name" and "setmini.name" to no avail.
You'll need the name of the database table in your order clause (not the model or association name). If you're following Rails' conventions for naming, this would be:
order('contents.name ASC')
After looking at some other answers to similar questions I eventually got it working with the following:
default_scope joins(:setmini).order('miniatures.name ASC')
Related
I'm having problems with a Rails 4 join table. I have quite a simple setup which is working elsewhere in my application using a non-conventionally named table for users, groups and usergroupmemberships. I'm trying to set it up this time using the proper conventional naming and it's just not working.
Models involved are User, ManagementGroup and ManagementGroupsUser
db tables: management_groups_user, management_groups, users
app/models/user.rb
Class User < ActiveRecord::Base
...
has_many :management_groups, through: management_groups_users
has_many :management_groups_users
....
app/models/management_group.rb
class ManagementGroup < ActiveRecord::Base
has_many :users, through: :management_groups_users
has_many :management_groups_users
app/models/management_groups_user.rb
class ManagementGroupsUser < ActiveRecord::Base
belongs_to :users
belongs_to :management_groups
The association appears to work from with #user.management_groups_users but nothing else. I'm fairly sure this is a problem with naming / plurality but I can't figure it out.
This is the model which joins the remaining models user.rb and management_group
#app/models/management_groups_user.rb
belongs_to :user
belongs_to :management_group
Since we are going to use model above to access another model management_group then
#app/models/user.rb
has_many :user_management_groups #This should come first
has_many :management_groups, through: user_management_groups
Since we are going to use model above to access another user model then
app/models/management_group.rb
has_many :user_management_groups
has_many :users, through: :user_management_groups
Now should work
Do it this way.
app/models/user.rb
has_many :user_management_groups
has_many :management_groups, through: user_management_groups
app/models/management_group.rb
has_many :user_management_groups
has_many :users, through: :user_management_groups
app/models/management_groups_user.rb
belongs_to :user
belongs_to :management_group
I hope these associations will help you.
This is another way if you pass foreign key and class name.
app/models/user.rb
has_many :user_management_groups, :foreign_key => "key", :class_name => "ClassName"
has_many :management_groups, through: user_management_groups, :foreign_key => "key", :class_name => "ClassName"
app/models/management_group.rb
has_many :user_management_groups
has_many :users, through: :user_management_groups
app/models/management_groups_user.rb
belongs_to :user, class_name: "ClassName"
belongs_to :management_group, class_name: "ClassName"
This is another way around.
It's important to realize there is a convention rails uses for HABTM and has many through. HABTM does not have a model, so it needs to infer the table name which, as others point out, is both plural and alphabetical order.
If you are doing has many through and have a model, the convention is that it wants singular first word, plural second. See examples
User has and belongs to many groups.
HABTM: table name should be groups_users
Has Many Through: table name should be user_groups (flip order is more intuitive)
Model for the latter would be UserGroup. Has many through would specify it as through: :user_groups
A user can teach many topics, and each topic can have many lessons.
A user can join lessons created by other users.
(1) is pretty simple and I implemented it as follow:
#user.rb
has_many :topics
has_many :lessons, through: :topic
#topic.rb
has_many :lessons
#lesson.rb
belongs_to :topic
belongs_to :user
(2) is a many-to-many relationships and I think a join table - let's call it matches (extra points to help me find a better name!) - is required.
If I add the following
#user.rb
.
.
has_many :lessons, through: :match
#lesson.rb
.
.
has_many :users, through: :match
#match.rb
belongs_to :lesson
belongs_to :user
I think I will get an error since Rails cannot get the difference between the two relationships while calling #user.lessons, for example.
What can be a correct approach?
p.s. I see there are many questions similar to this one but I was not able to find a proper solution to my problem.
I think you should name the join table user_lessons rather than match being a convention in Rails for join tables.
After renaming, I think you should rather have it like this:
user.rb
has_many :user_lessons
has_many :subscribed_lessons, through: :user_lessons, source: :lesson
Also note that your user
has_many :lessons, through: :topics #not :topic
Let me know if that works.
talk_groups table
field :parent_topic_id, type: Integer, default: -1
has_many :topics, :dependent => :destroy
belongs_to :parent_topic, :class_name => 'Topic', :foreign_key => :parent_topic_id
topics table
belongs_to :talk_group
The relation above works well on sqlite/mysql, but doesn't work on mongoid
because when a model can't have many and belongs_to with another same model
parent_topic.talk_group will appear Mongoid::Errors::AmbiguousRelationship: error
the problem is caused not because "when a model can't have many and belongs_to with another same model".
it's because when mongoid looks at the topic model, it can't know if the talk_group field is referring to the topics or parent_topic relation in the talk_group model.
mongoid provided an option to avoid this "AmbiguousRelationship" it's the inverse_of option...
so to solve this issue you should alter the topic model to become
belongs_to :talk_group, inverse_of: :parent_topic
and the talk_group model should become
has_many :topics, dependent: :destroy
belongs_to :parent_topic, class_name: 'Topic'
# you can add inverse_of option on this side as well although it's not mandatory
belongs_to :parent_topic, class_name: 'Topic', inverse_of: :talk_group
also refere to this question for more info.
I have a polymorphic PLACE model like this:
class Place < ActiveRecord::Base
belongs_to :placeable, polymorphic: :true
...
So, for example, in my Lodging model I have something like:
class Lodging < ActiveRecord::Base
has_one :place, as: :placeable, dependent: :destroy
...
And they work as expected.
The point is that now I want to create a CarRental model and this model has TWO places, on is the pick-up place and the other one is the drop-off place.
So I wrote:
class Transportation < ActiveRecord::Base
has_one :start_place, as: :placeable, dependent: :destroy
has_one :end_place, as: :placeable, dependent: :destroy
And of course that does not work. Any insights on how to do that?
EDIT 1: IF I DO LIKE
class Transportation < ActiveRecord::Base
has_one :start_place, class_name: 'Place', as: :placeable, dependent: :destroy
has_one :end_place, class_name: 'Place', as: :placeable, dependent: :destroy
It works! But why? Where is the start or end information saved?
** EDIT 2: NOPE, IT DOES NOT WORK **
It does not work... =(
I guess, associations spitted out errors before because of the unknown classes. has_one :start_place by default assumes you mean a class StartPlace that doesn't exist. It singularizes the term (as best as it can) and converts it to CamelCase. Once you've specified that you mean Place, it's clear.
You should be adding a new column anyways.Let's try single table inheritance (STI). Add a column type of type string to your places table:
rails generate migration AddTypeToPlaces type:string
...make sure it does what it says, then migrate and create new models like so:
rails generate model StartPlace --no-migration --parent=Place
rails generate model EndPlace --no-migration --parent=Place
Note: they don't get a dedicated table and inherit from Place, not ActiveRecord::Base. It should generate two empty classes, that's fine, they inherit things from Place.
...then revert your associations to what didn't work a while ago:
has_one :start_place, as: :placeable, dependent: :destroy
has_one :end_place, as: :placeable, dependent: :destroy
...and they should work now, because StartPlace is defined as
Place with type equal to "StartPlace"
...same with EndPlace with corresponding type.
I described this quite some time ago for a similar case.
Your schema doesn't make sense to me - that places belong to lodgings etc. Surely there's a fixed number of places, and then they can have a number of different things in them? In your schema the same place could be in the database lots of times which seems wrong. I don't think that polymorphic associations are the way to go here either.
I would model this like so:
class Place
has_many :lodgings
has_many :starting_transportations, :class_name => "Transportation", :as => :start_place
has_many :ending_transportations, :class_name => "Transportation", :as => :end_place
class Lodging
belongs_to :place #using lodgings.place_id
class Transportation
belongs_to :start_place, :class_name => "Place" #via transportations.start_place_id
belongs_to :end_place, :class_name => "Place" #via transportations.end_place_id
btw, i think "Location" is a better name than "Place" for physical locations in the real world. "Place" sounds too vague.
I have this has_many association
has_many :devices, through: :vehicles, foreign_key: :meid
I need last_device to be implemented as a has_one relation to use it with ransack gem and it can be implemented using a method like this
def last_device
devices.last
end
I've tried a few variants but I haven't managed to get it working
has_one :last_device, class_name: 'Device'
Actually I've done it by splitting it into two pieces
has_one :last_vehicle, -> { order('vehicles.created_at DESC') }, class_name: 'Vehicle'
has_one :last_device, through: :last_vehicle, source: :device, class_name: 'Device'
Don't think it's possible to do it the way you propose, 'cause you wouldn't have a correct setting method on that association.
The only thing I can think of (if you insist on working with ransack), is adding a last_device_id column to your model and then adding a relation like this:
has_one :last_device, class_name: Device
Though you'll need to update that column when a new device is added to a user's vehicle or the last one is removed.