|  | jsdata |  | jsdata | 
|  | crud |  | crud | 
|  |  |  |  | 
|  |  |  |  | 
|  |  |  |  | 
|  |  |  | ``` | 
|  |  |  | class Book (models.Model): | 
|  |  |  | title = models.CharField(max_length=50) | 
|  |  |  | authors = models.ManyToManyField(Author) | 
|  |  |  | publisher = models.ForeignKey(Publisher, null=True, on_delete=models.SET_NULL) | 
|  |  |  |  | 
|  |  |  | // JSData v3, JSDataHttpAdapter v3 | 
|  |  |  | DataStore.defineMapper(name, {  // 'Book' | 
|  |  |  | // ... endpoint, etc | 
|  |  |  | relations: { | 
|  |  |  | belongsTo: { | 
|  |  |  | Publisher: { | 
|  |  |  | foreignKey: 'publisher', | 
|  |  |  | localField: 'publisherObj', | 
|  |  |  | }, | 
|  |  |  | }, | 
|  |  |  | hasMany: { | 
|  |  |  | Author: { | 
|  |  |  | localKeys: 'authors', | 
|  |  |  | localField: 'authorObjs', | 
|  |  |  | } | 
|  |  |  | }, | 
|  |  |  | }, | 
|  |  |  | }); | 
|  |  |  |  | 
|  |  |  | DataStore.defineMapper(name, {  // 'Author' | 
|  |  |  | // ... endpoint, etc | 
|  |  |  | relations: { | 
|  |  |  | hasMany: { | 
|  |  |  | Book: { | 
|  |  |  | foreignKeys: 'authors', | 
|  |  |  | localField: 'books', | 
|  |  |  | } | 
|  |  |  | } | 
|  |  |  | }, | 
|  |  |  | }); | 
|  |  |  |  | 
|  |  |  | DataStore.defineMapper(name, {  // 'Publisher' | 
|  |  |  | // ... endpoint, etc | 
|  |  |  | // This relationship to Book is only necessary if you want Publishers to have the 'books' array (named using localField), listing all books they are related to. But 'publisherObj' can still be loaded onto a Book even without this side of the relationship defined. | 
|  |  |  | relations: { | 
|  |  |  | hasMany: { | 
|  |  |  | Book: { | 
|  |  |  | foreignKey: 'publisher', | 
|  |  |  | localField: 'books', | 
|  |  |  | } | 
|  |  |  | } | 
|  |  |  | }, | 
|  |  |  | }); | 
|  |  |  |  | 
|  |  |  | this.Book.readList().subscribe(books => { | 
|  |  |  |  | 
|  |  |  | // loadedBooks === [ | 
|  |  |  | //   { | 
|  |  |  | //     id: 1, | 
|  |  |  | //     title: 'To Kill a Mockingbird', | 
|  |  |  | //     authors: [1], | 
|  |  |  | //     authorObjs: undefined, | 
|  |  |  | //     publisher: 1, | 
|  |  |  | //     publisherObj: undefined | 
|  |  |  | //   } | 
|  |  |  | // ] | 
|  |  |  |  | 
|  |  |  | let bookPromises = books.map((book: any) => book.loadRelations(['publisherObj', 'authorObjs'])); | 
|  |  |  | Promise.all(bookPromises).then(loadedBooks => { | 
|  |  |  |  | 
|  |  |  | // loadedBooks === [ | 
|  |  |  | //   { | 
|  |  |  | //     id: 1, | 
|  |  |  | //     title: 'To Kill a Mockingbird', | 
|  |  |  | //     authors: [1], | 
|  |  |  | //     authorObjs: [ | 
|  |  |  | //       { | 
|  |  |  | //         id: 1, | 
|  |  |  | //         first_name: 'Harper', | 
|  |  |  | //         last_name: 'Lee', | 
|  |  |  | //         books: [ | 
|  |  |  | //           { | 
|  |  |  | //             id: 1, | 
|  |  |  | //             title 'To Kill a Mockingbird', | 
|  |  |  | //             authorObjs: [ { id: 1, ... } ], | 
|  |  |  | //             ... | 
|  |  |  | //           } | 
|  |  |  | //         ] | 
|  |  |  | //       } | 
|  |  |  | //     ], | 
|  |  |  | //     publisher: 1, | 
|  |  |  | //     publisherObj: { | 
|  |  |  | //       id: 1, | 
|  |  |  | //       name: 'HarperCollins', | 
|  |  |  | //       books: [ | 
|  |  |  | //         { | 
|  |  |  | //           id: 1, | 
|  |  |  | //           title: 'To Kill a Mockingbird', | 
|  |  |  | //           publisherObj: { id: 1, ... }, | 
|  |  |  | //           ... | 
|  |  |  | //         } | 
|  |  |  | //       ] | 
|  |  |  | //     } | 
|  |  |  | //   } | 
|  |  |  | // ] | 
|  |  |  | }); | 
|  |  |  | }); | 
|  |  |  | ``` | 
|  |  |  | \ No newline at end of file |