I have two models in my rails app, Appointment and Policy. My Appointment model has_many :policies, class_name: "Policy", foreign_key: 'writing_code' and my Policy model belongs_to :appointment. The writing_code column in each table is a String.
While it would appear that the association has been made (my app runs), #appointment.policies yields no results. Can someone shed some light on where I've gone wrong here?
Also, to preemptively answer the obvious question, I cannot simply use appointment_id because I will be uploading policy data that has an appointment's (or user's) "writing code" associated to each record. The data will not include the appointment id since it comes from a separate, third party system.
Thanks in advance for all your help!
EDIT:
schema:
create_table "policies", :force => true do |t|
t.integer "product_id"
t.decimal "premium"
t.string "writing_code"
t.datetime "created_at", :null => false t.datetime "updated_at", :null => false end`
I think here is your problem:
You have:
class Appointment
has_many :policies, class_name: "Policy", foreign_key: 'writing_code'
end
Here is what the guides says
"By convention, Rails assumes that the column used to hold the foreign key on this model is the name of the association with the suffix _id added."
Here is an example from the guides:
class Order < ActiveRecord::Base
belongs_to :customer, class_name: "Patron",
foreign_key: "patron_id"
end
In your case, your class name is Policy but your foreign_key name is writing_code. which is not conventional.
Rails is very opinionated. Stay within the way rails likes to work and life gets much easier.
I Agree completely with #WaliAli said also.
"#appointment.policies yields no results"
For appointment to have many policies, each policy needs to be linked with an appointment, in the Model AND in the the table schema.
What this means is that policies should have a field 'appointment_id' which is an integer.
Add an appointment_id field to the policies table.
$ rails generate migration AddAppointmentIdToPolicies appointment_id:integer
$ rake db:migrate
Then you do the following:
class Policy
has_many :appointments # this lets you do #policy.appointments
end
class Appointment
belongs_to :policy # this lets you do #appointment.policy
end
For 99% of rails apps doing it another way, whereby has_many & belongs_to includes more optional paramaters is a code smell.
[Update 1:]
"I have policy data that will be uploaded from a legacy system via .csv
file. This data will have no knowledge of the appointment_id, which is
why I need to use the writing_code as the foreign key value, as it's
the only common data that can associate the two models."
I would suggest resolving this as part of your CSV importer.
As you import each record, do a find for an appointment record that has a matching 'writing code' and then save the record to include the appointment id.
Something like this:
# Inside your CSV importer script
csv_rows.each do |row|
policy = Policy.new
policy.appointment_id = Appointment.find(writing_code: row.writing_code).id
# more code here..
policy.save
end
Related
I have a relationship between compositions and languages. One composition should be written in one and only one language.
My schema.rb contains the following lines:
...
create_table "compositions", force: :cascade do |t|
...
t.integer "product_language_id", null: false
end
...
add_foreign_key "compositions", "languages", column: "product_language_id"
...
I realized that the relationship was written wrong, so I changed the models to be like this:
previously there was a belongs_to :language line in composition.rb which I changed to has_one :language
previously there was a has_many :compositions line in language.rb which I changed to belongs_to :composition
Edit: FIRST QUESTION: is the procedure I made correct? I'm still a beginner at Ruby on Rails.
Now, in rails_admin, there's no possibility to select the language under the new form for composition, there is the line but no list, box nor anything, just the label name 'language', although I have an entry for it in its table.
Edit 2: Once reverting the relationship back to its initial status which I supposed it were incorrect, in rails_admin there is the possibility to add compositions from the language form, but I'd like to have also a drop down menu in the composition form to select the language, which is not appearing. Any suggestion?
Can you tell me where I'm failing? Thanks in advance
Thanks to this upwork freelancer I corrected it leaving the relationship in its original belongs_to / has_many status with the foreign key addition in composition.rb:
belongs_to :language, :foreign_key => 'product_language_id'
Rails assumes everything will be done according to convention. So foreign key of a table is expected to be tablename_id. When we break from the convention, we have to add additional options in our model to tell Rails that the foreign key is not what it expects, its something different.
We could write it this way as well:
belongs_to :product_language, class_name: 'Language'
I'm working on CRM platform.
I would like my users to add, edit and delete custom fields in Client, Contact and Lead objects. Those fields may be plain textfield, list, checkbox, tag etc. Those fields may be required or not. Those fields may have custom validation (that user will define).
Say one company from financials would like to add income to Client object, another would add order configuration to Lead object.
Is there any "enterprise-level" solution (ROR gem) for my problem.
Of cause I know about Custom configuration and config gem, but it doesn't look extensible enough.
Hard question, but this is how I would try to deal with it: I would make all the objects to be derived from a CustomField object, then I would create a one to many relationship between it and a Field model. Something like this:
create_table :field_types do |t|
t.string :name # This would identify the fields: checkbox, plain text, etc
end
create_table :fields do |t|
t.belongs_to :custom_field, null: false, index: true
t.belongs_to :field_type, null: false, index: true
t.string :name
end
class Field < ApplicationRecord
belongs_to :custom_field
belongs_to :field_type
end
class CustomField < ApplicationRecord
has_many :fields
end
This way you could just look into the specified fields on the database and mount it at the view.
Then I would create a table for each type of field that could be used by the users to save the data from the CustomField objects. For instance, I would check the Client field specifier, mount a view with checkboxes A and B. Then, I would get the data from the checkboxes and save each of them at the table Checkboxes with an identifier, so that I could tell that it came from clients.
Depending on what you need to do, another idea that pops to my head is to save the data as a JSON string into the database. This way you could have different fields with different values, all you would need to do is serialize and deserialize to save and load it from the database, respectively.
Sorry if it was a little confusing. Hope it helps.
Assuming your database is relational:
I would suggest to use Entity-Attribute-Value pattern:
https://en.wikipedia.org/wiki/Entity%E2%80%93attribute%E2%80%93value_model.
Here is a gem for it:
https://github.com/iostat/eav_hashes
Also document-oriented database like MongoDB would be an option, if you ever consider changing database. It is schemaless, so you can have different attributes for different instance.
I'm not aware of any out of the box options available, but you might be better off rolling your own on something like this anyway. It will allow you more flexibility, and shouldn't be terrible to implement. In terms of models, I'd probably go with a single-table inheritance table for the fields, probably using a jsonb column for customization options (assuming postgres):
create_table :fields do |t|
t.string :type, null: false # TextField, ListField, etc.
t.jsonb :config, default: {}, null: false
t.belongs_to :contact
end
You can then subclass as necessary for different use-cases:
class Field < ApplicationRecord
belongs_to :contact
end
class TextField < Field
def required=(required)
config[:required] = required
end
end
class CheckboxField < Field
def default_checked=(default_checked)
config[:default_checked] = default_checked
end
end
You can look into something like jsonb_accessor to make for a cleaner interface to the jsonb column.
Likewise, single-table inheritance looks like it may also make sense for the contacts as well, not sure what the base table should be, but maybe something like:
create_table :contacts do |t|
t.string :type, null: false # Contact, Lead, Client
end
class Contact < ApplicationRecord
end
class Lead < Contact
end
Here are some examples I found helpful for custom fields:
http://railscasts.com/episodes/403-dynamic-forms?view=asciicast
And:
https://github.com/lab2023/postgresql_jsonb_ransack_rails_5
https://gist.github.com/ismailakbudak/2ca1feac945999ec3e7d9cf0a373497a
I'm working on an application where a guest will be provided a short Base64 token that they could in turn use to access the edit function of one of several different models via one "search form" on the application homepage.
I have already created the token functionality and included it in the schema for the models I need. My question is, how would one best search for and access the edit function using the access token from the home page?
I'm having a hard time finding a good way to do this and while I'm finding a lot about access tokens, most of it doesn't seem to pertain to my use case.
Rails provides the ability for model classes to be inherited from a parent model class. Then the models can have shared attributes, but also unique ones. In the database all of these model objects are stored in the same table for all classes, so this is called Single Table Inheritance or STI. (Documented here but there are better docs in blog posts out there.)
If you use this approach, then you could search the parent class for all instances to find matching objects/records.
class AccessToken < ActiveRecord::Base
# has attribute access_token, and maybe others
end
class OneAccessibleKind < AccessToken
# may have other attributes
end
class AnotherAccessibleKind < AccessToken
# may have other attributes
end
Your migration would look something like this:
create_table :access_token do |t|
t.string "access_token"
t.string "type"
# add any additional attributes of subclasses
t.timestamps
end
You can then query against the parent class. Note
all_models = AccessToken.where(access_token: 'a-token')
Note that these will all come back as AccessToken objects (i.e. the parent class), but you can inspect the type attribute to see what their base class is.
This may not be the best solution, however, if your classes are mostly different fields because you'll have lots of unused columns. Depending on your backing database (assuming row-oriented SQL) and number of objects this could be a performance problem.
Another option would be to use a one-to-one relationship and have an AccessToken model for each of your other models. Here you can use an STI association.
class AccessToken < ActiveRecord::Base
belongs_to :owner, :polymorphic => true
end
class OneAccessibleKind < ActiveRecord::Base
has_one :access_token, :as => :owner
end
class AnotherAccessibleKind < ActiveRecord::Base
has_one :access_token, :as => :owner
end
With migrations something like this:
create_table :access_token do |t|
t.string :access_token
t.integer :owner_id, null: false
t.string :owner_type, null: false
t.timestamps
end
create_table :one_accessible_kind do |t|
# any attributes for this type
t.timestamps
end
Then you can find an access token and access each owner to get the objects.
AccessToken.where(access_token: 'a-token').map(&:owner)
I have a has_many relation in my app. e.g. department has many users.
I want to covert it to a has_and_belongs_to_many relation.
As part of the migration I need to preserve the current relation between users and departments, meaning I have to move all the data to the new connecting table.
This is the migration I created:
class CreateUserDepartment < ActiveRecord::Migration
def change
create_table :users_departments do |t|
t.belongs_to :user
t.belongs_to :department
end
###############################################
# need to move the data to the new table here #
###############################################
remove_column :users, :sub_department_id
end
end
what is the best way to write the missing line?
If you must, you can use execute "your SQL". See this question:
How do I add some inserts in rails migration?
The main value of the "Don't" answer to that question is in explaining why you would not want to use your models to do this. Also, I'd be surprised if you can or would want to do this using change, you would probably need to use self.up.
I've been trying to figure out the best way to build out a user flagging system in rails 3.1. I experimented with the make_flaggable gem, but it didn't give me what I needed.
I'm using devise for my user model and I have a Post model that belongs to the user. I need to have the ability to retrieve a list of all posts that have been flagged from the admin side of the site. So far, I've had difficulty obtaining that.
I'm uncertain about which type of relationship I would need to use between a Flag model and the Post/User model. I've been reading up on Polymorphic relationships and that is looking promising.
Any ideas or feedback would be much appreciated!
It's very easy to roll your own solution. I would do it this way.
class User
has_many :flags
end
class Post
has_many :flags
end
class Flag
belongs_to :user
belongs_to :post
end
You can get posts that have been flagged by going through the flag model or the post model.
# since a post has many flags
# you will get duplicates for posts flagged more than once
# unless you call uniq
flagged_posts = Post.joins(:flags).uniq
Or through the flags model:
flags = Flag.includes(:post).uniq
flags.each do |flag|
puts flag.post
end
To ensure you don't get duplicate flags on the same post from the same user I would add a uniq index in the migration:
def change
create_table :flags do |t|
t.belongs_to :user, null: false
t.belongs_to :post, null: false
t.timestamps
end
add_index :flags, [:user_id, :post_id], unique: true
end
Maybe I'm misunderstanding what you're trying to do, but why not just add a column to your Posts table called "flagged?" Then you can just do User.posts(:where=> :flagged=>true).