| 1 | from django.conf.urls.defaults import patterns, url |
| 2 | from django.core.exceptions import ImproperlyConfigured |
| 3 | from django.db.models import Model |
| 4 | from django.db.models.query import QuerySet |
| 5 | from django.views import generic |
| 6 | |
| 7 | class ViewCollection(object): |
| 8 | """ |
| 9 | A base class based view collection |
| 10 | """ |
| 11 | class_based_views = None |
| 12 | current_app = None |
| 13 | |
| 14 | def __init__(self, *args, **kwargs): |
| 15 | """ |
| 16 | Constructor. |
| 17 | """ |
| 18 | for key, value in kwargs.iteritems(): |
| 19 | setattr(self, key, value) |
| 20 | |
| 21 | def get_urls(self): |
| 22 | """ |
| 23 | Returns the urlspatterns to be used in for the views |
| 24 | """ |
| 25 | urlpatterns = patterns('') |
| 26 | |
| 27 | for name, regex, cbv in self.class_based_views: |
| 28 | urlpatterns += patterns('', |
| 29 | url(regex, cbv.as_view(), name=name) |
| 30 | ) |
| 31 | return urlpatterns |
| 32 | |
| 33 | @property |
| 34 | def urls(self): |
| 35 | """ |
| 36 | Property to be used in the URLConf in an include method |
| 37 | """ |
| 38 | return self.get_urls(), self.current_app |
| 39 | |
| 40 | def get_urls_namespacestring(self): |
| 41 | """ |
| 42 | Returns the urls prefix we should use for the url reversing |
| 43 | """ |
| 44 | if self.current_app is not None: |
| 45 | return '%s:' % self.current_app |
| 46 | return '' |
| 47 | |
| 48 | |
| 49 | class CrudViewCollectionViewMixin(object): |
| 50 | """ |
| 51 | A mixin for class based views for the CrudViewCollection |
| 52 | """ |
| 53 | extra_context = None |
| 54 | current_app = None |
| 55 | |
| 56 | def get_context_data(self, **kwargs): |
| 57 | sup = super(CrudViewCollectionViewMixin, self) |
| 58 | context = {} |
| 59 | if hasattr(sup, 'get_context_data'): |
| 60 | context = sup.get_context_data(**kwargs) |
| 61 | if self.extra_context is not None: |
| 62 | context.update(self.extra_context) |
| 63 | return context |
| 64 | |
| 65 | def render_to_response(self, context, **response_kwargs): |
| 66 | """ |
| 67 | Returns a response with a template rendered with the given context. |
| 68 | """ |
| 69 | if 'current_app' not in response_kwargs: |
| 70 | response_kwargs['current_app'] = self.current_app |
| 71 | return super(CrudViewCollectionViewMixin, self).render_to_response( |
| 72 | context, |
| 73 | **response_kwargs) |
| 74 | |
| 75 | |
| 76 | class CrudListView(CrudViewCollectionViewMixin, generic.ListView): |
| 77 | pass |
| 78 | |
| 79 | |
| 80 | class CrudCreateView(CrudViewCollectionViewMixin, generic.CreateView): |
| 81 | pass |
| 82 | |
| 83 | |
| 84 | class CrudUpdateView(CrudViewCollectionViewMixin, generic.UpdateView): |
| 85 | pass |
| 86 | |
| 87 | |
| 88 | class CrudDeleteView(CrudViewCollectionViewMixin, generic.DeleteView): |
| 89 | pass |
| 90 | |
| 91 | |
| 92 | class CrudDetailView(CrudViewCollectionViewMixin, generic.DetailView): |
| 93 | pass |
| 94 | |
| 95 | |
| 96 | class CrudViewCollection(ViewCollection): |
| 97 | """ |
| 98 | A Create, Read (Detail/List), Update, Delete View Collection |
| 99 | """ |
| 100 | templates = None |
| 101 | model = None |
| 102 | queryset = None |
| 103 | |
| 104 | forms = None |
| 105 | templates = None |
| 106 | |
| 107 | |
| 108 | def __init__(self, *args, **kwargs): |
| 109 | """ |
| 110 | Constructor |
| 111 | """ |
| 112 | super(CrudViewCollection, self).__init__(*args, **kwargs) |
| 113 | self.init_views() |
| 114 | |
| 115 | #Make sure we have a unique current_app if it is not yet set |
| 116 | if self.current_app is None: |
| 117 | self.current_app = 'crud_%s' % (self.get_model().__class__.lower()) |
| 118 | |
| 119 | |
| 120 | def init_views(self): |
| 121 | """ |
| 122 | Initialize the class based views |
| 123 | """ |
| 124 | self.class_based_views = [ |
| 125 | ('list', r'^$', CrudListView.as_view( |
| 126 | model = self.get_model(), |
| 127 | queryset = self.get_queryset(), |
| 128 | template_name = self.get_template_for('list'), |
| 129 | current_app = self.current_app |
| 130 | )), |
| 131 | ('create', r'^/create/$', CrudCreateView.as_view( |
| 132 | model = self.get_model(), |
| 133 | queryset = self.get_queryset(), |
| 134 | form_class = self.get_form_for('create'), |
| 135 | template_name = self.get_template_for('create'), |
| 136 | current_app = self.current_app |
| 137 | )), |
| 138 | ('update', r'^/update/(?P<pk>\d+)/$', CrudUpdateView.as_view( |
| 139 | model = self.get_model(), |
| 140 | queryset = self.get_queryset(), |
| 141 | form_class = self.get_form_for('update'), |
| 142 | template_name = self.get_template_for('update'), |
| 143 | current_app = self.current_app |
| 144 | )), |
| 145 | ('delete', r'^/delete/(?P<pk>\d+)/$', CrudDeleteView.as_view( |
| 146 | model = self.get_model(), |
| 147 | queryset = self.get_queryset(), |
| 148 | template_name = self.get_template_for('delete'), |
| 149 | success_url = '%s%s' % ( |
| 150 | self.get_urls_namespacestring(), |
| 151 | 'list' |
| 152 | ), |
| 153 | current_app = self.current_app, |
| 154 | )), |
| 155 | ('detail', r'^/detail/(?P<pk>\d+)/$', CrudDetailView.as_view( |
| 156 | model = self.get_model(), |
| 157 | queryset = self.get_queryset(), |
| 158 | form_class = self.get_form_for('detail'), |
| 159 | template_name = self.get_template_for('detail'), |
| 160 | current_app = self.current_app |
| 161 | )) |
| 162 | ] |
| 163 | |
| 164 | def get_template_for(self, view_name): |
| 165 | """ |
| 166 | Should return a template path or None |
| 167 | """ |
| 168 | if self.templates is None: |
| 169 | self.templates = {} |
| 170 | return self.templates.get(view_name) |
| 171 | |
| 172 | def get_form_for(self, view_name): |
| 173 | """ |
| 174 | Should return the form object or None |
| 175 | Note: If None the CBV will create a ModelForm |
| 176 | """ |
| 177 | if self.forms is None: |
| 178 | self.forms = {} |
| 179 | return self.forms.get(view_name) |
| 180 | |
| 181 | def get_model(self): |
| 182 | """ |
| 183 | Return the model or get it from the queryset |
| 184 | """ |
| 185 | if isinstance(self.model, Model): |
| 186 | return self.model |
| 187 | elif isinstance(self.queryset, QuerySet): |
| 188 | return self.queryset.model |
| 189 | raise ImproperlyConfigured( |
| 190 | 'No model or queryset specified on %r' % self.__class__) |
| 191 | |
| 192 | def get_queryset(self): |
| 193 | """ |
| 194 | Return the queryset or get it from the model |
| 195 | """ |
| 196 | if isinstance(self.queryset, QuerySet): |
| 197 | return self.queryset |
| 198 | elif isinstance(self.model, Model): |
| 199 | return self.model.objects.all() |
| 200 | raise ImproperlyConfigured( |
| 201 | 'No model or queryset specified on %r' % self.__class__) |