| | 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__) |