I have created a new model in my app - PaypalOrder. Now in one of the methods of this model I want to be able to access current_order object. Where Order is an existing model. How can I do this in ruby on rails?
I read about associations, but they seem a bit complicated.
EDIT:
The problem with using associations is that not every Order will have a corresponding PaypalOrder. But whenever there is a PaypalOrder I want to access Order. How can I setup this association
what about:
class PaypalOrder
belongs_to :order
end
?
you need an "order_id" column in paypal_orders table
and that's it
you then create a PaypalOrder with
def some_action
current_order = Order.find(some_id)
paypal_order = PaypalOrder.new(order: current_order)
#do what you want with paypal_order
end
if you don't have the order_id do
bundle exec rails g migration AddUserToPaypalOrder
and the change method
add_column :paypal_orders, :user, :references
or
add_column :paypal_orders, :user_id, :integer
The way to go is to use concerns, it works like this:
Model:
# app/models/PayPayOrder.rb
class PayPalOrder < BaseModel
# concerns
include MyMultipleNeededMethods
...
def now_i_use_the_concern_method
concern_method
end
...
end
Concern:
# app/models/concerns/MyMultipleNeededMethods.rb
module MyMultipleNeededMethods
extend ActiveSupport::Concern
def concern_method
puts "refactored like a boss"
end
...
end
Never ever try to cross reference methods this way. Use the given rails framework, its awesom ;-)
Hm... current_order and curren_user and usually current_s are tight to the session. So they can be accessed only by the controller. Since the models are handling business-domain logic they shouldn't access these objects...
Related
I have the a basic events table, and want to have sub-tables for each event type (hiking, party, riverrun, etc).
I see a lot of old (2011/2012) posts regarding CTI, MTI and STI. Some solutions worked for Heroku, while others did not.
What is the "current" Rails way of doing this type of thing? Has this been added to Rails 4.x? Is there a magical Gem that handles this (with Postgres on Heroku)?
Some information if it helps:
In the future, there will be between 20-50 events, and each sub-table might be as many as 80 columns. The site is hosted on Heroku. Running Rails 4.0.2
STI - Single Table Inheritance is what you are looking for.
http://api.rubyonrails.org/classes/ActiveRecord/Base.html#class-ActiveRecord::Base-label-Single+table+inheritance
http://api.rubyonrails.org/classes/ActiveRecord/Inheritance.html
You create your model as always but you add an attribute even_type as a string to your database. (default ist "type")
You let the EventModel know, that this will be the inheritance column.
class Event < ActiveRecord::Base
self.inheritance_column = :event_type
# dont forget your scopes to easy access them by type
# Event.party or Event.ultrafestival
scope :party, -> { where(event_type: 'Party') }
scope :ultrafestival, -> { where(event_type: 'UltraFestival') }
scope :tomorrowland, -> { where(event_type: 'TomorrowLand') }
def host
raise "needs to be implemented in your sub-class!"
end
end
Than you create some subclasses. Make sure those are inheriting from Event
class Party < Event
end
class UltraFestival < Event
end
class Tomorrowland < Event
end
Basically, all Objects are Events! So if you go Event.all you'll get them all! Technically an Object can be something else. All those "Events" will be stored in the same table, they will be differed by the event_type which will be in this example "party", "ultra_festival" or "tomorrowland".
Now you can add some special stuff to each of those classes for example
class Party < Event
def host
"private homeparty PTY"
end
def publish_photostream
end
def call_a_cleaning_woman
end
end
class UltraFestival < Event
attr_accessor :location
def host
"UMF Festival Corp."
end
def is_in_europe?
end
def is_in_asia?
end
end
class Tomorrowland < Event
def host
"ID&T"
end
def download_after_movie!
end
end
This is the standard Rails way - since years. Of course its working on every hoster and with postgres.
// edit:
if your sub-events need some special tables in the database, then you need to go MTI, multi-table-inheritance.
I have Customer and each customer has_many Properties. Customers belong to a Company.
I'm trying to add a certain Property to each one of a single Company's Customers. I only want this change to happen once.
I'm thinking about using a migration but it doesn't seem right to create a migration for a change that I only ever want to happen once, and only on one of my users.
Is there a right way to do this?
You can just use rails console.
In rails c:
Company.where(conditions).last.customers.each do |customer|
customer.properties << Property.where(condition)
customer.save!
end
Validation
Depending on how you're changing the Customer model, I'd include a simple vaidation on the before_update callback to see if the attribute is populated or not:
#app/models/Customer.rb
class Customer < ActiveRecord::Base
before_update :is_valid?
private
def is_valid?
return if self.attribute.present?
end
end
This will basically check if the model has the attribute populated. If it does, it means you'll then be able to update it, else it will break
--
Strong_Params
An alternative will be to set the strong_params so that the attribute you want to remain constant will not be changed when you update / create the element:
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
...
private
def strong_params
params.require(:model).permit(:only, :attributes, :to, :update)
end
end
It would be much more helpful if you explained the context as to why you need this type of functionality - that will give people the ability to create a real solution, instead of proposing ideas
I have a model like so:
class Phrase < ActiveRecord::Base
attr_accessible :phrase, :emotion, :category
end
This model corresponds to it's appropriate table. Every time I want to create a new Phrase, I run this code in a controller:
Phrase.where(:phrase => values['phrase']).first_or_create do |phrase|
phrase.emotion = values['emotion']
phrase.category = values['category']
end
I feel like I should be using the initialize method on my model, or perhaps creating a new method on my model to build them up. The above method seems pretty bad, especially when I start building models with 20+ attributes.
Is there a best/better practice around building models? Should the above controller code actually live in the model somewhere?
def create
#phrase = Phrase.new(params[:phrase])
end
or if you are using strong parameters
def create
#phrase = Phrase.new(phrase_params)
end
private
def phrase_params
params.require(:phrase).permit(:phrase, :emotion, :category)
end
end
Since your model allows for mass assignment of those attributes via attr_accessible you can just pass a hash to new:
Phrase.new(values)
You can also use find_or_create_by with a hash of options:
Phrase.find_or_create_by_phrase(values[:phrase], values)
This happened when I added an attr_accessible to my Relationship model.
class Relationship < ActiveRecord::Base
attr_accessible :followed_id
end
Without using Devise or a protected_attributes gem, what is the way around this? I know that in controllers you call a private method requiring and permitting fields. Is this something you should do in the model too? What is the convention here?
Thanks!
In Rails 4 you use Strong Parameters instead of Protected Attributes. (You don't need to include the gem in your gemfile as it's already included.)
You take the Rails 3 attr_accessible code out of your model and put corresponding code into your controller. See here for more documentation: https://github.com/rails/strong_parameters
In your case, something like:
class RelationshipController < ActionController::Base
def create
#relationship = Relationship.new(relationship_params)
if #relationship.save
# do something
else
# do something
end
end
private
def relationship_params
params.require(:relationship).permit(:followed_id)
end
end
Edit:
Here's a good article I just came across about this: http://blog.sensible.io/2013/08/17/strong-parameters-by-example.html
I have a legacy table with a column for the last update timestamp.
Now I do want to tell my model that the rails attribute updated_at is mapped to the legacy column.
alias_attribute :updated_at, :lastcall
Now I can access the column but it's not getting updated when i update the object.
So how can I use the rails timestamps with an legacy column?
Best,
P
Try to add this as well, which will alias the setter method.
alias_attribute :updated_at=, :lastcall=
I don't know if there's a 'proper' way of doing it, but you could do it with a before_save or before_update filter on the model.
class LegacyModel < ActiveRecord::Base
before_update :update_lastcall
private
def update_lastcall
self.lastcall = Time.now
end
end
If you don't want to get the model messy you could put it into an Observer.
I'd also like to draw your attention to this, if your timestamp column names are site-wide (as mine are). I didn't want to clutter up my models, and fortunately, you can monkey-patch ActiveRecord::Timestamp. I placed the below into a dir named lib/rails_ext/active_record.rb (I'm an organization freak) and called it with a require 'rails_ext/active_record' declaration in one of my initializers in config/initializers/.
module ActiveRecord
module Timestamp
private
def timestamp_attributes_for_update #:nodoc:
[:modified_time, :updated_at, :updated_on, :modified_at]
end
def timestamp_attributes_for_create #:nodoc:
[:created_date, :created_at, :created_on]
end
end
end
My custom attributes ended up being :modified_time and :created_date. You'd specify your :lastcall column in one of those (timestamp_attributes_for_update, I'm assuming). No mucking with your models required.