Remove object from Active Record Relation without deleting it - sql

I'm working in Ruby on Rails 4 with Postgresql, and I've hit a bit of a snag. We have an Active Record model called AttendanceRecord which belongs to an AttendanceDay, AttendanceSwipe, Course, and CourseTimeSlot. Attendance Records were supposed to be unique on these fields, but something went wrong and duplicates snuck in. So, I wrote a method to find all of the Attendance Records which were duplicated and only keep one of them.
In the course of that method, I built an Active Record Relation of objects that shared the same attributes, like so:
records = AttendanceRecord.where(course_id: attributes[0], course_time_slot_id: attributes[1], attendance_swipe_id: attributes[2], attendance_day_id: attributes[3])
Nice relation, right?
Next, I found the object that I wanted to keep and named it to_keep. Then, I tried to remove just that object from the relation, like this:
records.delete(to_keep)
Unfortunately, I discovered that the delete method works a little differently on a Relation than it does on an Array. Instead of simply removing the object from the list, it actually does delete it from the database (without the callbacks).
So: I'm wondering if there is a method that I'm missing that will remove my to_keep object from the Relation without actually touching the object itself. Then, I'll be able to safely call records.destroy_all and happily go about my business. :)

If you want to exclude an object from a relation you can do so by id. For example:
records.where('id <> ?', to_keep.id).destroy_all
or, thanks to #trushkevich, in rails 4 you can do:
records.where.not(id: to_keep.id).destroy_all
This means that destroy_all will be called on the records you've identified already but excluding the to_keep record.

Related

Django: How do I update only ONE record in my database?

I'm having a lot of trouble trying to update a single (record) object in my database.
context['eval_list'] = Evaluering.objects.update(eval_relationer_id=self.kwargs.get('pk'))
I use objects.update, but it updates ALL my objects fk. How do I achieve only updating one object? I have also tried this:
context['eval_list'] = Evaluering.objects.update_or_create(eval_relationer_id=self.kwargs.get('pk'))
But this creates a new object and does not update the record that I want to update. I know why it creates a new objects, and it is because the FK I'm trying to update is null. Surely, there must be a way to only update and not create a single record? What am I missing here?
I tried adding a filter, but it feels redundant? I tried this:
context['eval_list'] = Evaluering.objects.filter(eval_relationer_id=self.kwargs.get('pk')).update(eval_relationer_id=self.kwargs.get('pk'))
I did consider trying to create an ID of the FK instantly and not later on, but I couldn't really get that to work, but if I created an ID then the update_or_create would work because an ID would exist already, BUT I cannot believe that I can't update a single object without create?
If creating the ID earlier on is the only work around, I will have to figure out how.
MyModel.objects.filter(pk=some_value).update(field1='some value')
The filter gets your object (returns the Queryset with only that object), then the update changes some other field that is not the PK to whatever you want.
In your case probably something like this:
context['eval_list'] = Evaluering.objects.filter(eval_relationer_id=self.kwargs.get('pk')).update(some_attribute='some value')
After help from #Hanny I've figured out was going wrong.
I was trying to filter by the eval_relationer_id, when I should have been filtering by the evaluation pk and getting that specific PK. Otherwise I would be updating ALL the values which is not what I wanted.
So by filtering by pk:
filter(pk=self.kwargs.get('pk'))
And updating by the attribute / fk that I want to update
update(eval_relationer_id=self.kwargs.get('pk'))
This is the end-result:
context['eval_list'] = Evaluering.objects.filter(pk=self.kwargs.get('pk')).update(eval_relationer_id=self.kwargs.get('pk'))

Many-to-many relationships with NSFetchedResultsController

