-
Notifications
You must be signed in to change notification settings - Fork 115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Association Routes? #34
Comments
@sharathm this will be in shortly, I have the feature about half done |
Keenly anticipating the release :) |
This is the stack overflow thread that led me here.. http://stackoverflow.com/questions/25801426/sequelize-rest-api-generator |
Yeah I'm working on it as much as time allows, unfortunately it's not my full time job 😄. I currently have auto-association working for simple cases (BelongsTo, HasOne), but need a further discussion on how to handle the many to many cases and deeper levels that your example requires (/student/:studentId/course/:courseId/semester/:semesterId/subject). Specifically, those routes could be rather easily generated, but extending our idea of hooks to them is going to be more difficult. How would one express that exactly:
(^-- this is a joke) |
Does sequelize give a DB level representation of all associations between tables? I am thinking if its possible to generate a sort of in-memory database diagram and then map that to function calls to execute the required calls against the tables. |
@sharathm yeah sequelize does give us this information, it's more of a matter of massaging the epilogue design to accommodate deep levels of association. I have code that generates all the endpoints, a few tests put together and am now in the process of trying to tie it all together. I have a few questions. In your most complex example above: studentId is obviously the id of the student entry, but are subsequent id's meant to be the actual in-database id of the entry or an zero-indexed value based on the number of e.g. courses associated with that particular student. The simple cases aren't that difficult, it's once I start recursing into these deep levels of association that we really start stressing epilogue's original design. |
@breandr it would be useful if you could add your test/use cases here as well! |
@mbroadst - All are in-database Ids.. |
@sharathm oh I see, so that pairs down the number of things that need to be done. For instance in your example: |
Yes.. that is correct.. |
I'd advise against using aliases. This is how I see those two routes:
As you can see, the only ones that look the same on the face of it is |
I agree.. now that I think about it.. the whole idea of this is to resolve the objects in the route.. so that in the end, if i create a |
@breandr, @sharathm Hmm, I'm not sure I completely agree here but I'm trying to get a better handle on what the requirements are here since I don't actually require this functionality in anything I'm using epilogue for. If you don't mind I'd like to nix the "multiple course ids" bit for the sake of simplifying an already pretty big feature (a next step indeed). It seems to me that if you have the simple association case: /students
/students/:studentId
/students/:studentId/courses
/students/:studentId/courses/:courseId
So as you can see I think we agree with everything up until the last example. You said you think it should return some relevant student data as well? I haven't seen any other solutions that offer this, and I'm not even sure what student data you would be (the studentId or something?). As far as I can reason it, the last case there is essentially a full alias to /course/:courseId (except for the delete case I think), it has very little to do with the student, and would be provided almost for convenience. Am I understanding you guys correctly? I'm just trying to build up the test suite here to make sure when it actually is implemented we've covered all your use cases. NOTE: there is one more case I'm not mentioning but have added to my autogeneration code which is for hasOne/belongsTo. In this case if model User.hasOne(Role), and extra route would be provided such that I believe I've covered all the basic cases here (at least for one level deep), so let's see what holes you guys can punch in that before moving on to the next levels (Subjects, Semesters, etc). |
I totally understand putting the multiple id approach on hold, so we will move forward assuming I have a couple of issues. | Are you saying this should create a new record in the | | On the User.hasOne(Role), I was actually thinking of this scenario earlier. Sounds perfect. |
@breandr okay cool, thanks for the very helpful insight here (sorry everyone this seems like a pain but I'd rather get the feature done right the first try 😄). So it looks like I need to modify my last two presented cases accordingly: /students/:studentId/courses
/students/:studentId/courses/:courseId
Does that line up more with what you guys were thinking? I know you were saying you wanted to be able to update a specific column with PUT/PATCH on the second case here - can you be more specific about that? Also, it doesn't seem to make sense to me that the GET for the association would return a union of the student and course data, since by default if you use includes (auto association it will be termed), prefetch will be true so whatever associated data you have will be filled in in e.g. /student/:studentId |
@johngiudici @dchester do you guys have any insight you could lend to this? |
Nested resources more than one level deep are an anti-pattern, and should be avoided. A route like Furthermore, I believe it is nonstandard for nested resource routes to return data regarding the parent. The functionality described for associated routes also doesn't make much sense. Especially when regarding the This is how Rails, as one example, defines their nested resource routes functionality:
You should notice that the POST and DELETE methods affect the actual resource, they do not merely dis-associate the resource from it's parent (Though I believe it will update the associations in doing so). The Loopback framework also follows this convention, but also offers the functionality being described in this issue for many-to-many relations under a different route: |
@ackerdev - Thank you for the detailed explanation.. I now understand.. a RESTful API is suppose to return the state of the data as its stored.. its not intended to be a way to identify a data access pattern ..correct? |
@ackerdev those mostly look fine for one-to-many relationships (one magazine has many ads), but certainly don't work for many-to-many (many students have many courses example we were using previously). Furthermore, I would assume epilogue would generate |
@sharathm Eh... I think that's a bit too simplistic a view of what a RESTful API is for me to say that that is correct. @breandr
Won't work in what way? I assume you mean that you can't associate or dis-associate many-to-many relations, in which case, yeah. I'm not personally sure what makes for the most appropriate solution here for that. If you have a through model it's easy, but without a through model I'd have to give it some thought. I don't necessarily disagree with removing the superfluous routes. I believe only the |
Correct. My approach would be to use the same routes and methods for both one-to-many and many-to-many, but based on what it is, it will be handled differently. One-to-many will do as you suggest (create/delete), and many-to-many will instead associate/disassociate. The only negative I can see with this approach is that you would need to have an understanding of the relationships between resources (i.e. know if they are one-to-many or many-to-many). I don't think this is an unreasonable requirement when creating a private API of which you should have an intimate knowledge of the relationships anyway. The Loopback approach of adding "rel" to the route makes it more obvious as to what the route will do, but I have the same issue with it that you do: "rel" isn't a resource.
I guess it comes down to taste. I'm sure there are a large number of people who would like to access it using either route. @mbroadst is it possible to have a config that will include or not include these routes? Or would you prefer just going with one way (for now at least)? |
I agree. And if this is the approach, I think it goes even more in favor of not including the other superfluous routes. 👍 |
I think you'll run into a problem with assuming that you can expect dis/associating routes on every resource is that not every resource's association can be interacted with in that way. A many-to-one model that depends on it's parent for existence (eg a Making the framework smartly choose whether to create/delete or dis/associate based on whether or not it is a 1:N or N:M could create a pretty significant gotcha that I think we would really be best off avoiding. In my opinion, the ideal solution is to have a 'through' model. To the API user, it just looks like another resource, and they can manipulate it just the same as any other. I don't know if it's realistic to tell anyone who wants association manipulation to use through models, but I think it creates the most appropriate API for manipulating them. |
Right, but I would say that in most cases they can be, and epilogue should
If this is a one-to-many (a school has many courses) and you
As above, it would delete for one-to-many (if we have the superfluous Could you expand on "through model"? A quick google makes me think this is
|
Can we decide on the removal of the superfluous routes for now? I think that will remove some confusion. |
I don't see it making sense to generating that route at all given it'll just be an alias, then.
I think this is because of you having a pre-determined idea of what functionality it should have, but I don't think it's actually intuitive. The verb
It is a model for your pivot table. With Sequelize when defining a has-many association you can specify a |
@mbroadst I most definitely will take a look and see if there is anything I can contribute at all. I'm currently working on a project that will depend strongly on epilogue and associations. This might seem like a rookie question but I simply used |
@mikemimik if you want to try it out in another project you'll want to replace your dependency with a git url as documented here. So you'll want something like:
|
@mbroadst oh that's great, I didn't realize you could do that in the |
@mikemimik shouldn't be, it was all being hidden behind a top level "associations" flag. Look at the added test cases for help, I also think the commits should be incremental enough to follow |
Hey. I'm using epilogue to refactor a rest interface and I'm glad I don't have to write all this CRUD crap anymore. What's missing to get this to master and how can I help? |
In branch
|
No route will be created for this association;
Not sure where to from here though; I don't know much about sequelize internals. |
@Fridus any interest in taking a stab at this? looks like the broken test is already provided |
@mbroadst I'll look at that. It is when the associated object is the same model |
@mbroadst Sorry but I don't find how to do that.
If |
@Fridus hm, okay looks like we're both pretty slammed 😄 Looks like this was due to a change in Sequelize, I posted a fix. @Fridus do you feel confident that the auto-association is ready for an initial release? I'm releasing a 0.5.2 today, but I'd theoretically like to get the auto-associations into a 1.0 release relatively soon |
Is this done yet? if so can we document? |
I am pretty anxious to get this working as well. Are there any workarounds for this yet? It would appear from here that something does work? |
@AddoSolutions this is partially implemented (all read-related operations) as you can see from the above comment. If you need support for further operations we can discuss it here, and I can provide assistance as needed. |
Ok, that's cool. How did we want to implement this? My thought is that we will can loop through the relational attributes. Example: Person:
Then to post to it: {
"name": "John Doe",
"Group": 123,
"Posts": [1,2,3],
"Organizations": [3,2,1]
} From there we can map the relations for the given object. I have tried this a bit today, and to do the actual mapping, this is how I did it: for(var type in relations){
var model = relations[type].target.build({"id": request[type]});
model.isNew = false;
targetModel['set' + type](model);
} Of course accounting for multiple relations, that is a simple example. Is this the appropriate way to do this, or is there a better way? Thoughts? |
Not to bump this, but just wanted to get thoughts before I go about this. I am not totally sure on the best way to utilize the sequalize API here. |
hell of a long thread. Great to see all the interest. I ran into an issue with dealing with composite keys. Thanks to the hook/controller structure it was trivial for me to address the issue: Using the school example... Ran into issues with updates and accidental changes because the last key is not unique. It would probably good to have a warning/check either here or in sequelize that if the id is one of a composite key, then to require all keys for an update. My fix was that in the before of all requests, I parse the route and inject the additional criteria. Just wanted to document this here in case someone runs into the same issues. Thanks for making this. |
Can I try this out on the main repo? I'm just interested on the list functionality in single routes ( |
@danielo515 yes what implementation exists is indeed a part of the main repo and has been released. It's unfortunately undocumented (please let me know if you're interested in doing some of this 😄), but you can enable automatic associations by specifying |
I recently came to know epilogue, and I am just starting to play around with associations, refactoring all my CRUD code to use the cleaner Resource concept... One thing I find missing from the current implementation (and that wasn't really discussed here it seems) is the creation of a resource with associations in one request. Implementing this would follow the sequelize behavior when doing :
It would also help to be able to configure, for a resource:
This would allow decoupling the Data Models from the REST Resources. I'll try to implement this and I'll post a PR if I can come up with something usable... |
Is this broken on sequelize 4? |
Do you support association routes as well?
If I have a scenario for example..
Students have many courses
Each course has many semesters
Each semester has many subjects
and assuming the associations are defined correctly in Sequelize, can we auto generate routes like
POST /student/:studentId/course
GET /student/:studentId/course
PUT /student/:studentId/course/:courseId
POST /student/:studentId/course/:courseId/semester
POST /student/:studentId/course/:courseId/semester/:semesterId/subject
The text was updated successfully, but these errors were encountered: