Rails :: in nested model form, update belongs_to "parent" attributes - ruby-on-rails

I am building a Rails application and I am in front of an issue with form I can't fix.
I have an order model which belongs_to customer so when I build the form I do #order = #customer.orders.build
This works well to save orders attributes but I also want to update customer attributes as well which never work.
How can I save order and update "parent" customer attributes in the same process ?
Thanks for your help !
Edit:
Customer model:
class Customer < ActiveRecord::Base
has_many :orders
end
Order model:
class Order < ActiveRecord::Base
belongs_to :customer
accepts_nested_attributes_for :customer
end
My project:
Ruby On Rails 4.2.6 / Ruby 2.2.2
Devise 3.5.9
Simple form 3.1.0

You should update your customer in your create method. How about:
def create
#order = current_customer.orders.build order_params
if #order.save
#order.customer.update_attributes(order_params[:customer_attributes])
...
else
...
end
end

In your update action, you can do something like this:
def update
#customer = Customer.find(params[:id])
if #customer.update_attributes(customer_params.merge({ order_attributes: order_params}))
render #customer
end
end
Where customer_params and order_params are methods that use strong params to whitelist parameters sent by the form.
Obviously, I haven't tested the code, but ti should give you some direction.
Hope that helps!

Related

Rails update product quantity

I followed Ryan Bates screencasts using paypal standard payments and i basically now have to send checkout info from the Cart model.
I'm a bit stuck with trying to update product quantity after the transaction is completed.
I tried using a callback but to no avail. Any help would be appreciated
The closest i got was to use this update quantity callback but for some reason, it is updating the wrong cart. Not sure whether it picks up the wrong line item or its when it checks the cart it goes wrong
class PaymentNotification < ActiveRecord::Base
belongs_to :cart
serialize :params
after_create :mark_cart_as_purchased, :update_quantity
private
def mark_cart_as_purchased
if status == "Completed"
cart.update_attribute(:purchased_at, Time.now)
end
end
def update_quantity
#line_item = LineItem.find(params[:id])
#line_item.upd
end
end
Line Item Class
class LineItem < ActiveRecord::Base
belongs_to :order
belongs_to :product
belongs_to :cart
belongs_to :stock
after_create :stock_stat
def total_price
product.price * quantity
end
def upd
if cart.purchased_at
product.decrement!(quantity: params[:quantity])
end
end
end
The params hash is only available in the controller. You cannot access it in the model. You have to pass params[:quantity] to the upd method as a method parameter as such:
def update_quantity
#line_item = LineItem.find(params[:id])
#line_item.upd(params[:quantity])
end
def upd(quantity)
if cart.purchased_at
product.decrement!(quantity: quantity)
end
end
Also, you should consider using Time.current instead of Time.now in order to account for the time zone your application is configured with in application.rb unless you just want to use whatever time is local.

How pass params and save in db

have the relationships:
(my code is in portuguese)
Order
class Pedido < ActiveRecord::Base
belongs_to :pessoa
Person
class Pessoa < ActiveRecord::Base
belongs_to :usuario
has_many :enderecos
has_many :pedidos
accepts_nested_attributes_for :enderecos
end
User
class Usuario < ActiveRecord::Base
has_many :pessoas
has_many :pedidos, through: :pessoas
end
carrinhos_controller.rb
def checkout
#pedido = current_usuario.pedidos.build
end
In migration Person have usuario_id, Order have pessoa_id and others...
When I finish an order, the pessoa_id is null and does not save on database, why??
More codes:
pedidos_controller.rb
class PedidosController < ApplicationController
before_action :authenticate_usuario!
# Criar pedido
def create
#pedido = current_usuario.pedidos.build(pedido_params)
if #pedido.save
#pedido.construir_cache_item_carrinho(carrinho_atual)
#pedido.calcular_total!(carrinho_atual)
carrinho_atual.limpar!
#OrdemDeServico.new(carrinho_atual, #pedido).encomendar_pedido!
redirect_to pedido_path(#pedido.token)
else
render "carrinho/checkout"
end
end
Use this code:
def checkout
#pedido = current_usuario.pedidos.build
#pedido.save
end
If you are using build or new, then you have to use save method after that. Otherwise you can use direct create method.
build does not save to the DB. Either save afterwards (#pedido.save) or try #pedido = current_usuario.pedidos.create
I suppose you are using something like a nested form to post the parameters. I often use the nested_form gem by Ryan Bates.
If you look careful at the usage you can find some useful insights.
Also take care of the strong_parameters: it is required to declare in the controller which params you want to permit.

Saving association I.D upon creation?

If I have a user that has_many user_logins and a user_logins that belongs to user - When a user_login is created I'm using UserLogin.create(userlogin_params) and then my strong params permits the user_id column - but this alone is not saving the current users I.D to the column as it is coming out as nil.
How do I make it save the I.D?
User model:
has_many :user_logins
UserLogin model
belongs_to :user
accepts_nested_attributes_for :user
UserLoginController:
...
def create
#user_login = UserLogin.new(user_login_params)
...
end
...
def user_login_params
param.require(:user_login).permit(
:user_login_attribute1,
:user_login_attribute2,
user_attributes: [
:id,
:user_attribute1,
:user_attribute2
]
)
end
Tell me if it helps.
There are 2 issues here at hand.
First: How do you create an association with the parent record automagically there?
Second: How do you do this so your controller action isn't a giant hole waiting for a hacker to stick their nose in.
You need to start from the parent, then build the child, not start with the child and build the parent.
Consider the following:
class User < ActiveRecord::Base
has_many :logins, class_name: "UserLogin"
end
class UserLogin < ActiveRecord::Base
belongs_to :user
end
class UserLoginsController < ApplicationController
def create
if new_user_login(user_login_params).save
redirect_to :wherever
else
render :new
end
end
private
def new_user_login(attrs={})
current_user.logins.create(attrs)
end
def user_login_params
param.require(:user_login).permit(:attr_1, :attr_1)
end
Do not pass IDs into any secure params hash unless that ID is selectable by the user. If you allow an ID into secure params, a hacker can start moving records around to other objects and destroy your database integrity.
If you would like pairing help on this problem live and in person, you can check out my codementor profile at https://codementor.io/rubycasts/#reviews

Rails many to many model, adding records

Hello im trying to add some records to my database with this model
class Colleagueship < ActiveRecord::Base
belongs_to :employee
belongs_to :colleague, :class_name => 'Employee'
end
class Employee < ActiveRecord::Base
has_many :colleagueships
has_many :colleagues, :through => :colleagueships
# ...
end
but i have no idea in how to start a new form to create new records
im thinking to try something like
def new
employee = ## gotta get the id here in the form
#colleagueship = employee.colleagueships.build(:colleague_id => params[:colleague_id])
#colleagueship.save
end
what do you think? how do i achieve this with a post http method? do i have to save the employee variable with the request and add the employee_id there?
In the controller
def new
end
def create
# inspect submitted params here
puts params
if colleagueship.save
# etc etc
else
# error
end
end
private
def employee
#employee = Employee.find_by(params[:employee_id])
end
def colleagueship
#colleagueship = employee.colleageships.build
end
helper_method :employee, :colleagueship
Your routes should be nested to provide the key you'll use to find the employee.
resources :employees do
# this will generate /employees/:employee_id/colleagues/:id
resources :colleagueships
end
In your view, you will probably use the form_tag helper, as it's easier to customize forms with whatever fields you want, especially if you're avoiding accepts_nested_attributes which you should. You can also include a hidden_field_tag with employee_id if you aren't nested your routes.
= form_tag new_employee_colleague_path do
= text_field_tag 'colleageship[name]', placeholder: 'something...'
Something along these lines should work. Make sure to inspect the params hash to see that the values are formatted correctly.

Adding Posts To A Collection with Join Models

I'm trying to add a 'Collections' model to group Posts so that any user can add any Post they like to any Collection they've created. The Posts will have already been created by a different user. We are just letting other users group these posts in their own Collections. Basically like bookmarking.
What is the cleanest, and most Rails-ey-way of doing this?
I've created the model and run through the migration and what not. Also I've already created proper views for Collection.
rails g model Collection title:string user_id:integer
collections_controller.rb
class CollectionsController < ApplicationController
def index
#collections = current_user.collections.all
end
def show
#collection = Collection.all
end
def new
#collection = Collection.new
end
def create
#collection = current_user.collections.build(collection_params)
if #collection.save
redirect_to #collection, notice: 'saved'
else
render action: 'new'
end
end
def update
end
private
def collection_params
params.require(:collection).permit(:title)
end
end
collection.rb
class Collection < ActiveRecord::Base
belongs_to :user
has_many :posts
validates :title, presence: true
end
post.rb
has_many :collections
It seems like has_many or has_and_belongs_to_many associations are not correct? Should I be creating another model to act as an intermediary to then use
has_many :collections :through :collectionList?
If my association is wrong, can you explain what I need to change to make this work?
Also the next part in this is since this is not being created when the Post or Collection is created, I'm not sure the best way to handle this in the view. What is the best way to handle this, keeping my view/controller as clean as possible? I just want to be able to have a button on the Post#Show page that when clicked, allows users to add that post to a Collection of their own.
In such case you should use or has_and_belongs_to_many or has_many :through association. The second one is recommended, because it allows more flexibility. So now you should:
Create new model PostsCollections
rails g model PostsCollections post_id:integer collection_id:integer
and migrate it
Set correct model associations:
Something like:
class Post < ActiveRecord::Base
has_many :posts_collections
has_many :categories, through: :posts_collections
end
class Collection < ActiveRecord::Base
has_many :posts_collections
has_many :posts, through: :posts_collections
end
class PostsCollections < ActiveRecord::Base
belongs_to :post
belongs_to :collection
end
Then you'll be able to use
#collection.first.posts << #post
And it will add #post to #collection's posts
To add a post to a collection from view
Add a new route to your routes.rb, something like:
resources :collections do # you should have this part already
post :add_post, on: :member
end
In your Collections controller add:
def add_post
#post = Post.find(params[:post_id])
#collection = Collection.find(params[:id])
#collection.posts << #post
respond_to do |format|
format.js
end
end
As for views, you'll have to create a form to show a collection select and button to add it. That form should make POST method request to add_post_collection_path(#collection) with :post_id parameter.
You can read more explanations of how rails associations work in Michael Hartl's tutorial, because that subject is very wide, and can't be explained with short answer.

Resources