rails wrong association has_many link - ruby-on-rails

Trying to set up very basic association between Customer and Contact model.
Customer has_many :contacts
Contact belongs_to :customer
User has_many :customers
Routes
resources :customers do
resources :contacts
end
I don't want /contacts to be accessible
When I add in my views
new_customer_contacts_path
I have an error. If I have
new_customer_contact_path(contact)
it works however link to contact#show is wrong
--> it directs to customers/7/contact/2 where it should be customers/2/contact/7
Any idea?

new_customer_contact_path(contact)
This is wrong. You should pass customer to it instead of contact.
If you want to show the contact of a customer, you should use customer_contact_path(customer, contact).
For reference, go to http://guides.rubyonrails.org/routing.html and search 'Creating Paths and URLs From Objects'

You gotta tell the customer whose contact belongs_to!
Like the following:
# Customer.first and Contact.first can be exchanged to instances
# of Customer or Contact!
new_customer_contacts_path(Customer.first)
edit_customer_contact_path(Customer.first, Contact.first)
customer_contacts_path(Customer.first)

With nested routes you need to pass the objects (or at least their ids) in the order they are listed in the route. In the case of 'new' you only need to pass the parent object id as there is no id yet for the new nested object.
new_customer_contact_path(customer)

Related

Rails ActiveAdmin has_one and belongs_to causes 'Undefined Method for <PLURAL_RESOURCE>'

