Let's say I want to update an association by passing in a key other than the primary key, like social security number:
Teacher.find(params[:id]).update({'student_ids'=>['123-45-6789','987-65-4321']})
How can I make my Teacher model understand that it will be receiving SSN, and not database IDs? A SSN uniquely identifies a student.
Edit: I like Pavling's solution, as it keeps the logic in the model. However this fails:
Teacher.new({name:'Miss Molly',student_ssns:['123-45-6789','987-65-4321']})
With ActiveRecord::UnknownAttributeError: unknown attribute: student_ssns
How about...
student_ids = Student.where(ssn:['123-45-6789','987.65.4321']).map(&:id)
Teacher.find(params[:id]).update({'student_ids'=> student_ids})
If SSN is a unique identifier for your student rows, you could make it the primary key instead of the default integer - but convention would advise to leave the 'normal' ID field there.
So how about a method on Teacher which takes student_ssns rather than student_ids, and inside that method, find the student ids (similar to Kevin Monk's answer)?
# teacher.rb
def student_ssns=(ssns)
student_ids = Student.where(ssn: ssns).pluck(:id)
update({student_ids: student_ids})
end
Then you can use it thus:
Teacher.find(params[:id]).student_ssns(['123-45-6789','987.65.4321'])
Using Pavling's solution, but changing the definition line to:
def student_ssns=(ssns)
should get it working properly for you.
Related
So im using an api to get info on weather, its executes everyday, what im trying to do is to get updated if already exist, and create a new one if it doesn't in table.
I do want to update all attributs when udpdating.
i did try
model = Model.where(column_name: value).first_or_initialize(locked: false)
but i get an error saying :
unknown attribute locked for Model
raise UnknownAttributeError.new(self ,k.to_s)
If you need anything, ask and i will comment or edit. Im newb to ruby and rails
Firstly, the model.Model part should be just Model, as Model is your class.
locked is supposed to be a column/attribute of the Model class, although it seems is not the case judging from your error. Therefore, I'm gonna use other_column_name as an example.
Explanation of what this is doing:
Model.where(column_name: value).first_or_initialize(other_column_name: some_value)
Model.where(column_name: value): gets models that satisfy the condition column_name == value
first_or_initialize: if a model such that column_name == value was found, that one is returned. Otherwise, it initializes a model with column_name = value.
By passing other_column_name: some_value, if the model was not found and needs to be initialized, it sets other_column_name to some_value but: 1) it does not update it if it was initially found and 2) it does not save the record.
The equivalent of first_or_initialize that saves the new record would be first_or_create but this would still not update the record if it already existed.
So, you should do something like this:
m = Model.where(column_name: value).first_or_initialize
m.other_column_name = some_value
m.save
This way, you first get a model where column_name is value or initialize a new one with this value if it didn't already exist. Then, you set the attribute other_column_name to some_value and save the model.
A one-liner alternative would be
Model.where(column_name: value).first_or_create.update(other_column_name: some_value)
However, note that if it needs to be created, this one will perform 2 queries (the insert and the update).
About the error part. It says the attribute locked does not exist on the Model record. Are these classes you created? Are you using some pre-existing project? You could try posting Model.attribute_names and maybe your schema.rb
Firstly refer to the docs here
A table by the name of weather with the following attributes location: string temperature:integer wind:string needing to be updated or initialized based on the location would work like this
#weather_record = Weather.find_or_initialize_by(location: location_value)
#weather.temperature = -60
#weather.wind = strong
#weather.save
Next, never, ever use a reserved name for a model so do not have Model as the name of your table
Lastly in your example
model.Model.where(column_name: value).first_or_initialize(locked: false)
you are saying
a_record.ClassName.where which is just wrong, If you are using a class method then start with the class name e.g. Weather.where if you are using instance methods then use the instance name e.g. an_instance_of_weather.some_field
Try this mate:
column_name_value = (Way that you get the info from data)
model = Model.find_or_initialize_by column_name: column_name_value
Let me know if worked!
I'm not sure how to frame this question, I am looking to solve a design problem.
I'm using ActiveRecord.
An Agency can have multiple documents.
documents has a column additional_details of type jsonb. Contains hash details.
additional_details column has different set of key value pair based on agency.
Example:
doc1 = agency1.documents.first.additional_details => { xml_url: '', ... }
doc2 = agency2.documents.first.additional_details => { feed_url1: '', ... }
agency1 and agency2 are instance objects of Agency.
When I make a call to fetch the url like document.additional_details.get_url
I can write conditions like
def get_url
if agency1.name == 'Utah'
return additional_details[xml_url]
elsif
so on
elsif
so on
end
end
Which is not a good practice I feel.
I believe we can solve this at class level. Note I need to solve this on presentation layer, I'm using decorators .
Edit:
An particular agency will have same keys within additional_details column but values are certainly different.
I am assuming these are Active Record classes? Where is agency1 defined? You should almost never be hard coded based on specific instances, instead any such data should be part of the record and its instance.
It is especially unclear why you have different types of "URL" at all, and not just a simple url string column in the documents table. Why does agency1 use xml_url but agency2 uses feed_url1?
But as an example, if each agency has say a prefix defined (say the domain name / filestore, and the documents just the relative/local address), say:
agency1.document_root = "https://example.com/documents/"
agency1.documents.first.rel_url = "web/rails/rails_example.pdf"
Then in the Document class you might do:
def url
agency.document_root + rel_url
end
Which then gives you the:
agency1.documents.first.url
Add a field to your Agency model where you store the key to the url in the additional data hash. Let's say you name the field url_key. Then you can do
def get_url
additional_details[agency.url_key]
end
Note that I assume that get_url is a method on the Document model.
This question has two parts:
Consider an active record relation that has a Student - name:string, favorite_class:references and FavoriteClass name:string, abbrev:string.
First question: When creating a student record and adding a favorite class (name and abbrev) I want to check if that name/abbrev combo exists and if it does load that one for this association, else create a new one.
Second: When I try to update (Put) the student I want to be able to pass the abbrev of the favorite class and look up the record by that portion (assuming abbrevs are unique) and use that, else fail.
I am not seeing the rails way of doing this type of operation.
For the first question, the trick is to use find_or_create method:
#in the place where you create a new Student
#this method return first entity from db, if it doesn't found, will create one
fav_class = FavoriteClass.find_or_create(name: some_name, abbrev: some_abbrev)
Student.create(name: some_student_name, favorite_class: fav_class)
something similar you can do for your second question. Please, give me more details about it.
Update 1
If you want to update student's favourite class, you can do it in this way:
#I assume that you use latest vertion of rails of not, use .first instead of .take
new_class = FavoriteClass.where(abbrev: some_abbrev).take
student.update_attributes(favorite_class: new_class) if new_class
customer=Customer.new #create a new Customer object
customer.id =1000+i #Id needs to be set first because otherwise its automatically set
customer.update_attributes(
:fname=>'mike',
:lname=>'hancock',
:Year=>1998,
:Model=>'buick',
:Engine=>'4 liter',
:vinnum=>'h920129801298',
:signupdate=>"#{Date.today}",
:password=>'fguygfyed',
)
contact=Contact.create(
:customer_id=>customer.id, #set the foreign primary key
:contactmethod=>4567894561,
:contacttype=>"sms",
:dateadded=>"#{Date.today}",
)
customer.Contacts.create(
:contactmethod=> 4657894564,
:contacttype=> 'email',
:dateadded=> "#{Date.today}",
)
i+=1
end
This code works. However, if instead of
contact=Contact.create(
:customer_id=>customer.id, #set the foreign primary key
I just wrote customer.Contacts.create like I wrote at the end then the code fails. Why is it that I can only have one instance of customer.Contacts.create and for the other I have to explicitly declare the customer.id?
Method names are almost always declared as all lower-case. This doesn't seem to be valid.
What you probably intend is:
customer.contacts.create(...)
I'm feeling a little slow today. I'm trying to do something that I think is very simple. I have a Domain class with a property called 'name'. I want 'name' to have an index, and I want the index to require that the 'name' is unique. I've set the unique constraint and tried creating an index. I can't make sense out of the Gorm docs as to how I add the unique attribute to the index. Here's some code:
class Project {
String name
static hasMany = [things:Things]
static mapping = {
name index:'name_idx'
}
static constraints = {
name(unique:true)
}
}
All is well with the above, except when do "show indexes from project" in mysql it shows my name key as not unique. I know the problem is that I am not specifying unique in the mapping, but quite frankly the docs for gorm are making my head hurt. I see all kinds of stuff about columns, but I can't find a single example anywhere on the web that shows what I want to do. I don't need complex mappings or compound keys, I just want to know the syntax to add the unique attribute to the mapping declaration above. Any advice welcome.
I also did a grails export-schema and see the following:
create index name_idx on project (name);
Nothing in that to indicate this index requires unique values
A related followup question would be once I succeed in making that index unique, what type of error should I expect when I go to save a Project instance and the name is not unique? Is there a specific exception thrown? I realize that even if I check that a given 'name' is unique there's still a possibility that by the time I save it there may be a row with that name.
I'm quite sure the syntax to do what I want is simple but I just can't find a simple example to educate myself with. I've been to this page but it doesn't explain HOW the uniqueness is enforced. I'd like to enforce it at the name index level.
The indexColumn allows additional options to be configured. This may be what you're looking for.
static mapping = {
name indexColumn:[name:'name_idx', unique:true]
}
Grails Documentation for indexColumn
If you put only the unique constraint the GORM send DDL to create an unique index on database.
static constraints = {
name nullable: false, unique: true
}