Evolving a RESTful Django Contribution
I posted an announcement to the django-users list about the django-restful-model-views contribution to get some feedback on the approach. The main responder, Malcolm Tredinnick, gave me a lot to think about and along with more research into how Ruby on Rails (RoR) implements REST, I have new ideas about how to improve the contribution.
responding to feedback on the contribution
Malcolm underscored that the contribution currently deals only with RESTful model views and does not address the larger issue of implementing REST within Django for resources that do not map directly to models. In my first post, I raised this issue, bringing up the old Struts discussions of mapping form objects to the model layer, particularly with such things as modal forms, but I never returned to address it. Instead, I began following what little information I had to go on from discussions in the RoR community, which focused on the model layer.
In retrospect however, this may not have been a bad place to start. In addition to learning from the existing discussions of implementing a similar approach in RoR, creating a RESTful layer over the models is both straightforward and a natural fit in Django. In Django, the model layer is extremely important, and it’s relatively easy to discover information programmatically about the installed models for a Django project. Furthermore, a number of Django’s features and contributions are driven off this model layer, such as generic views, and it was natural to build RESTful URL and view mappings that implemented the appropriate ORM/generic views for the models. The current approach therefore ties together several nice Django features and fits with Django’s use of conventions to organize code and the general philosophy of minimizing what would otherwise be tedious coding. Time will tell is this provides a convenience to developers or is simply a temptation to design an application inappropriately.
But as Malcolm pointed out, this approach does not address all possible resources you might find in an application, nor does it necessarily address any meaningful resources within a given application, or at least, not in a clean way. Malcolm offered some illustrations of this that fit with the concerns I raised at the end of my first posting on REST in this series, but which I have yet to address. Resources have more to do with the user’s perspective of the site and how to interact with it, and as such, should be treated closer to the view layer than the model layer. Therefore, a one-to-one mapping between resources and models should not be assumed, at the very least.
I think the current implementation can be refactored to allow resources to be created more arbitrarily to fit the design of a particular application while at the same time still providing the automatic RESTful methods on top of the model layer when this would be a convenient approach in an application. A more generic super class could be extracted from the current Resources/Resources_Member classes that preserves the existing class and method structure but which raises Http404 exceptions for each RESTful method. This structure could then be safely subclassed as needed by the application, or alternately, you could use the subclass that implements the existing RESTful model views using generic views, if that is how your application is designed.
After all, in the current implementation, up to the point of making ORM calls on a corresponding model class, the Resources/Resource_Members are implemented as views for which URL mappings have been automatically created, so decoupling this structure from the model layer will be quite natural, and preserve the automatic URL generation and the approach I have taken to organizing the view code for a Resource.
How the resources are configured for applications may change as a result. If the model oriented subclass approach is used, then it may still be reasonable to look for a RESTFUL_APPS entry in settings.py to kick off the process. However, if a particular application has a finite number of resources which do not naturally map to model classes, then perhaps just specifying a list with what would essentially be labels for naming the implemented resource view classes, then using those labels to generate URLs, etc. would be enough. Alternately, if there are a finite number of resources which may or may not map to models, then a dictionary of resource labels optionally mapped to model classes (or None) could be provided for more flexibility. In fact, Python provides convenient type checking so several of these approaches could be implemented side by side and easily checked, allowing for simple and advanced uses of the contribution.
I might also suggest that if a developer takes the approach of specifying labels for individual resources to implement, this may be better put in the urls.py file instead of setttings.py. When specifying RESTFUL_APPS, putting it alongside INSTALLED_APPS made sense, but as the individual resource specification approach is more indicative of what the url mapping and view code layer will look like, it may be more natural to see this in urls.py and maintain all of that information in one place.
As for the organization of the view code itself, it may be worth pointing out that REST in RoR seems to be very tightly integrated with the model layer to the point that URL mapping (or routing if you prefer) and the organization of the RESTful controllers (views in Django terminology) is based on one to many database relationships instead of the way I have organized the code, originally based on a blog post by Charlie Savage, to deal with groups of model instances, then individual model instances. So, there are important similarities and also some fundamental differences between my approach and RoR’s with some interesting implications.
The RoR approach would typically translate to an aggregate structure in OO terms, which has a certain logic to it, and I could be convinced that this is a better approach, especially when implemented over one to one object relational mappings as Django does. But to my current thinking, the approach I surmised from Charlie Savage seems truer to a resource oriented view of an application, and, as outlined above, could be used to organize the controller/view code apart from the model layer. And in truth, with my current approach, which uses inner classes to distinguish between group/member resource behavior, you could implement a composite object approach just as easily, and this is basically the approach I began anyway, in which the appropriate model class becomes a field in the Resource object. Malcolm suggests that my approach may not seem logical to everyone, and that’s true. Perhaps I should say that it follows a certain logic, and one that I think is a promising way to think about resource orientation in a Web application in object oriented terms. (I am not a heavy user of inner classes, however, and this may not be a good way to use them.)
Malcolm pointed out that the current code assumes that interaction with the application will come from a standard Web browser being used by a human, and he is absolutely correct. Conquering this use case was my initial motivation, but now there are some obvious improvements that can be made to correct this limitation. For example, the current implementation only supports PUT and DELETE by overloading POST and using a hidden ‘_action’ form field. I should also directly support PUT and DELETE (and perhaps other HTTP methods like HEAD and OPTIONS) for clients that are not so limited. Also, reading about REST in RoR is making me think that ‘_action’ should really be named ‘_method’.
Malcolm also raises the possibility of service discovery of an application’s resources. I really need to educate myself further about the ATOM Publishing Protocol he mentioned and also things like WADL, but basically, I think it would be straightforward to create templates for such views and tack on entries to urlpatterns that ultimately point to them as a value added part of this contribution. And like everything else in the contribution, the goal would be to dynamically generate such views.
RoR also has the idea that there is built-in support for responding to clients in various formats, either by examining the accept field of the HTTP header, or by examining the URL for indicative file extensions, such as *.html or *.xml. Django’s approach to pattern matching URLs could provide an interesting way to implement this functionality, although I don’t know exactly what it might look like at this point. At the very least, I can modify the contribution to begin generating URL mappings that capture this information for the view being called and then try to figure out some clever way of using that data later.
RoR also provides help outputting RESTful URLs in templates for the purpose of constructing links and buttons used to interact with the application. Again, I think that with the way Django handles URL/view mapping, there is an opportunity here to do the same thing very cleanly. This might involve moving the URL generation capability into the resource class itself, and somehow reusing the regular expressions to populate Django’s urlpatterns and also provide URLs to the templates.
Despite my (perhaps unfounded) reservations about a few aspects of how REST is implemented in RoR, this seems to be the place where the RESTful Web application ground is being broken and where ideas are being tested, and although I have only a basic understanding of Ruby and RoR, I have been doing my best to investigate how RoR implements REST and that has sparked some new ideas about how to implement REST in Django.
With the generous feedback I have gotten, and my increased familiarity with how REST is implemented in RoR, I think I am finally beginning to have confidence in the future of this contribution. I think there is something here that can make a significant contribution to Django and provide important and convenient new features for developers.
But even if I am wrong about this, I am going to continue developing the contribution in my oh-so-plentiful spare time, if for no other reason than this work is forcing me to think more deeply about RESTful design and implementation than I might otherwise, and even if I make mistakes along the way, they will provide rich opportunities for learning.
- New contribution announcement: RESTful model views. Posted on django-users Google list.
- django-restful-model-views contribution
- RESTful Rails Development by Ralf Wirdemann and Thomas Baustert.
- REST Controller for RAILS on Charlie Savageâ€™s Blog
previous posts in this series
This work is licensed under a Creative Commons Attribution - ShareAlike 2.5 License.