I've been wondering if there is any better way to handle one-to-one relations with Rails.
I have a User model, which has_one Subscription. When the user is logged in, I get the subscription by retreiving current_user.subscription, so that finding subscriptions by id in URL is unncessary.
Right now, when user wants to update his subscription, he gets the url:
/subscription/3/?plan_id=2
But the subscription 3 is unncessary, and the other thing is I don't want to be showing the number of subscriptions (ids) to the user.
Whats would be a better solution for this? Would you guys bother with that?
Thanks
You could use resource :subscriptions (not resources). As described here (link)
the difference is, that resource doesn't create an index route (only if you say explicitly that you want one) and doesn't need IDs in the url
Quote from the linked answer:
At a high level, the intent of resource is to declare that only one of
these resources will ever exist. For example:
resource :profile, :only => [:edit, :update] As a user, I should only
be able to update my own profile. I should never be able to edit other
users' profiles, so there's no need for a URL scheme like
/users/1/profile/edit. Instead, I use /profile/edit, and the
controller knows to use the current user's ID rather than the ID
passed in the URL (since there is none).
That's why you don't get an index action with resource: there's only one resource, so > there's no sense in "listing" them.
I guess You have something like this defined in Your routes
resources :subscriptions
What You want is a collection level custom action which is defined like this:
resources :subscriptions do
get 'new_plan', on: :collection
end
Now You will need to add an action in You controller of the same name. And the link to get there will be: /subscription/new_plan?plan_id=2
Related
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.
I'm trying to understand the best way to architect a fairly simple relationship. I have a Job Model and a Category Model with a has_many relationship between them in a JobCategories model.
I'd like to have a page that lists all Jobs for a specific Category. Should the logic to pull this data be on the Category Controller (on the show action), or should I create a category method on the Job Controller? My gut tells me it should be on the Category side because a Category has Jobs, but it doesn't feel right that a Job would have the logic to pull all the Jobs for a given category.
Having said that, if I want the URL to be something that is more Job specific like:
domain/jobs/:id/{category-name} (for SEO purposes)
How would I structure the route so that it reads like the above, as opposed to
domain/categories/:id
which is what you'd get with resources :categories, only: [:show]
Thanks!
For a pretty slug, I'd suggest using FriendlyId on your categories model.
As for the routes, you will not get the desired route using resources :categories
One way to do it would be
resources :jobs, only: [] do
member do
get '/:slug' => 'categories#some_action'
end
end
the slug will be passed in your parameters.
This will yield a route like this
GET /jobs/:id/:slug(.:format) categories#some_action
UPDATE
the :slug is just an example for pretty url.
In your case you'd want to have :category_name. That would be passed into your controller through the params[:category_name].
One thing that I did start thinking when I re-read your question is that you want to show a list of jobs for a specific category. A url path like /jobs/:id/{category-name} shouldn't actually show a list of jobs as you are specifying an id which means a specific job. I think the url that you're looking to get is more along the lines of /jobs/{category-name}. Am I correct?
UPDATE 2
I suggest you read this Ruby On Rails Routing
UPDATE 3
Since you did want an url more like /jobs/{category-name}
You're routes should look like this
resources :jobs, only: [] do
collection do
get '/:category_name' => 'categories#some_action'
end
end
Good luck with your project! :D
what is the difference between resource and resources in rails routing
resource :geocoder
and
resources :posts
What is real difference between them ?
In essence, routing resources is when resources gives action abilities to a controller.
http://guides.rubyonrails.org/routing.html#specifying-a-controller-to-use
If a pluralized resources is used as a way to handle generic requests on any item, then a singular resource is a way to work on the current item at hand.
So in other words, if I have a collection of Apples, to retrieve a specific apple, I'd have to tell the router "Apples" what apple to retrieve by sending the ID of the apple. If I already have one Apple, then an ID is not needed.
Notice the differences between the two by looking at what actions (or routes) they have:
resources: Index, new, create, show, edit, update, destroy
resource: new, create, show, edit, update, destroy
In your example:
The controller "geocoder" is a singular resource that you can use to edit, create, update, etc.
The controller "posts", is a plural resource that will handle incoming generic posts that you can index, edit, create.. etc
Singular Resources:
Sometimes, you have a resource that clients always look up without referencing an ID. For example, you would like /profile to always show the profile of the currently logged in user.
Or, Normally your currently logged-In user belongs to a single organization, so to goto his/her organization profile page there can be two routes
#1
/organizations/:id
#2
/organization #simply
Here, the later implementation makes more sense; isnot it? you get the organization object from association
# in organizations#show
#organization = current_user.organization
To define such singular resource you use resource method: Example
# in routes.rb
resource :organization
creates six different routes in your application, all mapping to the Organizations controller:
whereas, you define plural resources using resources method
resources :organizations
http://guides.rubyonrails.org/routing.html#singular-resources
Sometimes, you have a resource that clients always look up without
referencing an ID. For example, you would like /profile to always show
the profile of the currently logged in user. In this case, you can use
a singular resource to map /profile (rather than /profile/:id) to the
show action.
A good way to see it is that resource does not have an index action, since it's suppose to be just one.
i think just the index view.
also there have been reported issues with routing with the resource helper and form helpers. personally, i use the syntax:
resources :someresource, except: :index
in order to avoid the reported bugs.
Situation: I have a team model, a user model and a teamate model for the users of a team.
Say I want to have a view that contains the information of a team team/show
and that I wish (to simplify the user's experience) to add a list of the users, an add user to team and the possibility to remove a user from that team.
To be perfectly restful, I would need a controller (let's call it Teamates), it would handle the users of a team.
I would have all the CRUD needed.
Is it clean to have the team/show view call the teamates controller for the following actions: adduser, removeuser, listusers.
What I am trying to achieve is less clicks for the users.
In other words, I would like the user to be able to manage the users of a team from the team view instead if requireing him to navigate even further.
I don't think you need a controller for teamates.
And you really should not have adduser/removeuser/etc actions in your team controller!
You could set up your routes like that:
resources :teams do
scope :module => "team_scope" do
resources :users
end
end
Then you would have a UsersController in app/controllers/team_scope/users_controller.rb
To create a new user for a team, you would post to: /team/1-team-a/users and it would hit the create action in the UsersController above.
When you use scope in your routes, it does not change the route helpers like with namespace. The new action would just be accessible via new_team_user_path(#team).
Hum... so yeah, in this case I would have a TeamatesController, and maybe set up my routes like that:
resources :teams do
resources :teamates, :only => [] do
collection do
get :edit
put :update
end
end
end
And then you could edit the associations between a team and its players...
Your form would post the users id to team_teamates_path(team)...
But I'm really not sure it's the best way, I'd have to think about it. This is not really restful as well.
I have a User model with the usual information (login, email, name, location, etc). However, when users decide to edit their information, I'd like to separate the fields to be edited according to the appropriate concerns.
For example, I'd like to have Name, Bio and Location to be edited on a Profile page or tab, and login, email and password to be edited on an Account page or tab.
What are the best practices, and the safest way, to accomplish that? Should I have two separate model/resources: User and UserProfile? Or can I just create something like a profile method in the UserController, with a custom form with only the specific profile fields, and link to it in the user page? I'm really confused on how to go about this.
Thanks in advance for any ideas you might have.
I think it depends on the rest of your Application. It sounds like in your case it's good to be modular. If you want users to be able to see the profile of other users, it's an advantage to have a separate model for the Profile and like it with a has_one relationship. I'd just call the class Profile so it can be accessed through user.profile in your controllers and views.
Models:
class User < ActiveRecord::Base
has_one :profile, :dependent => :destroy
end
class Profile < ActiveRecord::Base
belongs_to :user
end
If all your users have both profile and account fields then I wouldn't put it in seperate models. It will only add unnecesary complexiety to your forms and add may add some sql queries.
I don't see here any security problems. In case of editing, both actions (edit account and profile) should be protected the same way - so only owner user should be able to edit both of them. If he want to "hack" it and edit also his login when he edits his first name then it is his problem. It won't cause any problem since he is allowed to edit both fields.
According to views that are viewable for other people: just don't display there any fields that belongs to account part.
How to seperate it? For me the cleanest way is to add this kind of routes:
map.resources :accounts
map.resources :profiles
And use paths like /accounts/34/edit to edit account part and /profiles/34/edit to edit profile part.
In this case you will need a seperate controller for both routes: accounts_controller.rb and profiles_controller.rb. If they share a lot of similar methods and behavior, you can add it in users_controller.rb and in profiles and accounts controllers inherit from it.
You can also do it with one controller:
map.resources :accounts, :controller => 'users'
map.resources :profiles, :controller => 'users'
But I don't know how to pass some additional value with routes created with resources. When you use connect you can pass it with :defaults => {:foo => 'bar'} and then it will be availble in controller as params[:foo] but it doesn't seem to work with resources. So how can you distinguish between accounts and profiles? You can read it from current url (here is example). And then in controller you can render different views according to requested resource.
I would be inclined to cut along the grain here. It's going to be easier to map a model to a form. So if you have information that is accessed within different forms in your UI, I would create a model for each.
That said, at a certain point in my systems I tend to pull the profile information out of the base User class, so the User becomes solely for authentication.