Django application for smarter CRUD-based applications building.
Well, it's mostly about CRUD-based applications - create, read, update and delete different objects, but is not limited to that. If you want admin-like application, but you need a full control on source, than django-smarter
may fit your needs.
Warning! There are some backwards incompatible changes in v0.5
Watch section Customise views.
- Requirements:
- Django >= 1.3
To install:
pip install django-smarter
Then add smarter
to your INSTALLED_APPS
:
INSTALLED_APPS = (
...
'smarter',
...
)
Let’s define a simple model:
class Page(models.Model):
title = models.CharField(max_length=100)
text = models.TextField
def __unicode__(self):
return self.title
Now you can create generic views for the model.
In your urls.py:
from smarter import SmarterSite
from myapp.models import Page
site = SmarterSite()
site.register(Page)
urlpatterns = patterns('',
url(r'^', include(site.urls)),
# other urls ...
)
This will create generic views for Page model, accessed by urls:
- /page/
- /page/add/
- /page/<pk>/
- /page/<pk>/edit/
- /page/<pk>/remove/
Each url is mapped to view method and templates.
Templates by urls:
- /page/ => myapp/page_index.html
- /page/add/ => myapp/page_add.html
- /page/<pk>/ => myapp/page_details.html
- /page/<pk>/edit/ => myapp/page_edit.html
- /page/<pk>/remove/ => myapp/page_remove.html
Index template has template variable objects_list
.
All other templates have variable obj
.
Edit and add templates have also template variable form
.
Warning! This section is new to v0.5 docs and the way of views customization is changed since 0.4.x.
Here's example of view customization with options
dict. Keys in options
are action names, so you can customize any of available actions.
from smarter.views import GenericViews
from django import forms
class Views(GenericViews):
model = Page # some model
options = {
'add': {
# custom form class
'form': PageForm,
# custom fields widgets
'widgets': {
'title': forms.HiddenInput()
},
# explicit form fields
'fields': ('title', 'text'),
# exclude fields
#'exclude': ('title',)
# explicit custom template
'template': 'page/custom_add.html',
# help texts for fields
'help_text': {
'title': 'Max. 100 chars',
}
},
#...
}
You can subclass views class and add new view methods or override existing ones.
from django.shortcuts import get_object_or_404
from smarter.views import GenericViews
from myapp.models import Page
class PageViews(GenericViews):
model = Page
def urls_custom(self):
return [
self.url(r'^(?P<pk>\d+)/bookmark/$', 'bookmark')
]
def bookmark_view(self, request, pk):
obj = get_object_or_404(page, pk=pk)
# do some stuff for bookmarking ...
context = {'obj': obj}
# will render to myapp/page_bookmark.html
return self.render_to_response(context)
Than you need to register custom views in urls.py:
from smarter import SmarterSite
from myapp.views import PageViews
site = SmarterSite()
site.register(PageViews)
urlpatterns = patterns('',
url(r'^', include(site.urls)),
# other urls ...
)
Assume, you'd like to add login_required
decorator to views in your project. You may subclass from GenericViews
and use method_decorator
helper for that.
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from smarter.views import GenericViews
class Views(GenericViews):
@method_decorator(login_required)
def add_view(self, *args, **kwargs):
return super(Views, self).add_view(*args, **kwargs)
There's a special method check_permissions
which is invoked
from generic views.
It receives keyword arguments depending on processed view:
- for
add
action no extra arguments is passed, but if you defineform_params_add()
result will be passed as keyword arguments - for
edit
actioninstance
argument is passed, actuallyform_params_edit()
result is passed - for
details
andremove
actionsobj
argument is passed
from django.core.exceptions import PermissionDenied
from smarter.views import GenericViews
class Views(GenericViews):
def check_permissions(self, **kwargs):
if self.action == 'add':
if not self.request.is_superuser:
raise PermissionDenied
if self.action == 'edit':
obj = kwargs['instance']
if obj.owner != self.request.user:
raise PermissionDenied
What if you don't want to use MyModel.objects.all()
? What if you want to call a function or send a signal every time someone visits a certain object's detail page?
If it's a small change or addition, you can use the following hooks:
get_objects_list(self, action)
, which returns a queryset. It's used directly byindex_view
, and indirectly by the other views, becauseget_object
depends on it (read below). The default implementation just returnsself.model.objects.all()
get_object(self, pk)
, which will be used to get the object for remove_view, details_view and edit_view. The default implementation just returnsself.get_objects_list().get(pk=pk)
or raisesHttp404
.remove_object(self, obj)
, which deletes the object. The default implementation calls obj.delete().save_form(self, action, **kwargs)
saves the form in both theedit
andadd
views.get_form(self, form)
: in this method, you return a form for theedit
andadd
view. It's usually aModelForm
, but you can provide a form instance with a save() method, or hook intosave_form
. The default implementation gets a form from theself.form_class
dict, otherwise creates a ModelForm using modelform_factory.
Don't forget you can get the current request through self.request
, and the current action (E.G. 'index'
or details
) is available in self.action
.