Home

Using modelviews with models

Introduction

If you're building a resource-driven app, chances are you'll have views that map closely to Django models. For instance, you might have a Post model, and you want to create views that lets people interact with this resource. In this case, it would be redundant to define create, retrieve, update and delete functions in your views again and again.

For this reason, Django provides a helper class that let you create a View class for a Django model.

For example:

>>> from django.views.generic.model_views import ModelView, HtmlResponder, JsonResponder

# Defining your blog view
>>> blog = ModelView(Post.objects.filter(is_online=True),
...         responders=(HtmlResponder, JsonResponder), methods=('GET',))

# Using this one in your urls
>>> urlpatterns = pattern('',
...     url(^blog/(?P<slug>[-\w]+)/(?P<format>(html|json))?/?$), blog, name='blog'),
... )

Resources

Goal

API

get_subset

get_responder

get_form

save_form

read

create

update

delete

Nested resources

Sometimes, you need to deal with nested resources:

  • Sales for a given employee
  • Results for a team on a given time range

The get_subset function above allows you to customize the way you filter the original queryset given (URL) arguments but there is a shortcut when you declare your URLs. You can pass lookup_kwargs argument which must be a dictionary with filter argument as key and key mapped string as value. The key mapped value will be completed with URLs' arguments.

Here is an example of what can be done for comments of a given user:

>>> comments = ModelView(Comment.objects.all(),
...         responders=(HtmlResponder,), methods=('GET',))
>>> urlpatterns = pattern('',
...     ('^/users/(?P<username>[-\w]+)/comments/$',
...         comments,
...         {'lookup_kwargs': {'user__username__exact': '%(username)s'}}),
... )

If we GET the /users/guido/comments/ URL, the final queryset will be:

>>> Comment.objects.filter(user__username__exact='guido')

which returns all comments owned by guido user.

Responders

A responder is a class that subclasses, django_modelview.generic.responders.BaseResponder and whose class name must end with Responder. A given ModelView may have any number of responders, which are specified using the responders parameter as follows:

from django_modelview.generic.rest_views import *
responders = (HtmlResponder, JsonResponder, YamlResponder)
articles = ModelView(Article.objects.all(),
                 responders=responders,
                 methods=('GET',))

The first responder will be used as the default if another responder is not explicitly chosen. Thus, in the above example HtmlResponder will be used as the default responder.

In addition, each responder is also associated with one or more mimetypes. These mimetypes are used for determining the appropriate responder to use to respond to a request. Thus, there are three ways that a ModelView will attempt to discover the appropriate responder for a given request:

  1. If format is explicitly set in the request's url, it will use that format:

    mv = ModelView(Article.objects.all(),
                   responders=(HtmlResponder,JsonResponder,YamlResponder),
                   methods=('GET',))
    ('^article/(?P<format>(html|json|yaml))?/?', mv)
    

    Thus, an incoming request that looks like /article/html/ would always use the HtmlResponder.

  2. If the format is not explicitly made known, the ModelView will attempt to determine the correct responder using the request's mimetype. For example, a request with the mimetype text/yaml would be responded to using the YamlResponder. The full list of associated mimetypes are discussed below.

  3. Finally, if there is no explicit format in the url, nor is there a matching mimetype, then the default responder--the first in the list or tuple passed to the responders argument of the ModelView initializer--will be used instead.

BaseResponder

BaseResponder is the foundation of all the other responder classes.

Subclassing BaseResponder

If the existing responders are inadequate for your application, it is easy to subclass BaseResponder. You will need to override two methods:

  1. element(self, request, obj) returns an HttpResponse containing the appropriate rendering of a single instance, obj.

  2. render_list(self, request, object_list, paginator, page_obj) or list(self, request, paginate_by, allow_empty). Only one of these two methods will need to be overridden.

    If possible, it is preferable to override render_list, which has already restricted the paramter object_list to viewable instances, and has handled most pagination logic as well.

    list on the other hand does none of this logic for you, and you must implement all such restrictions and partitioning on your own.

    Both list and render_list should return an HttpResponder object.

HtmlResponder

The HtmlResponder renders objects using the Django template system. If attempting to render a list of objects, it was try to use the appname/modelname_list.html template. If attempting to render a single object, it will try to use the appname/modelname_detail.html template.

Is associated with the text/html mimetype.

JsonResponder

The JsonResponder serializes the response using the Json serializer at django.core.serializers.json.

It is associated with the application/json mimetype.

XmlResponder

The XmlResponder serializes the response using the XML serializer at django.core.serializers.xml_serializer.

It is associated with the application/xml mimetype.

YamlResponder

The YamlResponder serializes the response using the Yaml serializer at django.core.serializers.pyyaml.

It is associated with the text/yaml mimetype.

AtomResponder

The AtomResponder returns the contents as an Atom feed.

It is associated with application/atom+xml mimetype.

RssResponder

The RssResponder returns the contents in an RSS feed.

It is associated with application/rss+xml mimetype.

Authentication

Authentication is based on functions and allows fine-grained control over your resources given HTTP verbs.

We had seen that you can define allowed methods with a tuple:

>>> blog = ModelView(Post.objects.all(),
...         responders=(HtmlResponder, ), methods=('GET', 'POST'))

Alternatively, you can specify an authentication function for each method using a dictionary:

>>> from modelviews.authentications import django_authentication
>>> blog = ModelView(Post.objects.all(),
...         responders=(HtmlResponder, ),
...         methods={'GET': False, 'POST': django_authentication})

This way, only authenticated users will be able to POST new posts to the blog.

TODO: create a collection of most used dictionary in order to provide shortcuts.

django_authentication

Allows Django's authenticated users (check is_authenticated() function).

django_superuser_authentication

Allows Django's superuser users (check is_superuser field).

django_staff_authentication

Allows Django's staff users (check is_staff field).

Examples

Updated

Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.