I've been working on a web app lately, and Activerecord has started creeping me out- for the most part it's awesome, but it has taken to treating a particular column like a leper.
Initially I created a student model from a scaffold. The model holds various information like name, email, entry quarter, etc. And that all works beautifully. The problem I'm having is in how it's taken to treating a particular column- a string named type. Type is supposed to have a value like "B.S.", "M.S", "PhD", etc, however, neither creating a new object instance nor editing an old one are able to change the value from ''. Furthermore, if I go into the database and manually change the value, Rails throws a fit and throws errors when I call Student.find.
Any ideas as to what I'm doing wrong?
'type' is a protected column in activerecord for the purpose of inheritance. Oops!
Related
I have a procedure which receives two models, one which already exists, and another one which holds new attributes which I want to merge in the first one.
Since other parts of the program are holding the same reference to the new model, I can't just operate on the existing one. Therefor I do the following:
def merge(new_model, existing_model)
new_model.attributes = existing_model.attributes.merge(new_model.attributes)
new_model.id = existing_model.id
end
Now the new_model is being saved which gives me the uniqueness erorr (even though it's technically the same model). I also tried using the reload method, but that yields the same result.
Background:
The method above is run in a before_add callback on an association. I want to be able to call update on a model (with nested associations) without having to specify IDs of the nested models. This update is supposed to merge some associations, which is why I try to do the whole merge thing above.
You can't set the id of a model and then save the record expecting the id to be set since the id is the primary key of the database. So you are actually creating a whole new record and, thus, the uniqueness validation error. So you'll need to think of some other design to accomplish what you are wanting. It may help to know that what you are trying to do sounds similar to a deep_dup, except that ActiveRecord doesn't define this method (but Hash does).
for any model instance, there is an #object_id and #id - I know that they are not the same.
However, I'm not quite sure what makes them different and how they would each be used in context.
Please help clear this up!!
Thanks!
In Rails, an ActiveRecord model instance has an id property that maps to the value stored in the id column of the database. This may be nil if the record hasn't been saved.
In Ruby, object_id is a value that represents the identity of the object in question. It is always populated with something since everything in Ruby is an object.
These two are not related. There may be several independent instances of a model, each with their own object_id value but an identical id.
If two variables refer to something with the same object_id, then they refer to exactly the same object.
It's rare to see object_id used in code, it's a Ruby internal that's hardly ever needed. Mostly it's to establish if you're talking about identical objects, or just equivalent ones.
You will, on the other hand, see id and similar values used frequently since that's the glue that holds your relational database together.
Everything (and i mean everything) in Ruby is an object. Each of these objects has an object_id, which is a value used to track them in memory, basically.
In Rails, model instances are automatically set up with methods to return their value from the corresponding column in the database. .id is one of these.
As far as using in context, generally you would not use object_id in your code, ever: it's an under-the-hood thing.
EDIT - as an aside, a common issue seen in older versions of ruby/rails (where the object_id method was actually called id, and was overridden by rail's id method) was caused by the fact that the object_id of nil is 4. So you would call id on a variable which you expected to be a model instance, but was actually nil, thus getting "4" back when you expected to get the id of a record from the database.
In short, :id is the default primary_key of a table.And object_id could be the foriegn_key unless you set a custom foreign_key.
For example,take these two table users and posts.The relation would be
user => has_many posts and
post => belongs to user
so in the posts table,we should create a foreign_key(in this case user_id) to make the relations works.
Hope it helps!
id is specific for ActiveModel record and it relates to id column in database. object_id is defined on Object and is unique for every single object created in the memory.
I have many legacy databases from which I need to pull raw data. Each of the tables in the database have arbitrary names, and an arbitrary collection of fields. I have been getting access to these fields with the following class:
class Frt < ActiveRecord::Base
establish_connection :legacy
set_primary_key "point"
end
When I reach the point in my code where I know the table name, I can call:
Frt.set_table_name "table"
t = Frt.find_by_sql("blah")
something = t.field_name + t.other_field_name
etc...
The problem is that I've realized that this locks the accessible field names to whatever table I select first. If I try to change the table with another call to the `set_table_name' method, it changes the attribute for the class, but any new instances will still have the same set of fields as the first one. So far, in my app, I've not needing anything else, but I'm expanding the program in a way in which I know it will bite me in the butt down the road.
I've tried `Frt.send :set_table_name "new_table"', hoping that it would cause ActiveRecord to do it's magic again. It doesn't.
Can anyone suggest how I might be able to keep the convenience of ActiveRecord, but get it to dynamically remap its fields for whatever table I need loaded?
I've never used it, but Magic Model Generator claims to create models for tables automatically.
Sorry to answer my own post, but I guess I didn't explain it very well. In case someone else comes along after this, what I needed to do was issue a call to the "Frt.reset_column_information" method. I was revisiting this issue, and just dumped all the methods on the ActiveRecord class, and found that one lurking in the list.
A better way to solve this would be to create a model for each legacy table you need to interact with - no dynamic table name remapping required, and it works right out of the box.
I'm working on my first ASP.NET MVC (beta for version 3) application (using EF4) and I'm struggling a bit with some of the conventions around saving a new record and updating an existing one. I am using the standard route mapping.
When the user goes to the page /session/Evaluate they can enter a new record and save it. I have an action defined like this:
[ActionName("Evaluate")]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EvaluateSave(EvaluteSessionViewModel evaluatedSession)
{
}
When they save I grab an entity off the view model and attach it to my context and save. So far, so good. Now I want the user to be able to edit this record via the url /session/Evaluate/1 where '1' is the record ID.
Edit: I have my EF entity attached as a property to the View Model.
If I add an overloaded method, like this (so I can retrieve the '1' portion automatically).
[ActionName("Evaluate")]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult EvaluateSave(ID, EvaluteSessionViewModel evaluatedSession)
{
}
I get an "The current request for action 'Evaluate' on controller type 'SessionsController' is ambiguous between the following action" error. I'm not sure why they're ambiguous since they look unique to me.
I decided that I was just going to skip over this issue for now and see if I could get it to update an existing record, so I commented out the EvaluateSave that didn't have the ID parameter.
What I'd like to do is this:
// Load the original entity from EF
// Rebind the postback so that the values posted update the entity
// Save the result
Since the entity is populated as the parameter (evaluatedSession) the rebinding is happening too soon. But as I look at the approach I'd like to take I realized that it opens my code up to hacking (since a user could add in fields into the posted back page and these could override the values I set in the entity).
So it seems I'm left with having to manually check each field to see if it has changed and if it has, update it. Something like this:
if (evaluatedSession.MyEntity.myField <> savedSession.myField)
savedSession.myField = evaluatedSession.MyEntity.myField;
Or, save a copy of the entity and make sure none of the non-user editable ones have changed. Yuck.
So two questions:
First: how do I disambiguate the overloaded methods?
Second: is there a better way of handling updating a previously saved record?
Edit: I guess I could use something like Automapper...
Edit 9/22/2010 - OK, it looks like this is supposed to work with a combination of two items: you can control what fields bind (and specifically exclude some of them) via the [Bind(Exclude="field1,field2")] attribute either on the class level or as part of the method doing the saving, ex.
public ActionResult EvaluateSave([Bind(Exclude="field1")] EvaluateSessionViewModel evaluatedSession)
From the EF side of things you are supposed to be able to use the ApplyCurrentValues() method from the context, ex.
context.ApplyCurrentValues(savedEval.EntityKey.EntitySetName, evaluatedSession);
Of course, that doesn't appear to work for me. I keep getting "An object with a key that matches the key of the supplied object could not be found in the ObjectStateManager. Verify that the key values of the supplied object match the key values of the object to which changes must be applied.".
I tried attaching the original entity that I had just loaded, just in case it wasn't attached to the context for some reason (before ApplyCurrentValues):
context.AttachTo(savedEval.EntityKey.EntitySetName, savedEval);
It still fails. I'm guessing it has something to do with the type of EF entity object MVC creates (perhaps it's not filled in enough for EF4 to do anything with it?). I had hoped to enable .NET framework stepping to walk through it to see what it was attempting to do, but it appears EF4 isn't part of the deal. I looked at it with Reflector but it's a little hard for me to visualize what is happening.
Well, the way it works is you can only have one method name per httpverb. So the easiest way is to create a new action name. Something like "Create" for new records and "Edit" for existing records.
You can use the AntiForgeryToken ( http://msdn.microsoft.com/en-us/library/dd492767.aspx ) to validate the data. It doesn't stop all attempts at hacking but it's an added benefit.
Additional
The reason you can only have one action name per httpverb is because the model binders only attempt to model bind and really aren't type specific. If you had two methods with the same action name and two different types of parameters it can't just try and find the best match because your intent might be clearly one thing while the program only sees some sort of best match. For instance, your might have a parameter Id and a model that contains a property Id and it might not know which one you intend to use.
I'm using Ruby on Rails and the paths_of_glory gem
I need to access the types of achievements that a user accomplishes in order to display a picture along with each particular achievement. When I try to access it via #user.achievements.type, I get an error that says that it wants to return "array" (as in achievements is an array) instead of actually returning the elements in the type column of my database.
Since every ruby object has a method called type, my call to access the type column of the database fails. When I try to change the entry in the table, the paths_of_glory gem says it needs a type column in order to function properly.
I'm not quite sure where to go from here in order to access that column in the database. Any suggestions?
Not entirely sure what you're asking here, but maybe this will help.
For the first thing, #user.achievements is an array because you have multiple achievements, and the type method is for individual elements of #user.achievements which is why that won't work just like that. You'll have to do something like this:
#user.achievements.each do |achievement|
# Do stuff here
end
Regarding the type column, type is a reserved column in Rails used specifically for Single Table Inheritance, where multiple Rails models use a single database table. So you can't access it directly. I assume that paths_of_glory uses STI in some manner. You can access the model's class with something like achievement.class, then if you want just the name of it you can try achievement.class.to_s.
#user.achievements.each do |achievement|
model = achievement.class # => MyAwesomeAchievementClass
#image = model.picture # You could write some method in the model like this
end