I have a relationship between two models
Category Model
class Category < ActiveRecord::Base
belongs_to :product
end
Product Model
class Product < ActiveRecord::Base
has_many :categories
end
I have category_id in products table but When I create a new product in my products table
category_id is null. I am new to rails can anyone help please?
First off, a thought - most of the time, products have many categories, but each category also contains many products. Perhaps your association should be a many-to-many? On to your actual question.
If I understand correctly, your question is really about how to create categories and products that are related to each other in the database, i.e. how to set the value of product_id when building a new category.
For clarity in case you need it, product_id would only be set for a category. The category, after all, belongs to that product, so it has to hold its owner's ID.
So, let's say you want to build a new category that belongs to an existing product - you can do this:
# in your view, where you link from products/show.html.erb to category/new.html.erb
<%= link_to "Build new category for this product", new_category_url(:id => #product.id) %>
# you must have #product defined, and you also must have
# 'resources :categories' in your routes file
# in your controller action categories/new, set the new category's product id:
def new
#category = Category.new(:product_id => params[:id])
end
# include a hidden field to contain the product_id in your new form
<%= form_for #category do |f| %>
<%= f.hidden_field :product_id %>
... other fields, labels, etc.
<% end %>
# save the record as you normally would (analogous to the code in your comment to #Chowlett).
#category = Category.new(params[:category])
if #category.save
redirect_to :action => "list", :notice => "Category saved successfully."
else
render :action => "new"
end
The above code allows you to build a product, then each category one-by-one. So, we are building your product first, then including a link from the product/show page to your category/new form, passing in the ID of the product you want that category to be part of.
If you want to build a product and some categories at the same time, it is a bit more complicated. For more information on this, take a look at http://railscasts.com/episodes/196-nested-model-form-part-1 (this is the first of a three-part series) and https://github.com/ryanb/nested_form. I don't suggest this course of action unless you are very comfortable with the above basics. I once got mired in this code for a week when I was new to Rails!
First off, you have the _id field in the wrong table. If Category belongs_to :product, then your categories table needs a field product_id.
Look at it this way: each Product can have many Categories - so what single value would you expect to find in a category_id field?
If you still have problems after correcting that, let me know.
Edit: Once you've got your tables set up, you still need to tell Rails what the link should be. You've got a few options. Assuming you've got a Category in hand, your best bet is new_prod = my_cat.create_product(). Alternatively, you can use new_prod = Product.create(:category => my_cat).
Later on, you can associate the models together like this:
my_prod.category = my_cat
my_prod.save
Related
I have a Favorite model and an Article model. The Favorite has an attribute fav_id that is equal to the the id of Article.
I want to create a link_to #article.title and pass a param of :id=>#favorite.fav_id.
favorites controller code:
def show
#favorite = Favorite.find(params[:id])
#article = Article.find(params[:id])
end
view/favorite/show code:
<%= link_to #article.title, article_path(:controller=>:article, :id=>#favorite.fav_id, :action=>'view')
When I load the page, the favorite id is used, which is fine. I just want to pass favorite.fav_id to the #article = Article.find(params[:id]). That way, the link will show the article title instead of a number (fav_id). In the future id like to be able to show #article.description and other attributes too.
I have also considered passing the article attributes to favorite, but this seems like itd be a heavier load on the database so I've so far avoided that
I've also tried a :through => association. Maybe I did it wrong, but :through didn't work when I tried it. Any suggestions?
Rails 4.2.0
Ruby 2.1.5
If your Favorite model has an attribute, pointing to another model (Article), that means, by definition, that there's an association between those models. So you should explicitely set that association in your models.
First of all you need to define the association type. In your case it an be either a has_one association in case an article may correnpond to only one favorite or a has_many association if one article may correspond to multiple favorits (multiple favourities can have the save fav_id).
Then you need to set up your association in both the Article and the Favorite models.
In your app/models/article.rb it will go like this:
has_one :favorite, foreign_key: :fav_id
In your app/models/favorite.rb it will go like this:
belongs_to :article, foreign_key: :fav_id
I recommend you though to simplify that by changing the field name from fav_id to article_id so that you can drop the foreign_key parameter in both definitions.
Make sure to read the API docs for more information on working with associations.
After you set up that association, you can easily use it in the controller.
def show
#favorite = Favorite.find params[:id]
#article = #favorite.article
end
And then just use link_to #article.title, #article in your view.
I cant clearly understand what you are going to get, but this:
`
def show
#favorite = Favorite.find(params[:id])
#article = Article.find(params[:id])
end
Is completely wrong. You are trying to use same :id parameter to find either Article and Favorite. I don`t think this is behavior you are waiting for.
The Favorite has an attribute of .fav_id
Also, why Favorite model has fav_id attribute? Is it Single Table Inheritance? Or what? Rails way would be, for example, Favorite to have article_id.
I'm creating a restaurant app that will list restaurants as a rails starting project. For now, I want the restaurant to have one menu.
class Restaurant < ActiveRecord::Base
has_one :menu
end
class Menu < ActiveRecord::Base
belongs_to :restaurant
end
I've also added a migration for restaurant_id on the menu by running rails generate migration AddRestaurantIDToMenus restaurant_id:integer and ran rake db:migrate
My problem now is how do I associate a new menu to a restaurant? When I create a new menu post it does not automatically associate with a restaurant. Is there anyway I can create a menu directly from the restaurant's show page? What would I need to set up in the restaurant controller area for this?
First, when you do a migration of a model that belongs to another model, don't add the foreign key manually, instead use references for the column type, like this
rails g migration AddRestaurantRefToMenus restaurant:references
Now, when you want to create a menu that belongs to an instance of Restaurant, you can do something like this in your controller:
#menu = #restaurant.menus.create(some_attribute: some_value, another_attibute: another_value)
Definitely read through the rails guides on associations.
Oh my... You have so much to learn...
First of all – it sounds like you're still in the early stages of your app, so unless you have a strong reason to keep any data that's already in your database, there's no real reason to create a whole new migration just to add the restaurant_id to your Menu table. Just rake db:migrate VERSION=0, edit your create_menus migration, and then `rake db:migrate' up again.
As far as actually creating a menu and associating it with a restaurant, you can do it any number of ways:
Restaurant or Menu controller:
def create_menu
#restaurant = Restaurant.find(params[id])
#menu = Menu.create(:restaurant_id => #restaurant.id, :other_attribute => "something")
#Or...
#restaurant.menu = Menu.create(:restaurant_id => #restaurant.id, :other_attribute => "something")
#Or...
#menu = Menu.new(:other_attribute => "something")
if #menu.save #returns true if saved properly
#restaurant.menus << #menu
end
end
With regards to how to do both together, you could say in your Restaurants model
accepts_nested_attributes_for :menu
Then in the new of your controller you would have something like
#restaurant = Restaurant.new
#restaurant.menu = Menu.new
Your new view would now look something like
form_for (#restaurant) do |r|
<%= r... %> #whatever they can set for the restaurant (like a name maybe)
#
r.fields_for :menu do |m|
<%= m... %> #whatever they can set for the menu
end
r.submit "button"
end
Lastly create in your controller would look something like
#restaurant = Restaurant.new(params[:restaurant])
if (#restaurant.save)
...
else
...
end
You can do this with edit/update almost the exact same way (the main difference there being that your restaurant, and perhaps its menu, already exist) so your edit would look something like
#restaurant.find(params[:id])
then in your update it would look like
#restaurant.find(params[:id])
if #restaurant.update_attributes(params[:id])
...
else
...
end
Hope that helps answer some questions you have.
p.s. Here is a good place to start if you have questions about form_for and the different inputs.
I'm having some trouble creating a new relation in my has_and_belongs_to_many model. I defined the models like this:
journals model
has_and_belongs_to_many :posts
post model
has_and_belongs_to_many :journal
I don't know how create the association , I made a button but I don't know how it works. I created the action add_post
def add_post
#journal_post = JournalsPosts.new
end
I make this link to create the association but I don't know what I have to do now:
<%= link_to "Add to Journal",:controller => "journals",:action => "add_post" %>
The redirect works correctly but I don't know how to proceed now ? Do you know about some guide to HABTM associations? I have already tried this, but it didn't help.
After researching this myself, you should be able to do
def add_post
j = Journal.first # or find_by, etc
p = Post.first # or find_by, etc
j.posts << p # creates a record in journals_posts table
j.save!
end
(1) The accepted answer made it sound as though the association could only be made directly. Also you wouldn't have a "JournalsPosts" class if you're using the habtm association, since it specifically avoids using a model for the intermediary table.
(2) Note that this association will not be unique. If you call this multiple times, you'll get multiple entries in the journals_posts table with the same two integer pairs.
You should highly consider using has_many, :through as that's the preferred way to do these kinds of relationships now in Rails.
That said, if you want to continue with has_and_belongs_to_many, you need to somehow get the journal and post ids that you want to associate so you can correctly create the association.
In your routes:
resources :journals do
member do
put :add_post
end
end
In your view (make sure you set #journal and #post somewhere):
<%= link_to "Add to Journal", add_post_journal_path(#journal, :post_id => #post.id), :method => :put %>
In your controller:
def add_post
#journals_posts = JournalsPosts.new(:journal_id => params[:id], :post_id => params[:post_id])
if #journals_posts.save
...
else
...
end
end
I have a simple cart system that I have been working on for a little while for an application and am needing a little help in trying to figure out how to update a particular attribute in a join table (Between Order and Products).
Here is the code:
def add_product_to_cart
#product = Product.by_client(current_client).first
#order = current_order
unless #order.products.exists? :id => #product.id
#order.products << #product
end
end
I am trying to update a particular attribute when I update the #order.products...
This is what I am trying to do:
#order.products << #product --> When this happens I need to update a :price attribute..
Anyway of doing this?
class Order
has_many :products
def price
products.sum(:price)
end
end
Just off the top of my head. Here's the sum reference:
http://ar.rubyonrails.org/classes/ActiveRecord/Calculations/ClassMethods.html#M000296
Desire to put attributes into join table may be a sign of missing model. You can promote join table into model, say OrderItem, by adding primary key to it. HABTM associations in Order and Product then become has_many through associations. The new model would be a good place for setting up callback which populates price attribute. It can also unlock additional benefits, like time-stamping items and making them act_as_list, etc.
I have 2 equal-access models: Users and Categories
Each of these should have the standard-actions: index, new, create, edit, update and destroy
But where do I integrate the associations, when I want to create an association between this two models?
Do I have to write 2 times nearly the same code:
class UsersController << ApplicationController
# blabla
def addCategory
User.find(params[:id]).categories << Category.find(params[:user_id])
end
end
class CategoriessController << ApplicationController
# blabla
def addUser
Category.find(params[:id]).users << User.find(params[:user_id])
end
end
Or should I create a new Controller, named UsersCategoriesController?
Whats the best practice here? The above example doens't look very DRY.... And a new controller is a little bit too much, I think?
Thanks!
EDIT:
I need to have both of these associations-adding-functions, because f.e.
#on the
show_category_path(1)
# I want to see all assigned users (with possibility to assign new users)
and
#on the
show_user_path(1)
#I want to see all assigned categories (with possibility to assign new categories)
EDIT:
I'm taking about a HBTM relationship.
If you have a situation where you need to do this with has_and_belongs_to_many, you could take the approach you are currently using, or you could build this into your existing update actions.
When you add a habtm relationship, you will get an additional method on your classes...
class User < ActiveRecord::Base
has_and_belongs_to_many :categories
end
With this, you can do this:
user = User.find(params[:id])
user.category_ids = [1,3,4,7,10]
user.save
The categories with those ids will be set. If you name your form fields appropriately, the update can take care of this for you if you want to use checkboxes or multiselect controls.
If you need to add them one at a time, then the methods you've built in your original post are reasonable enough. If you think the repetition you have is a code smell, you are correct - this is why you should use the approach I outlined in my previous answer - an additional model and an additional controller.
You didn't mention if you are using has_and_belongs_to_many or if you are using has_many :through. I recommend has_many :through, which forces you to use an actual model for the join, something like UserCategory or Categorization something like that. Then you just make a new controller to handle creation of that.
You will want to pass the user and category as parameters to the create action of this controller.
Your form...
<% form_tag categorizations_path(:category_id => #category.id), :method => :post do %>
<%=text_field_tag "user_id" %>
<%=submit_tag "Add user" %>
<% end %>
Your controller...
class CategorizationsController < ApplicationController
def create
if Categorization.add_user_to_category(params[:user_id], params[:category_id])
...
end
end
then your categorization class...
class Categorization
belongs_to :user
belongs_to :category
def self.add_user_to_category(user_id, category_id)
# might want to validate that this user and category exist somehow
Categorization.new(:user_id => user_id, :category_id => category_id)
Categorization.save
end
end
The problem comes in when you want to send the users back, but that's not terribly hard - detect where they came from and send them back there. Or put the return page into a hidden field on your form.
Hope that helps.