问题
I am making a project where (from the perspective of school) you can calculate each student average. You can register a student (first entity) on a screen and subjects (second entity) on another screen. Student has name, email, grade and average as atributes, and Subjects has name. They relate many-to-many with each other.
I am trying to create a copy of subjects list to each student, then on each student i can register a grade to each subject. Like this:
Model concept
Model: !https://imgur.com/gmXyR5j
I've created a singleton of subjects since it is used more than one location:
import Foundation
import CoreData
class SubjectsManager {
static let shared = SubjectsManager()
var subjects: [Subject] = []
func loadSubject(with context: NSManagedObjectContext) {
let fetchRequest: NSFetchRequest<Subject> = Subject.fetchRequest()
let sortDescritor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescritor]
do {
subjects = try context.fetch(fetchRequest)
} catch {
print(error.localizedDescription)
}
}
func deleteSubject(index: Int, context: NSManagedObjectContext) {
let subject = subjects[index]
context.delete(subject)
do {
try context.save()
subjects.remove(at: index)
} catch {
print(error.localizedDescription)
}
}
private init() {
}
}
And, on my student screen, i've tried many thing but nothing is working.
The to-many relation of student with subject is called registeredSubjects
I've created a NSSET called subjectsManagerSet
to get values from the singleton, but it not working. Here what i've tried so far:
subjectManagerSet.addingObjects(from: subjectsManager.subjects)
Also tried to create a for loop of subjectManager.subjects
to add on subjectManagerSet
but it's not working too.
About errors, when i get samples from xcode output, it keep showing that subjectManagerSet
did not get values from subjectManager.subject
Error message:
2019-09-26 20:38:16.983725-0300 MyAverage[1734:62290] Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file /Users/vitorgomes/Desktop/Mentorizacao/MyAverage/MyAverage/Controllers/StudentsViewController.swift, line 119
(lldb) po subjectManagerSet
0 elements
The expected result is that i want a copy of subjects for each student instance, then i can add grades for each subjects for each student.
回答1:
The expected result is that i want a copy of subjects for each student instance, then i can add grades for each subjects for each student.
I am not addressing your problem as stated at the beginning of your question, but the one stated at the end, per the quote above.
I would encourage you to reconsider the structure of your model.
Perhaps something like this?
In this proposed model, you're able to assign to an object of the entity Enrolment
:
- a
grade
(anddate
) via these attribute properties; - a student via the one-to-many relationship property with a
Student
entity; - a subject via the one-to-many relationship property with a
Subject
entity.
In the following examples, I assume Core Data generated NSManagedObject
subclasses - that is - in the Data Model Inspector, set the value for Codegen = Class Definition (default).
(Personally and as an aside, I prefer to manually write NSManagedObject
subclasses for each of my entities and use Set
rather than the NSSet
, as I find it subsequently a lot easier to maintain type integrity in my code. But I have not done that here as most people who are new to Core Data will use the default for Codegen noted above.)
You're able to access these values in the following manner...
let student = Student()
print("Student name is: \(String(describing: student.name))")
if let studentEnrolments: NSSet = student.studentEnrolments {
for item in studentEnrolments {
if
let enrolment = item as? Enrolment,
let subjectName: String = enrolment.subject?.name {
print("Subject name for this student is: \(subjectName)")
}
}
}
Its easy to assign a subject enrolment to a student...
let enrolment = Enrolment()
let subject = Subject()
enrolment.subject = subject
student.addToStudentEnrolments(enrolment)
Then now or later, a grade can be applied to the enrolled subject...
let grade: String = "A"
enrolment.grade = grade
Of course the average becomes a mathematical function based on the sum of all grades for each student, divided by the count. This is in my humble opinion, better constructed as it is needed, rather than saved as an attribute with each Student
object.
Update
Im updating my answer to include a little database theory to explain my proposed object model.
According to Wikipedia, Database normalisation is...
the process of structuring a relational database in accordance with a series of so-called normal forms in order to reduce data redundancy and improve data integrity.
What does this practically mean to me? It means breaking my data down into its most discrete and unique parts, so that, in theory, I never need to enter any unique piece of data more than once.
Let me use a simple table example as a means of explaining this, as it might be set out in a spreadsheet (or your model concept):
Original Data
TABLE 1
A B C
1 STUDENT SUBJECT GRADE
2 Student1 Mathematics 8.8
3 Student1 Physics 7.0
4 Student1 Biology 6.0
5 Student2 Mathematics 5.0
6 Student2 Physics 9.0
7 Student2 Biology 7.0
Normalised Data
TABLE 1 TABLE 2 TABLE 3
A B C A B A B
1 STUDENT SUBJECT GRADE ID STUDENT ID SUBJECT
2 1 1 8.8 1 Student1 1 Mathematics
3 1 2 7.0 2 Student2 2 Physics
4 1 3 6.0 3 Biology
5 2 1 5.0
6 2 2 9.0
7 2 3 7.0
The normalised data uses relationships between the three tables. It stores the ID
(as a primary key) of each STUDENT
and each SUBJECT
, instead of the actual words. This is obviously far more efficient in many different ways, including but not limited to: bytes of stored data, ability to index, speed of data retrieval.
When you set a relationship property in your Core Data object model graph, you are doing the same thing...
So for your example, the Core Data object model graph Entity replaces TABLE. The Core Data framework automagically inserts a primary key column into the SQLite database for us when it constructs the tables and later a primary key unique integer when we programmatically add rows (records, a.k.a instances of an entity). While we don't have direct access to that as a developer (using Core Data), the Core Data framework allows us to build one-to-one, one-to-many and many-to-many relationships between two entities, that achieves the same outcome.
Enrolment Student Subject
A B C A B A B
Rel. Rel. Att. Rel. Att. Rel. Att.
∞ ∞ 1 1
Z_PK Student Subject grade Z_PK name Z_PK name
1 1 1 8.8 1 Student1 1 Mathematics
2 1 2 7.0 2 Student2 2 Physics
3 1 3 6.0 3 Biology
4 2 1 5.0
5 2 2 9.0
6 2 3 7.0
Att.
= Entity Attribute;
Rel.
= Entity Relationship;
∞
= many side of one-to-many Relationship (<<-);
1
= one side of one-to-many Relationship (-->)
Any questions, let me know?
来源:https://stackoverflow.com/questions/58126027/adding-elements-on-many-to-many-relation