In Rails, I have a 'User' model and a 'Wallet' model. A 'User' has_one wallet and each 'Wallet' belongs_to a 'User'. I made a 'Show' page in ActiveAdmin to view a User's Wallet. However, going to that page returns this error:
undefined method `wallets' for #<User:0x007f...>
HOWEVER, when I update the User model to 'has_many :wallets' instead of ':has_one wallet', everything works. Here is the relevant code from my models and ActiveAdmin code:
Models:
class User < ActiveRecord::Base
has_one :wallet, dependent: :destroy
end
class Wallet < ActiveRecord::Base
belongs_to :user
end
ActiveAdmin:
ActiveAdmin.register Wallet do
belongs_to :user
actions :all, except: :destroy
show do
div do
'hello'
end
end
end
ActiveAdmin.register User do
actions :all, except: :destroy
permit_params
action_item :wallet, only: :show do
link_to('Wallet', admin_user_wallet_path(user, user.wallet.id))
end
index do...
end
Any ideas as to where I might have gone wrong?
Edit 1: updates to correct colon placement mistakes in description
Edit 2:
in response to:
Can you show your routes file? Also, can you give us the full traceback of the error message and give us the output of rake routes? I suspect that the reason it's complaining about wallets not being defined (even though you never call wallets in the above code) is that some routing is making assumptions about how the relationships look. – Glyoko 4 mins ago
My routes file contains no mention 'wallet' or 'wallets'.
My stack error more specifically looks like this:
activemodel (4.1.15) lib/active_model/attribute_methods.rb, line 435
Let me know if you need more than that.
Here's the related output from 'bin/rake routes':
admin_user_wallets GET /admin/users/:user_id/wallets(.:format) admin/wallets#index
POST /admin/users/:user_id/wallets(.:format) admin/wallets#create
new_admin_user_wallet GET /admin/users/:user_id/wallets/new(.:format) admin/wallets#new
edit_admin_user_wallet GET /admin/users/:user_id/wallets/:id/edit(.:format) admin/wallets#edit
admin_user_wallet GET
/admin/users/:user_id/wallets/:id(.:format) admin/wallets#show
admin_user_wallet PATCH /admin/users/:user_id/wallets/:id(.:format) admin/wallets#update
admin_user_wallet PUT /admin/users/:user_id/wallets/:id(.:format) admin/wallets#update
ActiveAdmin uses InheritedResources gem internally, the belongs_to method ends up inside InheritedResources.
Possible solution here
ActiveAdmin.register Wallet do
belongs_to :user, singleton: true
actions :all, except: :destroy
end
The option singleton: true makes the Wallet a singular resource for the User.
Probably, another option optional: true may be helpful if Wallet is not required for any User to present
Even though your routes may not explicitly reference wallet(s), there may be something there making assumptions about how records are related to each other.
Look at the output of rake routes, in particular:
admin_user_wallet GET
/admin/users/:user_id/wallets/:id(.:format) admin/wallets#show
When you call admin_user_wallet_path(user, user.wallet.id) it's matching the /admin/users/:user_id/wallets/:id(.:format) route. Notice how that expects both a user id and a wallet id in the path. This is a tip-off that something is off here, since if you have the user, there should be exactly one wallet associated with it. You shouldn't need to give both the user and wallet id.
Since the wallet resource is nested under users, the page where you view the user's wallet is actually more of an index page than a show. If the wallet were an independent resource, then you could have a path like /admin/wallets/:id and things would work out fine.
But since the wallet is a subresource of the user, you would ideally want a path like /admin/users/:user_id/wallet. There's no need to pass the wallet id, since you already have the user.
tl;dr: Try changing the shows to indexs and see where that gets you. e.g.
index do
div do
'hello'
end
end
# ...
action_item :wallet, only: :index do
link_to('Wallet', admin_user_wallets_path(user))
end
Okay.. So I had this same exact issue. I had a belongs_to a parent where the parent had only a has_one to the child model..... Nothing seemed to work so I decided to fake it. I am not sure if this is the best way to do this but it worked. In the parent model, add a method:
class User < ActiveRecord::Base
has_one :wallet
def wallets
Wallet.where(user_id: id)
end
end
The above code is a hotfix until I can find some other way to implement what I need.

Nested routes that do not imply resource association

Is it considered bad practice (or un-RESTful) to create nested routes for resources that otherwise have no association? For example I have:
resources :foos do
resources :bars
end
But I have no business logic elsewhere in my database or application that associates :foos with :bars.
The reason I want to do this: Many of my routes are created as resources nested under my :groups resource. I do that so that I can always grab a group_id param and always show a layout that matches the group the user is currently "in". I'm comfortable with this when the resource belongs_to the group:
/groups/1/comments/1
But when some other comment does not belong_to the group (group1) and I want to look at it through layout that is "branded" as group1, my impulse is to route it like this:
/groups/1/comments/2
Is this ok to do, maybe I'm overthinking this?
I maintain an app with similar requirements. I do something roughly along the lines of:
class User
has_and_belongs_to_many :groups
belongs_to :active_group, class_name 'Group'
def active_group
return super unless super.nil?
group = groups.first
update_columns(active_group_id: group.id)
group
end
end
The User class validates that they are assigned to one or more groups and the active_group method is overriden to provide a default if it is a first login. With this approach you will need to provide an action to set the active group so the user can switch groups (presumably this is a requirement).
This assumes that you have some kind of authentication in place to know the current user. If restricting access to groups is not a concern, you can forego the habtm relationship and substitute groups.first with Group.first.
If you don't have/want/need authentication, you could just drop a active_group_id in the session cookie. But I would definitely consider it bad practice to nest unrelated resources.

Rails Route for Adding child to parent

Been working in another language for a few months and trying to get back into Rails.
If I have a model House, and a model Door, so that Door belongs_to House and House has_many Doors.
If I want to add a door to the house, do I use the route:
add_door_path
or is it
add_door_path(#house)
And if so how do I embed the house_id in to it? Or do I need to create a new route for that? Is it part of the standard resources or is this entirely custom?
if you have
# routes.rb
resources :house do
resources :doors
end
then you would have house_doors_path(#house)
That is because in your routes you have specified that doors is nested inside the house. You would need the parameter inside because the path requires a house id.
To check for the routes, go here http://localhost:3000/rails/info/routes.
If on the other hand, if you have
# routes.rb
post '/door/' => 'doors#create', as: :add_door
the new door would not know which house it belongs to. so you can add
post '/house/:id/door ... which you also need a parameter of house so your syntax would be add_door_path(#house)
I hope that answers your question.

Rails nested resources, how do I create two new resources when I need the other's id in the GET request?

Pretty much as the title suggest. I have two models, routes like this:
resources :users do
resources :books
end
- which gives me exactly the kind of urls I'm looking for. The problem is that I need to have a logic where a not logged in user can go in and click "create book", and in the new-page chose to click "new user" or "I have an account", which via javascript loads a form for a new user with all its params, or loads a form with only email and password. How do I create this route to work? With these routes it seems I get the path /users/:user_id/books/new, but then I need the user's ID, which I don't have.
Any Ideas?
By the way. User has_many :books, Book belongs_to :user.
Just add the route without the user id:
# with user_id
resources :users do
resources :books
end
# without user_id
resources :books
It looks dangerous btw to have a user id in the URL. That means that anyone can access the the books of any user, just by changing the URL. The ID of the current user should not appear in your routes at all, but should always use cookies or sessions.

Associated models without nested routes

I have two models:
class GarageOwner < ActiveRecord::Base
has_many :garages, dependent: :destroy
end
class Garage < ActiveRecord::Base
belongs_to :garage_owner
end
A Garage should never exist without a garage owner. So in the new action of the GaragesController I need the corresponding garage owner. I do not want to use nested routes so I do not have the garage owners id as a parameter. But how do I get him then?
Update for some clarification
Garages are created by a third model (Admin). So I can not access the garage owner through the current user.
I build my routes using resources:
garage_owners GET /garage_owners(.:format) garage_owners#index
POST /garage_owners(.:format) garage_owners#create
new_garage_owner GET /garage_owners/new(.:format) garage_owners#new
edit_garage_owner GET /garage_owners/:id/edit(.:format) garage_owners#edit
garage_owner GET /garage_owners/:id(.:format) garage_owners#show
PUT /garage_owners/:id(.:format) garage_owners#update
DELETE /garage_owners/:id(.:format) garage_owners#destroy
garages GET /garages(.:format) garages#index
POST /garages(.:format) garages#create
new_garage GET /garages/new(.:format) garages#new
edit_garage GET /garages/:id/edit(.:format) garages#edit
garage GET /garages/:id(.:format) garages#show
PUT /garages/:id(.:format) garages#update
DELETE /garages/:id(.:format) garages#destroy
The solution to not using nested routes is to insert the garage_owner_id as a hidden field in your new garage form. But, you've given no indication in your question of how the new garage form is meant to know about which garage_owner it should associate with so I can't give you a specific example.
Perhaps I'm not getting your question, but I think you'd have to either pick up the garage owner from your session (e.g. logged in user), or something derived from a value in your session or as a value submitted with the form used for the new operation, in which case it would be a parameter.
You could approach this a couple different ways:
If the GarageOwer requires a login, you could grab the ID of the GarageOwer user from the cookie stored at login.
Create a custom route match "/Garages/new/:owner_id" => "garages#new", there after in your controller access the owner's id via params[:owner_id].
Add owner_id as a hidden attributed to the form on the "garages/new" page.
I found an appropriate solution which is still restful I think. I just created multiple routes for garages:
resources :garage_owners do
resources :garages, except: :index
end
resources :garages, only: [:index, :show]
Normal users should not access the other actions.

Resources