Rails: Invalid single-table inheritance type error - ruby-on-rails-4

So, I am working on migrating this php site with an existing database which I cannot change over to Rails. There is a table: Quotes with a column named type. Whenever I try and create a model of this and set the type, it tells me the following error:
ActiveRecord::SubclassNotFound (Invalid single-table inheritance type: HOME is not a subclass of Quotes)
I don't understand why it thinks its inheriting because it's not supposed to. My create method looks like this:
quote = Quotes.create(
agent_id: agent.id,
client_id: client.id,
type: 'HOME',
status: 0,
date_created: DateTime.now
)
If I comment out the type, everything works fine. But with the Type it errors.

I resolved this by setting the models inheritance_column to nil. Active Record Models can inherit from a table through the attribute :type, setting the inheritance_column to nil removes that attribute allowing you to have a database column named type
class Quote < ActiveRecord::Base
self.inheritance_column = nil
end

I hate having potential gotchas deep in the code especially in the intial processes like generating a model. Better to just change the reserved word to something else and free yourself up to take advantage of inheritance column later if the need comes up. A cleaner solution is listed here -> rename a database column name using migration
It reads;
Execute $> rails generate migration ChangeColumnName
where, ChangeColumnName is the name of our migration. This can be any name.
Now, edit the generated migration file at db/migrate/_change_column_name.rb
class ChangeColumnName < ActiveRecord::Migration
def change
rename_column :table_name, :old_column, :new_column
end
end
$> rake db:migrate
You will have to edit controller and view files e.g. if the model name is Product then you will likely edit these files
/app/views/products/_form.html.erb
/app/views/products/show.html.erb
/app/controllers/products_controller.erb
/app/views/products/index.html.erb

Related

Error database query using ManyToManyField on model

I'm fairly new to django web development. And I got an error whereby I try to change a 'post' under admin url - so localhost:8080/admin. I'm able to create it successfully but when I try to click the post that I had just added. I'm getting this error:
Exception Type: DatabaseError Exception Value: This query is not
supported by the database.
And this is the code that I know is 'messing' with this query:
#Post is an abstract class
class BlogPost(Post):
...
translators = models.ManyToManyField(Staff, related_name='translators')
photographers = models.ManyToManyField(Staff, related_name='photographers')
authors = models.ManyToManyField(Staff, related_name='authors')
...
To explain what is going on with this blog post - it can have multiple 'owners'/people that contributed to this post and thus the decision using ManyToManyField. And vice-versa with the 'Staff' member - the type of 'member' can have multiple ownership on multiple posts (Let me know if this logic doesn't make any sense because it does to me).
I'm using mongodb for the database, django 1.5.11 and I have installed djangotoolbox. I've tried the following solutions with adding a relationship to BlogPost as shown below:
Class Staff(Member):
...
staff_posts = models.ManyToManyField(BlogPost, related_name="staff_posts")
...
But I'm getting an error on 'cannot import BlogPost'. I tried figuring out the reason of this error and I don't think that I have a circular dependance - after checking all of the files, there's no circular dependance.
MongoDB (or mongoengine, which I'm guessing you're using) doesn't support joins, so the typical way to model many-to-many relations in a relational database has to be implemented some other way.
One way is to use a ReferenceField inside a ListField. It might look like this (not tested):
class BlogPost(Post):
authors = models.ListField(models.ReferenceField(Staff))
...
Also see these answers:
https://stackoverflow.com/a/18747306/98057
https://stackoverflow.com/a/25568877/98057
Just to put it out there, I'm not real familiar with MongoDB.
However, I don't believe you need to define a ManyToManyField on your Staff class. You already have a ManyToMany defined in your BlogPost, having it defined in one class file is all that is required. (At least for MySQL).

How to make attribute_names list all attribute names in a document with dynamic attributes

I have a Rails 4.2 application with mongoid in which I'm importing csv files with test results. I can't define all fields in the model because they change from test to test and theres always around 700 of them. I use Dynamic Attributes and importing and displaying works fine.
I'm trying to use attribute_names method to get all attribute names but all I get is those defined in the model. If I don't define anything in the model it comes back with "_id" only. attributes method on the other hand can see attributes in the actual document on the other hand.
>> #results.first.attributes.count
=> 763
>> #results.first.attribute_names
=> ["_id"]
I also tried fields.keys, same problem
>> #results.first.fields.keys
=> ["_id"]
My model at the moment looks like this
class Result
include Mongoid::Document
include Mongoid::Attributes::Dynamic
def self.import(file)
CSV.foreach(file.path, headers: true) do |row|
Result.create! row.to_hash
end
end
end
Can somebody explain how to make it work?
Any help greatly appreciated.
This part is not very clear in the documentation.
and this answer doesn't address how you can make your case works ( I really don't know)... but it has one monkey patch at the end...
all I know is why this case not working...
as the documentation states
When dealing with dynamic attributes the following rules apply:
If the attribute exists in the document, Mongoid will provide you with your standard getter and setter methods.
For example, consider a person who has an attribute of "gender" set on the document:
# Set the person's gender to male.
person[:gender] = "Male"
person.gender = "Male"
# Get the person's gender.
person.gender
this is not your case... cause as it appears you are not defining any attributes in your model...
what applies in your case (from the code you showed and problem you described)
If the attribute does not already exist on the document,
Mongoid will not provide you with the getters and setters and will enforce normal method_missing behavior.
In this case you must use the other provided accessor methods: ([] and []=) or (read_attribute and write_attribute).
# Raise a NoMethodError if value isn't set.
person.gender
person.gender = "Male"
# Retrieve a dynamic field safely.
person[:gender]
person.read_attribute(:gender)
# Write a dynamic field safely.
person[:gender] = "Male"
person.write_attribute(:gender, "Male")
as you can see... there is no way for mongoid to add the setter and getter methods in runtime...
Monkey Patch
you could add a field (maybe string, array, hash, whatever suites you) to the document (attribute exists in the document)
on populating the document from the CSV row.. just save what are the fields of the CSV in that field... (hold the CSV keys in it)
use your predefined field (that holds the keys) instead of using .keys.
code example in your case.
class Result
include Mongoid::Document
include Mongoid::Attributes::Dynamic
field :the_field_that_holds_the_keys, type: Array
# ...
end
and in your controller:
#results.first.some_attribute
#=> method missing error
#results.first[:some_attribute]
#=> some_value
#results.first.the_field_that_holds_the_keys
#=> [:some_attribute, :some_other_attribute, :yada]

