I am using ActiveRecord's replace method to... well, replace a collection of associated objects with new ones.
Everything works fine, except for when I delete items, only the association itself is deleted (the id is nulled in the database) but the actual associated objects are not.
E.g. I have a Profile model, which has_many Skills, and when I delete a skill or two, only the profile_id gets removed from the skills table, not the entire row.
I want to get rid of the entire row.
Related
For example, I have a model Hobby. And I have a User model, and user has many hobbies. Hobby also has many users. And I have a linking table UserHobby (belongs to user, belongs to hobby).
Some user registers on a site, picks some hobbies, and then saves this list. Technically creates an array of records. So on my backend part I have to do something like this:
UserHobby.create( user: user, hobby: params[:hobbies]), where :hobbies is an array. But it doesn't work this way.
Is there a way to do this without using something like params[:hobbies].map{ |hobby| UserHobby.create( user: user, hobby: hobby) } ?
The map is fine, but when creating associations use association methods.
params[:hobbies] is a param coming from a form, so presumably it contains an Array of Hobby IDs (perhaps better as params[:hobby_ids]). Since these Hobbies already exist in the database, we can simply append them to the user's list of hobbies. This will insert the necessary rows in the join table.
user.hobbies << Hobbies.where(id: params[:hobbies])
user.hobbies is cached and Rails will only check the database once. If you do UserHobby.create!(user: user, hobby: hobby) then user.hobbies will be out of date. If you update user.hobbies directly then user.hobbies will be updated.
I am creating a few records programmatically based on a users input and creating an array of records to import.
When I check the database I can see the relationship has been created if they are new records.
If one of the records already exists in the database I can see an entry of the following in the association table but I can also see the new records have been created in their respective table so they exist but the records ID is not being updated in the association table.
user_id: 1
keyword_id: null
but if I run the code for a second time it will add the relationship correctly.
This is my code
records_to_add = []
words.each do |word|
keyword = Keyword.find_or_initialize_by(
word: word,
device: device,
)
records_to_add.push(keyword)
end
keywords_added = Keyword.import records_to_add, on_duplicate_key_ignore: true, validate: true
user.keywords << records_to_add
I think there is something wrong with this part of the code
user.keywords << records_to_add
It isn't creating the relationship correctly if one of the records already exists...
You are calling 'find_or_initialize_by' in your words loop, and then importing those records, which creates a new row in your Keyword table for all the new records.
So far, so good.
Then your script takes the first list (persisted and new records) and attempts to associate them to the user. At this point, it creates associations for existing Keyword records, but tries to create new Keyword records again for the ones that it just created in the import and associate those. These probably fail a unique validation at that point, and are not associated nor persisted.
That leaves you with just the unassociated but newly created records.
I have a scaffolded Grails application with two domains, Person and Course. Person belongs to Course, and Course hasMany Persons. I have modified show.gsp for Course to list all of the Persons associated with the selected Course.
To achieve this, Course.groovy contains the following line:
List persons = new ArrayList()
And, as a result, the "person" database table contains a persons_idx field. I frequently will be adding new data to the "person" table outside of my Grails application, from an external website.
When INSERTing new data, how to I figure out what to set persons_idx as?
I had originally used a SortedSet instead of an ArrayList for persons, since I care about sorting. But since I am sorting on Person.lastName, and there will always be multiple people with the same last name, then the list will exclude those persons who have the same last names as others. I wish there was another way...
Thanks.
Having two applications manipulate the same Database is a thing to avoid, when possible. Can your 2nd application instead call an action on the controlling app to add a Person to the Course with parameters passed to specify each? That way, only one app is writing to the DB, reducing caching, index, and sequence headaches.
You also state that Person belongsTo Course... so you create a new Person for "Bob Jenkins" for each course that he's in? This seems excessive. You should probably look into a ManyToMany for this.
Without moving to a service, unfortunately, you'd want to change the indices on some if not many of the rows for the children of the Course you're trying to add a Person to, as that index is the sorted index for all the Persons in the Course.
I would suggest going back to a "Set", and do your sorting in the app. Your other question about sorting already told you not to override compareTo to just check the last name. If I were you, I'd forget about overriding compareTo at all (except to check IDs, if you want), and just use the sort() method, passing in a closure that correctly sorts the objects.
For practice I'm writing a shopping website where we have tables User and Item. A user obviously has_many items (when they are added to their basket), but the item, it belongs_to a User, even though many users will have the same item in their basket?
Furthermore, what if I want a list of items a user has added to their basket, but also a list of items they have viewed (for making suggestions based on searches), would it be better to have some 'through' tables: Basket and Viewed?
When you have this many-to-many relationships, you can use the HABTM schema:
Class User...
has_and_belongs_to_many :items
However, most of the time webshops use orderlines to keep up with items that users are purchasing. This means that an 'user' 'has_many' 'orderlines', an 'item' 'has_many' 'orderlines', an 'orderline' 'belongs_to' an 'user' and to an 'item'.
And maybe your orderlines will just be copies of items, and won't have a direct link because you don't want to alter the orderline after they have been processed. It really depends on the focus of your shop which scheme suits your needs.
Try to find some examples on the web and think about how you want to handle items, orders and baskets.
I'm used to separate things that are not the same, even if the relationship is one-to-one. So first of all I would recommend users from baskets (1:1-relationship).
After that a basket contains many items and items can be in multiple baskets (m:n-relationship). Make sure, that maybe a user likes to buy the same item multiple times.
views can be realised as a linking table between users and items: users have many views and items have many views, but one view is always linked to exactly one user and one item.
If I delete a CoreData record from an entity, does that also delete the relationship that record had with another record in another entity?
It depends on the Delete Rule that you have enabled for the relationship. The Core Data Programming Guide documents these (extracted below). Have a look in the inspector to see which option you have set for your relationships. If you have a specific data model which you want comment on you should edit your question to be more specific.
Deny
If there is at least one object at the relationship destination, then the source object cannot be deleted. For example, if you want to remove a department, you must ensure that all the employees in that department are first transferred elsewhere (or fired!) otherwise the department cannot be deleted.
Nullify
Set the inverse relationship for objects at the destination to null. For example, if you delete a department, set the department for all the current members to null. This only makes sense if the department relationship for an employee is optional, or if you ensure that you set a new department for each of the employees before the next save operation.
Cascade
Delete the objects at the destination of the relationship.
For example, if you delete a department, fire all the employees in that department at the same time.
No Action
Do nothing to the object at the destination of the relationship. For example, if you delete a department, leave all the employees as they are, even if they still believe they belong to that department.