├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.rst ├── django_baker ├── __init__.py ├── admin.py ├── bakery.py ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ └── bake.py ├── templates │ └── django_baker │ │ ├── __init__urls │ │ ├── __init__views │ │ ├── admin │ │ ├── base.html │ │ ├── create.html │ │ ├── delete.html │ │ ├── detail.html │ │ ├── forms │ │ ├── list.html │ │ ├── update.html │ │ ├── urls │ │ └── views └── tests.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | django_baker.egg-info 3 | dist 4 | bread 5 | pastries 6 | extra_goodies 7 | pages 8 | project 9 | users 10 | fabfile.py 11 | __init__.py 12 | first_requirements.txt 13 | requirements.txt 14 | manage.py -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Kris Fields. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of Django nor the names of its contributors may be used 15 | to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | include README.rst 3 | recursive-include django_baker/management * 4 | recursive-include django_baker/templates * -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ============ 2 | Django Baker 3 | ============ 4 | 5 | Django Baker wants to help you get your projects up and running quickly. Given one or more app names, s/he will automatically generate views, forms, urls, admin, and templates for all of the models in the models.py file. All files are pep-8 compliant (with exception to the maximum line length rule, which I don't agree with). 6 | 7 | Once you add a single urlpattern to your project's URLconf, you'll have a working list view, detail view, create view, update view, and delete view for each model in your app. 8 | 9 | Optionally you may specify which models in an app to bake if you'd rather not generate files for all of them. 10 | 11 | You can override any of the template files that are used in the 'baking' process in the same way that you would override any third party template. Just create a django_baker folder inside your project's main templates directory and create a file with the same name as any of the 9 files you wish to override, which are: detail.html, create.html, update.html, list.html, delete.html, views, urls, forms, admin, base, __init__urls, __init__views. Hopefully their names are self explanatory. 12 | 13 | ********** 14 | Installing 15 | ********** 16 | 17 | .. code-block:: python 18 | 19 | pip install django-baker 20 | 21 | Add 'django_baker' to INSTALLED_APPS 22 | 23 | ***** 24 | Usage 25 | ***** 26 | 27 | Let's assume your project is called TastyTreats, and has two apps, one called bread and another called pastries. 28 | 29 | .. code-block:: python 30 | 31 | python manage.py bake bread pastries 32 | 33 | This will generate files for all of the models in both of the apps. You can override this by passing in models for each app. Let's assume your pastries app has the following models: Tart, Strudel, and Danish but you only want to bake tarts and danishes. 34 | 35 | .. code-block:: python 36 | 37 | python manage.py bake bread pastries:Tart,Danish 38 | 39 | Finally you simply need to add one or more urlpattern to your project's URLconf. 40 | 41 | .. code-block:: python 42 | 43 | (r'^pastries/', include('pastries.urls')), 44 | 45 | will result in the following url schema: 46 | 47 | .. code-block:: html 48 | 49 | www.tastytreats.com/pastries/tarts 50 | www.tastytreats.com/pastries/danishes 51 | 52 | alternatively you can create multiple urlpatterns to create shorter urls. 53 | 54 | .. code-block:: python 55 | 56 | (r'^tarts/', include('pastries.urls.tart_urls')), 57 | (r'^danishes/', include('pastries.urls.danish_urls')), 58 | 59 | will result in the following url schema: 60 | 61 | .. code-block:: html 62 | 63 | www.tastytreats.com/tarts 64 | www.tastytreats.com/danishes 65 | 66 | 67 | Views 68 | ===== 69 | 70 | To keep things tidy, a views directory is created, and each model is given it's own views file (ie. tart_views.py). An __init__ file is created that imports from each of the views files. With the __init__ file in place, you can import from any of the individual views files the same way you would have before (ie. from bread.views import CornBreadCreateView). 71 | 72 | For convenience, almost all of the CBV methods that can be overridden are stubbed out, ready to be altered as needed. The methods are presented in the order in which they are called. I chose to leave a couple of methods out as I couldn't imagine any scenario in which I would want to override them. 73 | 74 | Also for convenience and easy alteration, almost all of the attributes that you can set are listed. 75 | 76 | Some other niceties: 77 | -------------------- 78 | - *form_class* is set to a ModelForm that was added to your forms.py file 79 | - *context_object_name* is set to the slugified model_name (ie. tarts for DetailView, UpdateView, and DeleteView, or tarts_list for the ListView) 80 | - if your model has exactly one unique slug field, it's used as the *slug_field* and *slug_url_kwarg* attribute. 81 | - *get_success_url* returns the url for the object's DetailView (for DeleteView, the ListView url is returned). 82 | 83 | Templates 84 | ========= 85 | 86 | The generated templates files are kept very minimal as there aren't usually a lot of commonalities in templates between projects. Each extends a model base file (ie. tart_base.html) which in turn extends "base.html", which your project is assumed to have. The model level base file is empty but nice to have if you wish to add any html specific to that model. 87 | 88 | The ListView template lists each object, with links to view, update, or delete. There is also a link to create a new object. 89 | 90 | The DetailView template lists the object with a link to update or delete, as well as a link back to the list view. 91 | 92 | Both the CreateView and UpdateView templates display the model form with a link back to the ListView. 93 | 94 | The DeleteView template has a simple confirm required button and a link back to the ListView. 95 | 96 | Forms 97 | ===== 98 | 99 | A ModelForm is created for each model, with many of the commonly set attributes listed for easy alteration. The *fields* attribute is set to each field in the model other than the id. 100 | 101 | In addition, a few commonly changed methods are stubbed out, including a *clean_field_name* method for each field in the form. 102 | 103 | Urls 104 | ==== 105 | 106 | A new urls directory is created, with each model getting it's own file (ie. tart_urls.py). An __init__ file is created which adds urlpatterns that include each of the newly created urls files. This allows you to choose whether to add routing to the app as a whole, or individually to each model (see usage above). 107 | 108 | For DetailView, UpdateView, and DeleteView, if a model has exactly one unique slug field, that slug field will be used in the url. Otherwise pk will be used. 109 | 110 | For CreateView, UpdateView, and DeleteView, the login_required decorator has been added as the vast majority of the time these actions tend to require the user to be logged in. In the future, I intend to make this optional. 111 | 112 | Admin 113 | ===== 114 | 115 | This is where I really had some fun. For each model, a ModelAdmin is created that makes use of a model admin mixin that I wrote. The goal of ExtendedModelAdminMixin is to make setting up a fully functional admin for each model (with intelligently chosen list_display, list_filter, and search_fields) a one liner. 116 | 117 | The actual contents of the admin.py files generated are fairly small, since most of the magic is happening in the ExtendedModelAdminMixin. Many of the attributes that you can set are listed so that you may easily alter them as needed. I didn't include any of the methods you can override as there are too many and it would get way too cluttered. There are a lot of useful ones though, which you can view here: https://docs.djangoproject.com/en/1.10/ref/contrib/admin/#modeladmin-methods 118 | 119 | ExtendedModelAdminMixin sets defaults for the following: 120 | 121 | list_select_related 122 | ------------------- 123 | 124 | Defaults to all of the model's ForeignKey and OneToOneFields, including those where null=True. This will usually decrease database queries and improve page load time. 125 | 126 | You can override this by setting **list_all_select_related** to False. 127 | 128 | list_display 129 | ------------ 130 | 131 | Defaults to all of the model's fields, in the order that they are listed in your models.py file, with the exception of the id field and any ManyToManyField. 132 | 133 | You can override this by setting the *list_display* attribute or you may extend it by setting **extra_list_display** (defaults to an empty list), the contents of which will be appended to *list_display*, with any fields in both being displayed only once. 134 | 135 | In addition, each URLField, ForeignKey, and OneToOneField will display as a link. URLFields will link to their respective urls, while ForeignKey and OneToOneFields will link to their respective object's admin change pages. 136 | 137 | You can ovverride this functionality by setting **link_url_fields** and/or **link_foreign_key_fields** to False. 138 | 139 | list_filter 140 | ----------- 141 | 142 | Defaults to any field where the choices attribute has been set, as well as any field with a field type matching a field type in the **list_by_fields** attribute (defaults to ['BooleanField', 'NullBooleanField', 'USStateField']), as well as any ForeignKey field where the number of related objects is less than the **max_related_objects** attribute (defaults to 100). 143 | 144 | You can override this by setting the *list_filter* attribute or you may extend it by setting **extra_list_filter** (defaults to an empty list), the contents of which will be appended to *list_filter*. 145 | 146 | search_fields 147 | ------------- 148 | 149 | Defaults to any field with a field type matching a field type in **search_by_fields** (defaults to ["CharField", "TextField"]), 150 | 151 | You can override this by setting the *search_fields* attribute or you may extend it by setting **extra_search_fields** (defaults to an empty list), the contents of which will be appended to search_fields. 152 | 153 | **** 154 | Note 155 | **** 156 | 157 | Django Baker will remove 2 files (views.py, urls.py) from each app baked so long as the files are 4 lines or less (the initial size of the files when you run startapp). This is necessary so they don't conflict with the newly generated views and urls folders. If the files are greater than 4 lines you will need to remove them yourself. 158 | 159 | ************************** 160 | The future of Django Baker 161 | ************************** 162 | 163 | My top 3 todo items are: 164 | 165 | 1. Allow apps to be baked more than once to account for newly added models. Right now the default behavior is to only create new files and skip any steps where the file about to be baked already exists. 166 | 2. Automatically generate tests for each app and model 167 | 3. Add tests to Django Baker itself 168 | 169 | Pull requests are awesome. 170 | 171 | -------------------------------------------------------------------------------- /django_baker/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krisfields/django-baker/f3cafef93a4eac2b6a2de37c89898fc403ef7ac7/django_baker/__init__.py -------------------------------------------------------------------------------- /django_baker/admin.py: -------------------------------------------------------------------------------- 1 | from django.core.validators import URLValidator 2 | from django.db.models.fields import FieldDoesNotExist 3 | from django.utils.encoding import smart_text 4 | 5 | from functools import partial 6 | 7 | 8 | def number_field_choices(field): 9 | """ 10 | Given a field, returns the number of choices. 11 | """ 12 | try: 13 | return len(field.get_flat_choices()) 14 | except AttributeError: 15 | return 0 16 | 17 | 18 | def remove_dupes(seq, idfun=None): 19 | """ 20 | Efficient method to remove dupes from list, while preserving order. 21 | """ 22 | if idfun is None: 23 | def idfun(x): 24 | return x 25 | seen = {} 26 | result = [] 27 | for item in seq: 28 | marker = idfun(item) 29 | if marker in seen: 30 | continue 31 | seen[marker] = 1 32 | result.append(item) 33 | return result 34 | 35 | 36 | def is_urlfield(field, model=None): 37 | """ 38 | Given a field, will check if it's a URLField or not. 39 | If model is given, field is actually a field_name and field must first be retrieved. 40 | """ 41 | if model: 42 | try: 43 | field = model._meta.get_field(field) 44 | except FieldDoesNotExist: 45 | return False 46 | try: 47 | return field.default_validators[0].regex == URLValidator.regex 48 | except AttributeError: 49 | return False 50 | except IndexError: 51 | return False 52 | 53 | 54 | def is_foreignkey(field_name, model): 55 | """ 56 | Given a field_name and model, checks if field is ForeignKey or OneToOneField or not. 57 | """ 58 | try: 59 | field = model._meta.get_field(field_name) 60 | if field.get_internal_type() in ["ForeignKey", "OneToOneField"]: 61 | return True 62 | return False 63 | except FieldDoesNotExist: 64 | return False 65 | 66 | 67 | class ExtendedModelAdminMixin(object): 68 | """ 69 | Model Admin Mixin that makes (hopefully) intelligent choices to minimize the time it takes to get the admin up 70 | and running. 71 | """ 72 | extra_list_display = [] 73 | extra_list_filter = [] 74 | extra_search_fields = [] 75 | link_url_fields = True 76 | link_foreign_key_fields = True 77 | max_related_objects = 100 78 | list_all_select_related = True 79 | filter_by_fields = ["BooleanField", "NullBooleanField", "USStateField"] 80 | search_by_fields = ["CharField", "TextField"] 81 | 82 | def __getattr__(cls, name): 83 | """ 84 | Dynamically creates a new method for each URLField and ForeignKey/OneToOneField field that will return the 85 | object name with a link to either the webpage (if URLField) or admin change page 86 | (if ForeignKey/OneToOneField). 87 | """ 88 | def url_link(instance, field): 89 | target = getattr(instance, field) 90 | if not target: 91 | return "" 92 | return '%s' % (target, target) 93 | 94 | def foreign_key_link(instance, field): 95 | target = getattr(instance, field) 96 | if not target: 97 | return "None" 98 | return u'%s' % ( 99 | target._meta.app_label, target._meta.model_name, target.id, smart_text(target)) 100 | 101 | if name[:9] == 'url_link_': 102 | method = partial(url_link, field=name[9:]) 103 | method.__name__ = name[9:] 104 | method.allow_tags = True 105 | method.admin_order_field = name[9:] 106 | setattr(cls, name, method) 107 | return getattr(cls, name) 108 | 109 | if name[:8] == 'fk_link_': 110 | method = partial(foreign_key_link, field=name[8:]) 111 | method.__name__ = name[8:] 112 | method.allow_tags = True 113 | setattr(cls, name, method) 114 | return getattr(cls, name) 115 | raise AttributeError 116 | 117 | def __init__(self, request, *args, **kwargs): 118 | """ 119 | Sets list_all_select_related to all ForeignKey and OneToOneField fields which will cause queryset to 120 | select all of those fields, minimizing db queries. Can be overridden by setting list_all_select_related to 121 | False. 122 | """ 123 | super(ExtendedModelAdminMixin, self).__init__(request, *args, **kwargs) 124 | if self.list_all_select_related is True: 125 | self.list_select_related = [field.name for field in self.model._meta.fields if field.get_internal_type() in 126 | ["ForeignKey", "OneToOneField"]] 127 | 128 | def get_list_display(self, request): 129 | """ 130 | Automatically creates admin list display for each field other than id. Any fields in the extra_list_display 131 | attribute are added at the end of list_display. Dupes are removed. 132 | URLField fields are created with links to the URL so long as the field is not in list_display_links. 133 | ForeignKey and OneToOneField fields are created with links to their admin change pages so long as the field 134 | is not in list_display_links. 135 | """ 136 | list_display = super(ExtendedModelAdminMixin, self).get_list_display(request) 137 | if not isinstance(list_display, list): 138 | combined_list_display = ([field.name for field in self.model._meta.fields if field.name != "id"] + 139 | self.extra_list_display) 140 | list_display = remove_dupes(combined_list_display) 141 | if self.link_url_fields: 142 | list_display = ["url_link_%s" % field_name if is_urlfield(field_name, self.model) else field_name for 143 | field_name in list_display] 144 | if self.link_foreign_key_fields: 145 | list_display = ["fk_link_%s" % field_name if is_foreignkey(field_name, self.model) else field_name for 146 | field_name in list_display] 147 | list_display_links = self.get_list_display_links(request, list_display) 148 | list_display = [field_name.replace("url_link_", "").replace("fk_link_", "") if field_name in 149 | list_display_links else field_name for field_name in list_display] 150 | return list_display 151 | 152 | def get_list_filter(self, request): 153 | """ 154 | Automatically creates admin filters for every field listed in filter_by_fields attribute (defaults to 155 | BooleanField, NullBooleanField, USStateField, as well as any field with choices (ex. IntegerField with 156 | choices=SOMETHING) and any ForeignKey where the total number of objects is less than or equal to the 157 | max_related_objects attribute, which defaults to 100. 158 | Any fields in the extra_list_filter attribute are added at the end of list_filter. 159 | """ 160 | list_filter = super(ExtendedModelAdminMixin, self).get_list_filter(request) 161 | if not isinstance(list_filter, list): 162 | combined_list_filter = ([field.name for field in self.model._meta.fields if 163 | (field.get_internal_type() in self.filter_by_fields) or 164 | (number_field_choices(field) > 0) or 165 | (field.get_internal_type() == "ForeignKey" and 166 | field.rel.to.objects.count() <= self.max_related_objects)] + 167 | self.extra_list_filter 168 | ) 169 | list_filter = remove_dupes(combined_list_filter) 170 | return list_filter 171 | 172 | def get_search_fields(self, request): 173 | """ 174 | Automatically creates admin search fields for every field listed in search_by_fields. 175 | Any fields in the extra_search_fields attribute are added at the end of search_fields. 176 | """ 177 | search_fields = super(ExtendedModelAdminMixin, self).get_search_fields(request) 178 | if not isinstance(self.search_fields, list): 179 | combined_search_fields = ([field.name for field in self.model._meta.fields if field.get_internal_type() in 180 | self.search_by_fields] + 181 | self.extra_search_fields 182 | ) 183 | search_fields = remove_dupes(combined_search_fields) 184 | return search_fields 185 | -------------------------------------------------------------------------------- /django_baker/bakery.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | import os 4 | import re 5 | import itertools 6 | 7 | from django.db.models.fields import SlugField 8 | from django.template.loader import get_template 9 | from django.template import Context 10 | from django.utils.six import iteritems 11 | 12 | 13 | class Baker(object): 14 | """ 15 | Given a dictionary of apps and models, Baker will bake up a bunch of files that will help get your new app up 16 | and running quickly. 17 | """ 18 | 19 | def bake(self, apps_and_models): 20 | """ 21 | Iterates a dictionary of apps and models and creates all the necessary files to get up and running quickly. 22 | """ 23 | for app_label, models_app in iteritems(apps_and_models): 24 | models, app = models_app 25 | models = list(models) 26 | model_names = {model.__name__: self.get_field_names_for_model(model) for model in models} 27 | self.create_directories(app) 28 | self.create_init_files(app, model_names.keys(), models) 29 | self.remove_empty_startapp_files(app) 30 | for file_name in ["forms", "admin"]: 31 | file_path = "%s/%s.py" % (app.path, file_name) 32 | template_path = "django_baker/%s" % (file_name) 33 | self.create_file_from_template(file_path, template_path, {"model_names": model_names}) 34 | for model in models: 35 | model_attributes = self.model_attributes(app, model) 36 | self.create_files_from_templates(model_attributes) 37 | 38 | def get_field_names_for_model(self, model): 39 | """ 40 | Returns fields other than id and uneditable fields (DateTimeField where auto_now or auto_now_add is True) 41 | """ 42 | return [field.name for field in model._meta.get_fields() if field.name != "id" and not 43 | (field.get_internal_type() == "DateTimeField" and 44 | (field.auto_now is True or field.auto_now_add is True)) and 45 | field.concrete and (not field.is_relation or field.one_to_one or 46 | (field.many_to_one and field.related_model))] 47 | 48 | def create_directories(self, app): 49 | """ 50 | If not already there, adds a directory for views, urls and templates. 51 | """ 52 | for folder_name in ["views", "urls", "templates/%s" % app.label]: 53 | directory_path = "%s/%s" % (app.path, folder_name) 54 | if not os.path.exists(directory_path): 55 | os.makedirs(directory_path) 56 | 57 | def create_init_files(self, app, model_names, models): 58 | """ 59 | If not already there, creates a new init file in views and urls directory. Init file imports from all 60 | of the files within the directory. 61 | """ 62 | model_name_slugs = ["%s_views" % (self.camel_to_slug(model_name)) for model_name in model_names] 63 | model_names_dict = {self.camel_to_slug(model.__name__): self.camel_to_slug(self.model_name_plural(model)) for 64 | model in models} 65 | for folder_name in ["views", "urls"]: 66 | file_path = "%s/%s/__init__.py" % (app.path, folder_name) 67 | template_path = "django_baker/__init__%s" % folder_name 68 | self.create_file_from_template(file_path, template_path, {"app_label": app.label, 69 | "model_name_slugs": model_name_slugs, 70 | "model_names_dict": model_names_dict 71 | }) 72 | 73 | def model_attributes(self, app, model): 74 | """ 75 | Creates a dictionary of model attributes that will be used in the templates. 76 | """ 77 | model_name = model.__name__ 78 | model_name_plural = self.model_name_plural(model) 79 | slug_field = self.get_unique_slug_field_name(model) 80 | slug_field_name = slug_field.name if slug_field else "slug" 81 | lookup_field = slug_field_name if slug_field else "pk" 82 | return { 83 | 'app_label': app.label, 84 | 'app_path': app.path, 85 | 'model': model, 86 | 'model_name': model_name, 87 | 'model_name_slug': self.camel_to_slug(model_name), 88 | 'model_name_plural': model_name_plural, 89 | 'model_name_plural_slug': self.camel_to_slug(model_name_plural), 90 | 'model_fields': self.get_field_names_for_model(model), 91 | 'slug_field': slug_field, 92 | 'slug_field_name': slug_field_name, 93 | 'lookup_field': lookup_field 94 | } 95 | 96 | def create_files_from_templates(self, model_attributes): 97 | """ 98 | Determines the correct path to put each file and then calls create file method. 99 | """ 100 | for folder_name in ["views", "urls"]: 101 | file_path = "%s/%s/%s_%s.py" % (model_attributes['app_path'], folder_name, 102 | model_attributes['model_name_slug'], folder_name) 103 | template_path = "django_baker/%s" % (folder_name) 104 | self.create_file_from_template(file_path, template_path, model_attributes) 105 | for file_name in ["base", "list", "detail", "create", "update", "delete"]: 106 | file_path = "%s/templates/%s/%s_%s.html" % (model_attributes['app_path'], model_attributes['app_label'], 107 | model_attributes['model_name_slug'], file_name) 108 | template_path = "django_baker/%s.html" % (file_name) 109 | self.create_file_from_template(file_path, template_path, model_attributes) 110 | 111 | def create_file_from_template(self, file_path, template_path, context_variables): 112 | """ 113 | Takes template file and context variables and uses django's render method to create new file. 114 | """ 115 | if os.path.exists(file_path): 116 | print("\033[91m" + file_path + " already exists. Skipping." + "\033[0m") 117 | return 118 | with open(file_path, 'w') as new_file: 119 | 120 | new_file.write(get_template(template_path).render(context_variables)) 121 | print("\033[92m" + "successfully baked " + file_path + "\033[0m") 122 | 123 | def remove_empty_startapp_files(self, app): 124 | """ 125 | Removes 'empty' (less than or equal to 4 lines, as that is what they begin with) views, admin, and tests 126 | files. 127 | """ 128 | for file_name in ["views", "admin", "tests"]: 129 | file_path = "%s/%s.py" % (app.path, file_name) 130 | if os.path.exists(file_path): 131 | num_lines = sum(1 for line in open(file_path)) 132 | if num_lines <= 4: 133 | os.remove(file_path) 134 | 135 | def camel_to_slug(self, name): 136 | """ 137 | Helper method to convert camel case string (PumpernickelBread) to slug string (pumpernickel_bread) 138 | """ 139 | name = re.sub(r'([a-z])([A-Z])', r'\1 \2', name).title().replace(" ", "").replace("_", "") 140 | s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name) 141 | slug = re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower() 142 | return slug 143 | 144 | def model_name_plural(self, model): 145 | """ 146 | Gets the pluralized version of a model. Simply adds an 's' to model name if verbose_name_plural isn't set. 147 | """ 148 | if isinstance(model._meta.verbose_name_plural, str): 149 | return model._meta.verbose_name_plural 150 | return "%ss" % model.__name__ 151 | 152 | def get_unique_slug_field_name(self, model): 153 | """ 154 | Determines if model has exactly 1 SlugField that is unique. If so, returns it. Otherwise returns None. 155 | """ 156 | slug_fields = [] 157 | for field in model._meta.get_fields(): 158 | if isinstance(field, SlugField) and field.unique: 159 | slug_fields.append(field) 160 | if len(slug_fields) == 1: 161 | return slug_fields[0] 162 | return None 163 | -------------------------------------------------------------------------------- /django_baker/management/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krisfields/django-baker/f3cafef93a4eac2b6a2de37c89898fc403ef7ac7/django_baker/management/__init__.py -------------------------------------------------------------------------------- /django_baker/management/commands/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krisfields/django-baker/f3cafef93a4eac2b6a2de37c89898fc403ef7ac7/django_baker/management/commands/__init__.py -------------------------------------------------------------------------------- /django_baker/management/commands/bake.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | from django.core.management.base import BaseCommand, CommandError 4 | from django.core.exceptions import ImproperlyConfigured 5 | from django.apps import apps 6 | 7 | from ...bakery import Baker 8 | 9 | 10 | class Command(BaseCommand): 11 | args = "appname:modelname,modelname2,modelname3" 12 | help = ("Generates generic views (create, update, detail, list, and delete), urls, forms, and admin for model in an" 13 | "app. Optionally can restrict which apps are generated on a per app basis.\n\nexample: python manage.py " 14 | "bake bread:Sesame,Pumpkernickel donut:Glazed,Chocolate") 15 | 16 | def add_arguments(self, parser): 17 | # Positional arguments 18 | parser.add_argument('apps_and_models', nargs='+') 19 | 20 | def handle(self, *args, **options): 21 | ingredients = self.parse_bake_options(options["apps_and_models"]) 22 | baker = Baker() 23 | baker.bake(ingredients) 24 | 25 | def parse_bake_options(self, apps_and_models): 26 | """ 27 | Parses command line options to determine what apps and models for those apps we should bake. 28 | """ 29 | apps_and_models_to_bake = {} 30 | for app_and_model in apps_and_models: 31 | app_and_model_names = app_and_model.split(':') 32 | app_label = app_and_model_names[0] 33 | if len(app_and_model_names) == 2: 34 | selected_model_names = app_and_model_names[1].split(",") 35 | else: 36 | selected_model_names = None 37 | app, models = self.get_app_and_models(app_label, selected_model_names) 38 | apps_and_models_to_bake[app_label] = (models, app) 39 | return apps_and_models_to_bake 40 | 41 | def get_app_and_models(self, app_label, model_names): 42 | """ 43 | Gets the app and models when given app_label and model names 44 | """ 45 | try: 46 | app = apps.get_app_config(app_label) 47 | except: 48 | raise CommandError("%s is ImproperlyConfigured - did you remember to add %s to settings.INSTALLED_APPS?" % 49 | (app_label, app_label)) 50 | 51 | models = self.get_selected_models(app, model_names) 52 | return (app, models) 53 | 54 | def get_selected_models(self, app, model_names): 55 | """ 56 | Returns the model for a given app. If given model_names, returns those so long as the model names are 57 | actually models in the given app. 58 | """ 59 | if model_names: 60 | try: 61 | return [app.get_model(model_name) for model_name in model_names] 62 | except: 63 | raise CommandError("One or more of the models you entered for %s are incorrect." % app_label) 64 | else: 65 | return app.get_models() 66 | -------------------------------------------------------------------------------- /django_baker/templates/django_baker/__init__urls: -------------------------------------------------------------------------------- 1 | from django.conf.urls import include, url 2 | 3 | app_name="{{ app_label }}" 4 | 5 | urlpatterns = [ 6 | {% for model_name_slug, plural_model_name_slug in model_names_dict.items %} 7 | url(r'^{{ plural_model_name_slug }}/', include('{{ app_label }}.urls.{{ model_name_slug }}_urls')),{% if forloop.first %} # NOQA{% endif %}{% endfor %} 8 | ] 9 | -------------------------------------------------------------------------------- /django_baker/templates/django_baker/__init__views: -------------------------------------------------------------------------------- 1 | 2 | {% for model_name_slug in model_name_slugs %}from .{{ model_name_slug }} import * # NOQA 3 | {% endfor %} 4 | -------------------------------------------------------------------------------- /django_baker/templates/django_baker/admin: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import {{ model_names|join:", " }} 3 | from django_baker.admin import ExtendedModelAdminMixin 4 | 5 | {% for model_name in model_names %} 6 | class {{ model_name }}Admin(ExtendedModelAdminMixin, admin.ModelAdmin): 7 | extra_list_display = [] 8 | extra_list_filter = [] 9 | extra_search_fields = [] 10 | list_editable = [] 11 | raw_id_fields = [] 12 | inlines = [] 13 | filter_vertical = [] 14 | filter_horizontal = [] 15 | radio_fields = {} 16 | prepopulated_fields = {} 17 | formfield_overrides = {} 18 | readonly_fields = [] 19 | 20 | {% endfor %} 21 | {% for model_name in model_names %}admin.site.register({{ model_name }}, {{ model_name }}Admin) 22 | {% endfor %} -------------------------------------------------------------------------------- /django_baker/templates/django_baker/base.html: -------------------------------------------------------------------------------- 1 | {% verbatim %}{% extends "base.html" %}{% endverbatim %} -------------------------------------------------------------------------------- /django_baker/templates/django_baker/create.html: -------------------------------------------------------------------------------- 1 | {% templatetag openblock %} extends "{{ app_label }}/{{ model_name_slug }}_base.html" {% templatetag closeblock %} 2 | 3 | {% verbatim %}{% block title %}{% endverbatim %} 4 | Create {{ model_name }} 5 | {% verbatim %}{% endblock %}{% endverbatim %} 6 | 7 | {% verbatim %}{% block content %}{% endverbatim %} 8 |

{{ model_name }}

9 |

Create {{ model_name }}

10 | 11 |
{% verbatim %}{% csrf_token %}{% endverbatim %} 12 | {% verbatim %}{{ form.as_p }}{% endverbatim %} 13 | 14 |
15 | Cancel 16 | {% verbatim %}{% endblock %}{% endverbatim %} 17 | -------------------------------------------------------------------------------- /django_baker/templates/django_baker/delete.html: -------------------------------------------------------------------------------- 1 | {% templatetag openblock %} extends "{{ app_label }}/{{ model_name_slug }}_base.html" {% templatetag closeblock %} 2 | 3 | {% verbatim %}{% block title %}{% endverbatim %} 4 | Delete {% templatetag openvariable %} {{ model_name_slug }} {% templatetag closevariable %} 5 | {% verbatim %}{% endblock %}{% endverbatim %} 6 | 7 | {% verbatim %}{% block content %}{% endverbatim %} 8 |

Delete {% templatetag openvariable %} {{ model_name_slug }} {% templatetag closevariable %}

9 | 10 |
{% verbatim %}{% csrf_token %}{% endverbatim %} 11 |

Are you sure you want to delete {% templatetag openvariable %} {{ model_name_slug }} {% templatetag closevariable %}?

12 | 13 |
14 | 15 | Cancel 16 | 17 | {% verbatim %}{% endblock %}{% endverbatim %} 18 | -------------------------------------------------------------------------------- /django_baker/templates/django_baker/detail.html: -------------------------------------------------------------------------------- 1 | {% templatetag openblock %} extends "{{ app_label }}/{{ model_name_slug }}_base.html" {% templatetag closeblock %} 2 | 3 | {% verbatim %}{% block title %}{% endverbatim %} 4 | {% templatetag openvariable %} {{ model_name_slug }} {% templatetag closevariable %} 5 | {% verbatim %}{% endblock %}{% endverbatim %} 6 | 7 | {% verbatim %}{% block content %}{% endverbatim %} 8 |

{{ model_name }}

9 |

{% templatetag openvariable %} {{ model_name_slug }} {% templatetag closevariable %} | Update | 10 |
{% verbatim %}{% csrf_token %}{% endverbatim %} 11 | 12 |
13 |

14 | {% verbatim %}{% endblock %}{% endverbatim %} 15 | -------------------------------------------------------------------------------- /django_baker/templates/django_baker/forms: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from .models import {{ model_names|join:", " }} 3 | 4 | {% for model_name, model_fields in model_names.items %} 5 | class {{ model_name }}Form(forms.ModelForm): 6 | 7 | class Meta: 8 | model = {{ model_name }} 9 | fields = {{ model_fields|safe }} 10 | exclude = [] 11 | widgets = None 12 | localized_fields = None 13 | labels = {} 14 | help_texts = {} 15 | error_messages = {} 16 | 17 | def __init__(self, *args, **kwargs): 18 | return super({{ model_name }}Form, self).__init__(*args, **kwargs) 19 | 20 | def is_valid(self): 21 | return super({{ model_name }}Form, self).is_valid() 22 | 23 | def full_clean(self): 24 | return super({{ model_name }}Form, self).full_clean() 25 | {% for field in model_fields %} 26 | def clean_{{ field }}(self): 27 | {{ field }} = self.cleaned_data.get("{{ field }}", None) 28 | return {{ field }} 29 | {% endfor %} 30 | def clean(self): 31 | return super({{ model_name }}Form, self).clean() 32 | 33 | def validate_unique(self): 34 | return super({{ model_name }}Form, self).validate_unique() 35 | 36 | def save(self, commit=True): 37 | return super({{ model_name }}Form, self).save(commit) 38 | {% if not forloop.last %} 39 | {% endif %}{% endfor %} 40 | -------------------------------------------------------------------------------- /django_baker/templates/django_baker/list.html: -------------------------------------------------------------------------------- 1 | {% templatetag openblock %} extends "{{ app_label }}/{{ model_name_slug }}_base.html" {% templatetag closeblock %} 2 | 3 | {% verbatim %}{% block title %}{% endverbatim %} 4 | {{ model_name_plural }} 5 | {% verbatim %}{% endblock %}{% endverbatim %} 6 | 7 | {% verbatim %}{% block content %}{% endverbatim %} 8 |

{{ model_name_plural }}

9 |

Create new {{ model_name }}

10 | 19 | {% verbatim %}{% endblock %}{% endverbatim %} 20 | 21 | -------------------------------------------------------------------------------- /django_baker/templates/django_baker/update.html: -------------------------------------------------------------------------------- 1 | {% templatetag openblock %} extends "{{ app_label }}/{{ model_name_slug }}_base.html" {% templatetag closeblock %} 2 | 3 | {% verbatim %}{% block title %}{% endverbatim %} 4 | Update {% templatetag openvariable %} {{ model_name_slug }} {% templatetag closevariable %} 5 | {% verbatim %}{% endblock %}{% endverbatim %} 6 | 7 | {% verbatim %}{% block content %}{% endverbatim %} 8 |

{{ model_name }}

9 |

Update {% templatetag openvariable %} {{ model_name_slug }} {% templatetag closevariable %}

10 | 11 |
{% verbatim %}{% csrf_token %}{% endverbatim %} 12 | {% verbatim %}{{ form.as_p }}{% endverbatim %} 13 | 14 |
15 | Cancel 16 |
{% verbatim %}{% csrf_token %}{% endverbatim %} 17 | 18 |
19 | {% verbatim %}{% endblock %}{% endverbatim %} 20 | -------------------------------------------------------------------------------- /django_baker/templates/django_baker/urls: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from ..views import ({{ model_name }}ListView, {{ model_name }}CreateView, {{ model_name }}DetailView, 3 | {{ model_name }}UpdateView, {{ model_name }}DeleteView) 4 | from django.contrib.auth.decorators import login_required 5 | 6 | {% if slug_field %} 7 | urlpatterns = [ 8 | url(r'^create/$', # NOQA 9 | login_required({{ model_name }}CreateView.as_view()), 10 | name="{{ model_name_slug }}_create"), 11 | 12 | url(r'^(?P<{{ slug_field_name }}>[-\w]+)/update/$', 13 | login_required({{ model_name }}UpdateView.as_view()), 14 | name="{{ model_name_slug }}_update"), 15 | 16 | url(r'^(?P<{{ slug_field_name }}>[-\w]+)/delete/$', 17 | login_required({{ model_name }}DeleteView.as_view()), 18 | name="{{ model_name_slug }}_delete"), 19 | 20 | url(r'^(?P<{{ slug_field_name }}>[-\w]+)/$', 21 | {{ model_name }}DetailView.as_view(), 22 | name="{{ model_name_slug }}_detail"), 23 | 24 | url(r'^$', 25 | {{ model_name }}ListView.as_view(), 26 | name="{{ model_name_slug }}_list"), 27 | ] 28 | {% else %} 29 | urlpatterns = [ 30 | url(r'^create/$', # NOQA 31 | login_required({{ model_name }}CreateView.as_view()), 32 | name="{{ model_name_slug }}_create"), 33 | 34 | url(r'^(?P\d+)/update/$', 35 | login_required({{ model_name }}UpdateView.as_view()), 36 | name="{{ model_name_slug }}_update"), 37 | 38 | url(r'^(?P\d+)/delete/$', 39 | login_required({{ model_name }}DeleteView.as_view()), 40 | name="{{ model_name_slug }}_delete"), 41 | 42 | url(r'^(?P\d+)/$', 43 | {{ model_name }}DetailView.as_view(), 44 | name="{{ model_name_slug }}_detail"), 45 | 46 | url(r'^$', 47 | {{ model_name }}ListView.as_view(), 48 | name="{{ model_name_slug }}_list"), 49 | ] 50 | {% endif %} -------------------------------------------------------------------------------- /django_baker/templates/django_baker/views: -------------------------------------------------------------------------------- 1 | from django.views.generic.detail import DetailView 2 | from django.views.generic.edit import CreateView, UpdateView, DeleteView 3 | from django.views.generic.list import ListView 4 | from ..models import {{ model_name }} 5 | from ..forms import {{ model_name }}Form 6 | from django.urls import reverse_lazy 7 | from django.urls import reverse 8 | from django.http import Http404 9 | 10 | 11 | class {{ model_name }}ListView(ListView): 12 | model = {{ model_name }} 13 | template_name = "{{ app_label }}/{{ model_name_slug }}_list.html" 14 | paginate_by = 20 15 | context_object_name = "{{ model_name_slug }}_list" 16 | allow_empty = True 17 | page_kwarg = 'page' 18 | paginate_orphans = 0 19 | 20 | def __init__(self, **kwargs): 21 | return super({{ model_name }}ListView, self).__init__(**kwargs) 22 | 23 | def dispatch(self, *args, **kwargs): 24 | return super({{ model_name }}ListView, self).dispatch(*args, **kwargs) 25 | 26 | def get(self, request, *args, **kwargs): 27 | return super({{ model_name }}ListView, self).get(request, *args, **kwargs) 28 | 29 | def get_queryset(self): 30 | return super({{ model_name }}ListView, self).get_queryset() 31 | 32 | def get_allow_empty(self): 33 | return super({{ model_name }}ListView, self).get_allow_empty() 34 | 35 | def get_context_data(self, *args, **kwargs): 36 | ret = super({{ model_name }}ListView, self).get_context_data(*args, **kwargs) 37 | return ret 38 | 39 | def get_paginate_by(self, queryset): 40 | return super({{ model_name }}ListView, self).get_paginate_by(queryset) 41 | 42 | def get_context_object_name(self, object_list): 43 | return super({{ model_name }}ListView, self).get_context_object_name(object_list) 44 | 45 | def paginate_queryset(self, queryset, page_size): 46 | return super({{ model_name }}ListView, self).paginate_queryset(queryset, page_size) 47 | 48 | def get_paginator(self, queryset, per_page, orphans=0, allow_empty_first_page=True): 49 | return super({{ model_name }}ListView, self).get_paginator(queryset, per_page, orphans=0, allow_empty_first_page=True) 50 | 51 | def render_to_response(self, context, **response_kwargs): 52 | return super({{ model_name }}ListView, self).render_to_response(context, **response_kwargs) 53 | 54 | def get_template_names(self): 55 | return super({{ model_name }}ListView, self).get_template_names() 56 | 57 | 58 | class {{ model_name }}DetailView(DetailView): 59 | model = {{ model_name }} 60 | template_name = "{{ app_label }}/{{ model_name_slug }}_detail.html" 61 | context_object_name = "{{ model_name_slug }}" 62 | slug_field = '{{ slug_field_name }}' 63 | slug_url_kwarg = '{{ slug_field_name }}' 64 | pk_url_kwarg = 'pk' 65 | 66 | def __init__(self, **kwargs): 67 | return super({{ model_name }}DetailView, self).__init__(**kwargs) 68 | 69 | def dispatch(self, *args, **kwargs): 70 | return super({{ model_name }}DetailView, self).dispatch(*args, **kwargs) 71 | 72 | def get(self, request, *args, **kwargs): 73 | return super({{ model_name }}DetailView, self).get(request, *args, **kwargs) 74 | 75 | def get_object(self, queryset=None): 76 | return super({{ model_name }}DetailView, self).get_object(queryset) 77 | 78 | def get_queryset(self): 79 | return super({{ model_name }}DetailView, self).get_queryset() 80 | 81 | def get_slug_field(self): 82 | return super({{ model_name }}DetailView, self).get_slug_field() 83 | 84 | def get_context_data(self, **kwargs): 85 | ret = super({{ model_name }}DetailView, self).get_context_data(**kwargs) 86 | return ret 87 | 88 | def get_context_object_name(self, obj): 89 | return super({{ model_name }}DetailView, self).get_context_object_name(obj) 90 | 91 | def render_to_response(self, context, **response_kwargs): 92 | return super({{ model_name }}DetailView, self).render_to_response(context, **response_kwargs) 93 | 94 | def get_template_names(self): 95 | return super({{ model_name }}DetailView, self).get_template_names() 96 | 97 | 98 | class {{ model_name }}CreateView(CreateView): 99 | model = {{ model_name }} 100 | form_class = {{ model_name }}Form 101 | # fields = {{ model_fields|safe }} 102 | template_name = "{{ app_label }}/{{ model_name_slug }}_create.html" 103 | success_url = reverse_lazy("{{ model_name_slug }}_list") 104 | 105 | def __init__(self, **kwargs): 106 | return super({{ model_name }}CreateView, self).__init__(**kwargs) 107 | 108 | def dispatch(self, request, *args, **kwargs): 109 | return super({{ model_name }}CreateView, self).dispatch(request, *args, **kwargs) 110 | 111 | def get(self, request, *args, **kwargs): 112 | return super({{ model_name }}CreateView, self).get(request, *args, **kwargs) 113 | 114 | def post(self, request, *args, **kwargs): 115 | return super({{ model_name }}CreateView, self).post(request, *args, **kwargs) 116 | 117 | def get_form_class(self): 118 | return super({{ model_name }}CreateView, self).get_form_class() 119 | 120 | def get_form(self, form_class=None): 121 | return super({{ model_name }}CreateView, self).get_form(form_class) 122 | 123 | def get_form_kwargs(self, **kwargs): 124 | return super({{ model_name }}CreateView, self).get_form_kwargs(**kwargs) 125 | 126 | def get_initial(self): 127 | return super({{ model_name }}CreateView, self).get_initial() 128 | 129 | def form_invalid(self, form): 130 | return super({{ model_name }}CreateView, self).form_invalid(form) 131 | 132 | def form_valid(self, form): 133 | obj = form.save(commit=False) 134 | obj.save() 135 | return super({{ model_name }}CreateView, self).form_valid(form) 136 | 137 | def get_context_data(self, **kwargs): 138 | ret = super({{ model_name }}CreateView, self).get_context_data(**kwargs) 139 | return ret 140 | 141 | def render_to_response(self, context, **response_kwargs): 142 | return super({{ model_name }}CreateView, self).render_to_response(context, **response_kwargs) 143 | 144 | def get_template_names(self): 145 | return super({{ model_name }}CreateView, self).get_template_names() 146 | 147 | def get_success_url(self): 148 | return reverse("{{ app_label }}:{{ model_name_slug }}_detail", args=(self.object.{% if slug_field %}{{ slug_field_name }}{% else %}pk{% endif %},)) 149 | 150 | 151 | class {{ model_name }}UpdateView(UpdateView): 152 | model = {{ model_name }} 153 | form_class = {{ model_name }}Form 154 | # fields = {{ model_fields|safe }} 155 | template_name = "{{ app_label }}/{{ model_name_slug }}_update.html" 156 | initial = {} 157 | slug_field = '{{ slug_field_name }}' 158 | slug_url_kwarg = '{{ slug_field_name }}' 159 | pk_url_kwarg = 'pk' 160 | context_object_name = "{{ model_name_slug }}" 161 | 162 | def __init__(self, **kwargs): 163 | return super({{ model_name }}UpdateView, self).__init__(**kwargs) 164 | 165 | def dispatch(self, *args, **kwargs): 166 | return super({{ model_name }}UpdateView, self).dispatch(*args, **kwargs) 167 | 168 | def get(self, request, *args, **kwargs): 169 | return super({{ model_name }}UpdateView, self).get(request, *args, **kwargs) 170 | 171 | def post(self, request, *args, **kwargs): 172 | return super({{ model_name }}UpdateView, self).post(request, *args, **kwargs) 173 | 174 | def get_object(self, queryset=None): 175 | return super({{ model_name }}UpdateView, self).get_object(queryset) 176 | 177 | def get_queryset(self): 178 | return super({{ model_name }}UpdateView, self).get_queryset() 179 | 180 | def get_slug_field(self): 181 | return super({{ model_name }}UpdateView, self).get_slug_field() 182 | 183 | def get_form_class(self): 184 | return super({{ model_name }}UpdateView, self).get_form_class() 185 | 186 | def get_form(self, form_class=None): 187 | return super({{ model_name }}UpdateView, self).get_form(form_class) 188 | 189 | def get_form_kwargs(self, **kwargs): 190 | return super({{ model_name }}UpdateView, self).get_form_kwargs(**kwargs) 191 | 192 | def get_initial(self): 193 | return super({{ model_name }}UpdateView, self).get_initial() 194 | 195 | def form_invalid(self, form): 196 | return super({{ model_name }}UpdateView, self).form_invalid(form) 197 | 198 | def form_valid(self, form): 199 | obj = form.save(commit=False) 200 | obj.save() 201 | return super({{ model_name }}UpdateView, self).form_valid(form) 202 | 203 | def get_context_data(self, **kwargs): 204 | ret = super({{ model_name }}UpdateView, self).get_context_data(**kwargs) 205 | return ret 206 | 207 | def get_context_object_name(self, obj): 208 | return super({{ model_name }}UpdateView, self).get_context_object_name(obj) 209 | 210 | def render_to_response(self, context, **response_kwargs): 211 | return super({{ model_name }}UpdateView, self).render_to_response(context, **response_kwargs) 212 | 213 | def get_template_names(self): 214 | return super({{ model_name }}UpdateView, self).get_template_names() 215 | 216 | def get_success_url(self): 217 | return reverse("{{ app_label }}:{{ model_name_slug }}_detail", args=(self.object.{% if slug_field %}{{ slug_field_name }}{% else %}pk{% endif %},)) 218 | 219 | 220 | class {{ model_name }}DeleteView(DeleteView): 221 | model = {{ model_name }} 222 | template_name = "{{ app_label }}/{{ model_name_slug }}_delete.html" 223 | slug_field = '{{ slug_field_name }}' 224 | slug_url_kwarg = '{{ slug_field_name }}' 225 | pk_url_kwarg = 'pk' 226 | context_object_name = "{{ model_name_slug }}" 227 | 228 | def __init__(self, **kwargs): 229 | return super({{ model_name }}DeleteView, self).__init__(**kwargs) 230 | 231 | def dispatch(self, *args, **kwargs): 232 | return super({{ model_name }}DeleteView, self).dispatch(*args, **kwargs) 233 | 234 | def get(self, request, *args, **kwargs): 235 | raise Http404 236 | 237 | def post(self, request, *args, **kwargs): 238 | return super({{ model_name }}DeleteView, self).post(request, *args, **kwargs) 239 | 240 | def delete(self, request, *args, **kwargs): 241 | return super({{ model_name }}DeleteView, self).delete(request, *args, **kwargs) 242 | 243 | def get_object(self, queryset=None): 244 | return super({{ model_name }}DeleteView, self).get_object(queryset) 245 | 246 | def get_queryset(self): 247 | return super({{ model_name }}DeleteView, self).get_queryset() 248 | 249 | def get_slug_field(self): 250 | return super({{ model_name }}DeleteView, self).get_slug_field() 251 | 252 | def get_context_data(self, **kwargs): 253 | ret = super({{ model_name }}DeleteView, self).get_context_data(**kwargs) 254 | return ret 255 | 256 | def get_context_object_name(self, obj): 257 | return super({{ model_name }}DeleteView, self).get_context_object_name(obj) 258 | 259 | def render_to_response(self, context, **response_kwargs): 260 | return super({{ model_name }}DeleteView, self).render_to_response(context, **response_kwargs) 261 | 262 | def get_template_names(self): 263 | return super({{ model_name }}DeleteView, self).get_template_names() 264 | 265 | def get_success_url(self): 266 | return reverse("{{ app_label }}:{{ model_name_slug }}_list") 267 | -------------------------------------------------------------------------------- /django_baker/tests.py: -------------------------------------------------------------------------------- 1 | # from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | from setuptools import setup 3 | 4 | with open(os.path.join(os.path.dirname(__file__), 'README.rst')) as readme: 5 | README = readme.read() 6 | 7 | # allow setup.py to be run from any path 8 | os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) 9 | 10 | setup( 11 | name='django-baker', 12 | version='0.22', 13 | packages=['django_baker'], 14 | include_package_data=True, 15 | license='BSD License', 16 | description='Management command that generates views, forms, urls, admin, and templates for models', 17 | long_description=README, 18 | url='https://github.com/krisfields/django-baker', 19 | author='Kris Fields', 20 | author_email='avalaunchit@gmail.com', 21 | classifiers=[ 22 | 'Environment :: Web Environment', 23 | 'Framework :: Django', 24 | 'Intended Audience :: Developers', 25 | 'License :: OSI Approved :: BSD License', 26 | 'Operating System :: OS Independent', 27 | 'Programming Language :: Python', 28 | # Replace these appropriately if you are stuck on Python 2. 29 | 'Programming Language :: Python :: 2.7', 30 | 'Programming Language :: Python :: 3.6', 31 | 32 | 'Topic :: Internet :: WWW/HTTP', 33 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 34 | ], 35 | keywords='generator generates', 36 | ) --------------------------------------------------------------------------------