Best way to populate a new attribute (new database column) of existing Rails models?

I've just added a new column to an existing table in my database:
class AddMoveableDateToDocument < ActiveRecord::Migration
def change
add_column :documents, :moveable_date, :datetime
end
end
In my Rails model I want the moveable_date attribute to be set to a default value upon creation, and the application will be able to change this date later. So, something like:
class Document < ActiveRecord::Base
before_create :set_moveable_date
def set_moveable_date
self.moveable_date ||= self.created_at
end
end
Now, the existing models that are already saved into the database will not have this moveable_date set yet (the value is nil). How do I run through all my existing models and populate the moveable_date attribute with its default value? What is the easiest/best practice way? Can be in the application code itself, in the console, in the terminal, or otherwise. Thanks!
You will get a lot of opinionated answers on this one. Some will suggest the console, some will suggest a one-time rake task.
I would suggest doing it as part of the migration that adds the column. After adding the column, you can run Document.reset_column_information so that the Rails app picks up on your new column, and then iterate through the existing document records and set the moveable date as appropriate.
Or if it's as simple as setting the moveable date to the created_at date, you can use something like Document.update_all("moveable_date = created_at") instead of iterating over them.
That's a good suggestion!
Another way is to add the line before_save :set_moveable_date to your model. It won't accomplish the transition immediately, but if your data is updated on a regular basis, it'd work.

Generating image url using carrierwave in rails

This is somewhat related my question about joins here. By default when I use listing.image.name in my search results view, it does a full query to find the image for every listing in my results array. It even does an extra query just to check if the listing has any images. So to avoid this, I'm adding the following to my Thinking Sphinx query:
#ts_params[:sql][:joins] = "INNER JOIN listing_images ON listing_images.listing_id = listings.id AND listing_images.position = 0"
#ts_params[:sql][:select] = "listings.*, listing_images.image as image_name, listing_images.id as image_id"
This works, however I'm not sure how to generate the full image_url using carrierwave. Previously, where it was doing an extra query per result, I was using listing.image.image_url(:sizename). So, I can find the image name and ID from my join as above, but how to I convert this to a full image url via carrierwave? Is there a built-in method to retrieve the url, that doesn't require an 'image' object?
I tried listing.image_id.image_url(:sizename) but it gave an undefined method error as expected.
From carrierwave's perspective, the answer is obvious:
user.avatar.url
user.avatar.thumbnail.url
Here, user is an instance of a model, and avatar is the field on the model with mount_uploader called on it. In your case this would be something like:
listing_image.image_name.url
listing_image.image_name.thumbnail.url
That probably doesn't work, though, because it looks like you may be loading your listing_image fields into the Listing instead of through includes (or the dreaded N+1 non-eager loads). You may need to resolve your other stackoverflow question before this one will be possible.
Edit:
Per our discussion on your other issue, once you've got a has_one :main_image for just the one ListingImage you want, you're going to use something like #listing.main_image.image_name.url and, if you have a version named "thumbnail", #listing.main_image.image_name.thumbnail.url.
I had the similar issue when I was fetching image using query and wanted to build image url using db field instead of issuing full sql query for each image.
I found out that with couple of field fetched from related image table we can build image which will not run any sql for image if we have three fields id, updated_at and image_name this field should be from the table where image is being saved. It could be from the main table where image is saved as separate column or completely separate table use to specially for image here is a sample code
It can be in your helper or in decorator as per your choice
def logo_url(id, updated_at, name)
return if id.blank? || updated_at.blank? || name.blank?
Company.new(id: id, updated_at: updated_at, logo: name).logo
end
and in view you can call this helper method as
<%= logo_url(company.id, company.updated_at, company.logo).url %>
This way you can have your image with url without executing sql query on each image.

RailsAdmin papertrail empty history

I have configured RailsAdmin to use papertrail for showing the history of changes on models.
It seems to be working fine, but in the UI it displays changes always as an empty array. Is this the expected output? I was expecting to see the actual changes made on the records, or even better a drill down with a before and after change on the record.
Rails 4, RailsAdmin 0.5, PaperTrail 2.7.2
You need to have a column called object_changesin your versions table.
See these instructions from paper_trail. Look at the section called "Diffing Versions."
In short, when you generate the versions table, do this rails g paper_trail:install --with-changes
Or, if you already have an objects table, do this migration:
class AddObjectChangesColumnToVersions < ActiveRecord::Migration
def self.up
add_column :versions, :object_changes, :text
end
def self.down
remove_column :versions, :object_changes
end
end
I'm seeing the exact same thing and filed a bug report with rails_admin: https://github.com/sferik/rails_admin/issues/1751
If that is addressed I'll update this comment.