I have a model that looks like this:
and want to display it in a UITableView using an NSFetchedResultsController. I want to list all of the Objects for a given Owner, grouped by Group. For example, if we were to view the groups for some Owner Owner A the table view might look like this:
Group A
Object A
Object B
Object C
Group B
Object A
Object D
Group C
Group D
Object C
Object E
It's also important that the Groups be ordered by their name attribute (not shown in the schema above) and that NSFetchedResultsController delegate methods get called whenever Objects are modified or added/removed from a Group.
Given those requirements, I have set up the NSFetchedResultsController to fetch a bunch of Objects with a predicate like [NSPredicate predicateWithFormat:#"ANY groups.owner = %#", someOwner], which throws an NSInvalidArgumentException exception: "to-many key not allowed here". I have tried a few other predicates, but am stuck.
Any suggestions? Is this not something I should be using an NSFetchedResultsController for? Or is there a better way to model my data?
Thanks!
Edit: I actually got the predicate working with the above code, my mistake was in my sectionNameKeyPath argument. I was trying to pass in groups.name, which was what was producing the error. I can see why that way wouldn't work, but am struggling to find a different way to achieve the desired results. Perhaps a join object?
Edit 2: This works somewhat well with a join object like this:
There are two downsides that I see now. The first is that I have to enforce uniqueness myself using code. That's easy enough, but a bit of a nuisance. The second downside, that I don't yet see a way around, is that the NSFetchedResultsController will not call its delegate for updates to Objects anymore. I can live with that downside for now, but am happy to hear better suggestions.
I think the problem you're going to see is that if you have a fetched results controller searching for Objects then it's going to find each Object only exactly once. So each object will appear exactly once in your table. Whereas what you sort of want to do is invert things and find all the Groups, then displaying all the relevant contained Objects. In terms of the fetched results controller, rather than finding rows and thereby being able to divide into sections you want to find sections and thereby figure out what to supply as rows.
The easiest thing, I think, would be to create a fetched results controller on Group and to add an intermediary object of your own that remaps those to sections and supplies group.objects (with a suitably deterministic sorting applied) as the rows per section.
If you want the rows to be dynamic then I guess the easiest thing is to create a fetched results controller per section based on the feedback of the Group controller.
If that's all getting a bit painful and you want to just write your own collection logic then you'll probably want to catch NSManagedObjectContextDidSaveNotification and rerun your logic whenever that occurs. For memory efficiency reasons you probably want to keep hold only of the objectIDs and to get the appropriate objects only when the table view requests them via the existingObjectWithID:error: method on the context. Core Data has a built-in caching mechanism that responds appropriately to memory warnings so that's all written for you.
You shouldn't need a join object to get at data in Many to Many relationships
See this answer: How to deal with many to many relationships with NSFetchedResultsController?
And example project: https://github.com/dmathewwws/Many-to-Many-CoreData

How to determine number of children of a record?

Good afternoon everyone!
I'm studying NHibernate, and decided to make some changes. Among them, I noticed that some fields are unnecessary. So I bring my doubt:
I have a list, let's call it Class_List within each study class, I can have N students for each class. Within the list Class_List, I also have other properties as simple as the name of the class.
How I see it is unnecessary to store how many students I have in the database, I would, in a single query, how many records I have. This, using NHibernate.
Is this possible? How?
Best regards,
Gustavo.
Edit: I've forgot to say one thing... I want to return this number of record, as a column. But this column is not mapped in my .hbm.xml file.
If students are mapped as a collection on Class, you can try using something like this:
var numberOfStudents = session.CreateCriteria<Class>()
.Add(Restrictions.IdEq(1))
.CreateCriteria("_students", "students")
.SetProjection(Projections.RowCount())
.UniqueResult<Int32>();
Where '1' is the id of the class (you can use other property) and '_students' is the name of the students collection.

NHibernate: If two calls are made to CreateCriteria, which list is Get<T> going to retrieve the object from?

Under one UnitOfWork (session) I may call CreateCriteria twice. My first call is to populate a grid for data editing. Let's say the data has been edited and flushed (saved) to the database and the grid is still open. After the data is edited, I may call CreateCriteria a second time to retrieve a list of objects that are validated and found in error.
Lets say ObjectA was retrieved by both calls to session.CreateCriteria. It was edited in the grid but found in error within the second list.
The first question would be: Considering first level cache, is ObjectA--that was retrieved from the second call to CreateCriteri--represent the one retrieved from the first call? or, better yet, did NHibernate "detect and reuse" ObjectA from the first call assuming the keys did not change?
To my final point in question: I want to edit ObjectA which was found in error, and let's say it was brought up in a ListBox. Therefore, I want to highlight that object, call session.Get()(key) in order to retrieve it from cache, then bring up a change form to change ObjectA's properties. Which object am I changing? The one from the first call to CreateCriteria or the second call? Are they the same?
Thank you in advance.
Second level cache
Take a look at http://ayende.com/Blog/archive/2006/07/24/DeepDivingIntoNHibernateTheSecondLevelCache.aspx and http://www.javalobby.org/java/forums/t48846.html
From the former:
The second level cache does not hold
entities, but collections of values
So, with caching setup properly, NHibernate will be able to recreate your object without having to get the actual values from the database. In other words, the object will be created the same as when it wasn't in cache, except that since the values are cached, NHibernate won't actually query the database since it already knows what's in there.
I'm not quite sure what you mean by "validation" and "found in error". Are you validating before insert? Typically my entities are validated before the insert/update and won't actually be inserted/updated if invalid.
Validation aside, what I think you're asking is that if you:
save something
do a flush
retrieve an item (from a new session) with the same key as the one saved in step 1
will you be retrieving the same reference to the object you saved in step 1(?). And the answer is no since NHibernate does not cache the OBJECT but rather the values so it can create a new entity populated with the cached values (instead of actually performing a DB query).
However, does that really matter? If you overload Equals such that equality of 2 entities are based on their ID, then finding the same (not reference equal, but same) item in a grid (or a hash of any kind) should be a snap.
First level cache
I didn't realize you were talking about 1st level cache. 1st level cache works as an identity map and does cache the instance of the object. Therefore, if you do 2 selects from the db based on the same ID, you will retrieve the same instance of the object.

In a datacontext are inserted values not available within the datacontext until after submitchanges?

I'm going through an XML file of articles and the journalist(s) that wrote them. As we are adding the articles into _Data our datacontext we may come across a journalist that needs adding so we do this:
newJourno = New journalist With {.name = strJournalist}
_Data.journalists.InsertOnSubmit(newJourno)
.articles_journalists.Add(New articles_journalist With {.id_journalist = newJourno.id, .id_article = .id})
However subsequently we may come across this same journalist again and nothing is returned when we do this:
Dim journo = _Data.journalists.Where(Function(s) s.name = strJournalist).SingleOrDefault
So it uses the code above again to insert the same journalist again.
Once all of our inserts are done we do a submitchanges. At this point it has a head fit:
INSERT statement conflicted with COLUMN FOREIGN KEY constraint 'FK_articles_journalists_journalists'. The conflict occurred in database 'blah', table 'journalists', column 'id'. The statement has been terminated.
From looking through the sql generated in sql profiler you can see that it is trying to add some journalists more than once, this will fail as the name must be distinct. The subsequent records that are trying to be inserted with these journalists are failing as the journalist wasn't updated.
Surely if I have a collection of journalists, add some to it and then look in my collection I should see all of them and not just the original ones. I can fudge it I guess by doing a submitchanges but that seems a bit silly.
Thanks in advance,
Dave.
If you want to add two child-parent rows to the database, you must assign the entity, instead of the Id column, the Id will be autogenerated and will be available only after the submit changes.
You have to do a articles_journalist object, and then assign the newJourno entity to this:
articles_journalist.journalist = newJourno;
CMS is right about needing to assign the object, not the id.
However this doesn't seem to get around the problem of the datacontext not realising that it has had new stuff added to it until you submitchanges. I can only presume this is by design and therefore I am now calling submitchanges as and when the code inserts objects that we later search for.
"the name must be distinct."
This is a serious design flaw. Person names are never unique.