Package: django-vb-baseapp
django-vb-baseappThis is a helper app forhttps://github.com/vbyazilim/django-vb-adminand ships with while installation:$pipinstalldjango-vb-admin
$django-vb-admin-hIt’s also available on PyPI and available via:$pipinstalldjango-vb-baseappFeaturesTwo abstract custom base models:CustomBaseModelandCustomBaseModelWithSoftDeleteTwo custom base model admins:CustomBaseModelAdminandCustomBaseModelAdminWithSoftDeleteSoft deletion feature and admin actions forCustomBaseModelAdminWithSoftDeletepre_undeleteandpost_undeletesignals forsoft deleteoperationPre enabled models admin site:ContentTypeAdmin,LogEntryAdmin,PermissionAdmin,UserAdminTimezone and locale middlewaresOnscreen debugging feature for views! (Template layer...)Handy utils:numerify,save_file,SlackExceptionHandlerFancy file widget:AdminImageFileWidgetforImageFieldon admin by defaultOverwriteStoragefor overwriting file uploadsCustom file storage for missing files for development environment:FileNotFoundFileSystemStorageCustom and configurable error page views for:400,403,404,500Custom management command with basic output featureCustomBaseCommandBuiltinconsole,console.dir()viavb-consolepackageSimpler server logging forrunserver_plusThis project usesbulma.ioas HTML/CSS framework, ships withjQueryandFontawesomeScreenshotsTutorialLet’s build a basic blog with categories and tags! First, create a virtual
environment:# via builtin$python-mvenvmy_env
$sourcemy_env/bin/activate# or via virtualenvwrapper$mkvirtualenvmy_envNow, create a postgresql database;$createdbmy_project_devNow set your environment variables:$exportDJANGO_SECRET=$(head-c75/dev/random|base64|tr-dc'a-zA-Z0-9'|head-c50)$exportDATABASE_URL="postgres://localhost:5432/my_project_dev"Editmy_env/bin/activateor~/.virtualenvs/my_env/bin/postactivate(according to your virtualenv creation procedure) and put these export
variables in it. Will be handy next time you activate the environment. Now;$pipinstalldjango-vb-admin
$cd/path/to/my-django-project
$django-vb-adminstartproject# or$django-vb-adminstartproject--target="/path/to/folder"You’ll see:Setupcompleted...
Now,createyourvirtualenvironmentandrunpipinstall-rrequirements/development.pipmessage. Now;$pipinstall-rrequirements/development.pip
$pythonmanage.pymigrate
Operationstoperform:Applyallmigrations:admin,auth,contenttypes,sessions
Runningmigrations:Applyingcontenttypes.0001_initial...OKApplyingauth.0001_initial...OKApplyingadmin.0001_initial...OKApplyingadmin.0002_logentry_remove_auto_add...OKApplyingadmin.0003_logentry_add_action_flag_choices...OKApplyingcontenttypes.0002_remove_content_type_name...OKApplyingauth.0002_alter_permission_name_max_length...OKApplyingauth.0003_alter_user_email_max_length...OKApplyingauth.0004_alter_user_username_opts...OKApplyingauth.0005_alter_user_last_login_null...OKApplyingauth.0006_require_contenttypes_0002...OKApplyingauth.0007_alter_validators_add_error_messages...OKApplyingauth.0008_alter_user_username_max_length...OKApplyingauth.0009_alter_user_last_name_max_length...OKApplyingauth.0010_alter_group_name_max_length...OKApplyingauth.0011_update_proxy_permissions...OKApplyingsessions.0001_initial...OKNow, we have a ready Django project. Let’s check;$pythonmanage.pyrunserver_plus# or$rake

INFO|*Runningonhttp://127.0.0.1:8000/(PressCTRL+Ctoquit)INFO|*Restartingwithstat
Performingsystemchecks...

Systemcheckidentifiednoissues(0silenced).

DjangoversionX.X.X,usingsettings'config.settings.development'Developmentserverisrunningathttp://[127.0.0.1]:8000/
UsingtheWerkzeugdebugger(http://werkzeug.pocoo.org/)QuittheserverwithCONTROL-C.
WARNING|*Debuggerisactive!
WARNING|*DebuggerPINdisabled.DEBUGGERUNSECURED!Let’s create ablogapp!$pythonmanage.pycreate_appblog# or$rakenew:application[blog]"blog"applicationcreated.-Donotforgettoaddyour`blog`to`INSTALLED_APPS`under`config/settings/base.py`:INSTALLED_APPS+=['django_extensions','blog.apps.BlogConfig',# <-- add this]-Donotforgettofixyour`config/urls.py`:# ...# add your newly created app's urls here!urlpatterns+=[# ...# this is just an example!path('__blog__/',include('blog.urls',namespace='blog')),# ..]# ...You can follow the instructions, fix yourconfig/settings/base.pyandconfig/urls.pyas seen on the command output. Now run development server
and call the url:$pythonmanage.pyrunserver_plusOpenhttp://127.0.0.1:8000/__blog__/. Also, another builtin app is running;http://127.0.0.1:8000/__vb_baseapp__/. You can remove__vb_baseapp__config fromconfig/urls.py.Now let’s add some models. We have 3 choices as parameters:django: Uses Django’smodels.Modelbasemodel: UsesCustomBaseModel(which inherits frommodels.Model)softdelete: UsesCustomBaseModelWithSoftDeleteWe’ll use soft-deletable model to demonstrate soft-delete features. Let’s
createPost,CategoryandTagmodels:$pythonmanage.pycreate_modelblogpostsoftdelete# or$rakenew:model[blog,post,softdelete]models/post.pycreated.
admin/post.pycreated.
postmodeladdedtomodels/__init__.py
postmodeladdedtoadmin/__init__.py`post`relatedfilescreatedsuccessfully:-`blog/models/post.py`-`blog/admin/post.py`Pleasecheckyourmodelsbeforerunning`makemigrations`ok?

$pythonmanage.pycreate_modelblogcategorysoftdelete# or$rakenew:model[blog,category,softdelete]models/category.pycreated.
admin/category.pycreated.
categorymodeladdedtomodels/__init__.py
categorymodeladdedtoadmin/__init__.py`category`relatedfilescreatedsuccessfully:-`blog/models/category.py`-`blog/admin/category.py`Pleasecheckyourmodelsbeforerunning`makemigrations`ok?

$pythonmanage.pycreate_modelblogtagsoftdelete# or$rakenew:model[blog,tag,softdelete]models/tag.pycreated.
admin/tag.pycreated.
tagmodeladdedtomodels/__init__.py
tagmodeladdedtoadmin/__init__.py`tag`relatedfilescreatedsuccessfully:-`blog/models/tag.py`-`blog/admin/tag.py`Pleasecheckyourmodelsbeforerunning`makemigrations`ok?Let’s fix models before creating and executing migrations:# blog/models/post.pyimportloggingfromdjango.confimportsettingsfromdjango.dbimportmodelsfromdjango.utils.translationimportugettext_lazyas_fromconsoleimportconsolefromvb_baseapp.modelsimportCustomBaseModelWithSoftDelete__all__=['Post']logger=logging.getLogger('app')console=console(source=__name__)classPost(CustomBaseModelWithSoftDelete):author=models.ForeignKey(to=settings.AUTH_USER_MODEL,on_delete=models.CASCADE,related_name='posts',verbose_name=_('author'))category=models.ForeignKey(to='Category',on_delete=models.CASCADE,related_name='posts',verbose_name=_('category'))title=models.CharField(max_length=255,verbose_name=_('title'))body=models.TextField(verbose_name=_('body'))tags=models.ManyToManyField(to='Tag',related_name='posts',blank=True)classMeta:app_label='blog'verbose_name=_('post')verbose_name_plural=_('posts')# check pluralizationdef__str__(self):returnself.titleandCategorymodel:# blog/models/category.pyimportloggingfromdjango.dbimportmodelsfromdjango.utils.translationimportugettext_lazyas_fromconsoleimportconsolefromvb_baseapp.modelsimportCustomBaseModelWithSoftDelete__all__=['Category']logger=logging.getLogger('app')console=console(source=__name__)classCategory(CustomBaseModelWithSoftDelete):title=models.CharField(max_length=255,verbose_name=_('title'))classMeta:app_label='blog'verbose_name=_('category')verbose_name_plural=_('categories')# check pluralizationdef__str__(self):returnself.titleandTagmodel:# blog/models/tag.pyimportloggingfromdjango.dbimportmodelsfromdjango.utils.translationimportugettext_lazyas_fromconsoleimportconsolefromvb_baseapp.modelsimportCustomBaseModelWithSoftDelete__all__=['Tag']logger=logging.getLogger('app')console=console(source=__name__)classTag(CustomBaseModelWithSoftDelete):name=models.CharField(max_length=255,verbose_name=_('name'))classMeta:app_label='blog'def__str__(self):returnself.nameLet’s create and run migration file:$pythonmanage.pymakemigrations--namecreate_post_category_and_tag# or$rakedb:update[blog,create_post_category_and_tag]Migrationsfor'blog':applications/blog/migrations/0001_create_post_category_and_tag.py-CreatemodelCategory-CreatemodelTag-CreatemodelPost

$pythonmanage.pymigrate# or$rakedb:migrate

Operationstoperform:Applyallmigrations:admin,auth,blog,contenttypes,sessions
Runningmigrations:Applyingblog.0001_create_post_category_and_tag...OKNow we have a model which has relations to other models viaForeignKeyandManyToManylevel. Let’s tweakblog/admin/post.py:# blog/admin/post.pyimportloggingfromdjango.contribimportadminfromconsoleimportconsolefromvb_baseapp.adminimport(CustomBaseModelAdminWithSoftDelete,)from..modelsimportPost__all__=['PostAdmin']logger=logging.getLogger('app')console=console(source=__name__)@admin.register(Post)classPostAdmin(CustomBaseModelAdminWithSoftDelete):list_filter=('category','tags','author')list_display=('__str__','author')ordering=('title',)# hide_deleted_at = FalseLet’s create a super user and jump in to admin pages.AUTH_PASSWORD_VALIDATORSis removed fromdevelopmentsettings, you can type any password :)$pythonmanage.pycreatesuperuser--username="${USER}"--email="your@email.com"$pythonmanage.pyrunserver_plus# or$rake

INFO|*Runningonhttp://127.0.0.1:8000/(PressCTRL+Ctoquit)INFO|*Restartingwithstat
Performingsystemchecks...

Systemcheckidentifiednoissues(0silenced).

DjangoversionX.X.X,usingsettings'config.settings.development'Developmentserverisrunningathttp://[127.0.0.1]:8000/
UsingtheWerkzeugdebugger(http://werkzeug.pocoo.org/)QuittheserverwithCONTROL-C.
WARNING|*Debuggerisactive!
WARNING|*DebuggerPINdisabled.DEBUGGERUNSECURED!
INFO|GET|302|/admin/
INFO|GET|200|/admin/login/?next=/admin/
INFO|GET|404|/favicon.ico
:
:Now openhttp://127.0.0.1:8000/admin/and add a new blog post!
Create different categories and tags. Then openhttp://127.0.0.1:8000/admin/blog/category/page.In the Action menu, you’ll have couple extra options:Delete selected categoriesRecover selected categories (Appears if you are filtering inactive records)Hard delete selected categoriesNow, delete one or more categories or tags. Checkactivity statefilter
for post, category and tag models. You can recover deleted items from the
action menu too.ModelsCustomBaseModelThis is a common model. By default,CustomBaseModelcontains these fields:created_atupdated_atAlmost a defaultmodels.Modelwith two extra fields.CustomBaseModelWithSoftDeleteThis model inherits fromCustomBaseModeland provides fake deletion which is
probably calledSOFT DELETE. This means, when you call model’sdelete()method or QuerySet’sdelete()method, it acts like delete action but never
deletes the data.Just sets thedeleted_atfield toNOW.This works exactly like Django’sdelete(). Broadcastspre_deleteandpost_deletesignals and returns the number of objects marked as deleted and
a dictionary with the number of deletion-marks per object type.You can callhard_delete()method to delete an instance or a queryset
actually.This model usesCustomBaseModelWithSoftDeleteManageras default manager.How soft-delete works?When you call.delete()method of a model instance or queryset, model manager
setsdeleted_atattribute toNOWall the way down through relatedForeignKeyandManyToManyfields. This means, you still keep everything.Nothing is actually deleted, therefore your database constraints are still
work fine. When you access deleted (inactive) object from admin site, you’ll
see "deleted" text prefix in your related form fields if your related objects
areCustomBaseModelWithSoftDeleteinstances.When you clickrecoverbutton in the same page, all related and soft-deleted
objects’deleted_atvalue will set toNULLand available again.Please use.actives()queryset method instead of.all(). Why?.all()method is untouched and works as default. Whenall()called, returning
queryset set contains everything event if thedeleted_atis NULL or not...Examples>>>Post.objects.all()SELECT"blog_post"."id","blog_post"."created_at","blog_post"."updated_at","blog_post"."deleted_at","blog_post"."author_id","blog_post"."category_id","blog_post"."title","blog_post"."body"FROM"blog_post"LIMIT21Executiontime:0.000950s[Database:default]<CustomBaseModelWithSoftDeleteQuerySet[<Post:Pythonpost1>,<Post:Pythonpost2>,<Post:Pythonpost3>,<Post:Pythonpost4>,:::<Post:Golangpost4>]>>>>Category.objects.all()SELECT"blog_category"."id","blog_category"."created_at","blog_category"."updated_at","blog_category"."deleted_at","blog_category"."title"FROM"blog_category"LIMIT21Executiontime:0.000643s[Database:default]<CustomBaseModelWithSoftDeleteQuerySet[<Category:Python>,<Category:Ruby>,<Category:Bash>,<Category:Golang>]>>>>Tag.objects.all()SELECT"blog_tag"."id","blog_tag"."created_at","blog_tag"."updated_at","blog_tag"."deleted_at","blog_tag"."name"FROM"blog_tag"LIMIT21Executiontime:0.000519s[Database:default]<CustomBaseModelWithSoftDeleteQuerySet[<Tag:textmate>,<Tag:pyc>,<Tag:irb>,<Tag:ipython>,<Tag:lock>,<Tag:environment>]>>>>Category.objects.get(title='Bash').delete()(9,{'blog.Post_tags':4,'blog.Category':1,'blog.Post':4})>>>Category.objects.delete()(11,{'blog.Post_tags':4,'blog.Category':3,'blog.Post':4})>>>Category.objects.inactives()SELECT"blog_category"."id","blog_category"."created_at","blog_category"."updated_at","blog_category"."deleted_at","blog_category"."title"FROM"blog_category"WHERE"blog_category"."deleted_at"ISNOTNULLLIMIT21Executiontime:0.000337s[Database:default]<CustomBaseModelWithSoftDeleteQuerySet[<Category:Bash>]>>>>Post.objects.inactives()SELECT"blog_post"."id","blog_post"."created_at","blog_post"."updated_at","blog_post"."deleted_at","blog_post"."author_id","blog_post"."category_id","blog_post"."title","blog_post"."body"FROM"blog_post"WHERE"blog_post"."deleted_at"ISNOTNULLLIMIT21Executiontime:0.000387s[Database:default]<CustomBaseModelWithSoftDeleteQuerySet[<Post:Bashpost1>,<Post:Bashpost2>,<Post:Bashpost3>,<Post:Bashpost4>]>>>>Category.objects.inactives().undelete()(9,{'blog.Post_tags':4,'blog.Category':1,'blog.Post':4})>>>Category.objects.inactives()<CustomBaseModelWithSoftDeleteQuerySet[]>>>>Post.objects.inactives()<CustomBaseModelWithSoftDeleteQuerySet[]>CustomBaseModelWithSoftDeleteQuerySethas these query options:.actives(): filters ifCustomBaseModelWithSoftDelete.deleted_atis set toNULL.inactives(): filters ifCustomBaseModelWithSoftDelete.deleted_atis not set toNULL.delete(): soft delete on given object/queryset..undelete(): recover soft deleted on given object/queryset..hard_delete(): this is real delete. this method erases given object/queryset and there is no turning back!.When soft-delete enabled (during model creation), Django admin will
automatically useCustomBaseModelAdminWithSoftDeletewhich is inherited from:CustomBaseModelAdmin<-admin.ModelAdmin.Model AdminsCustomBaseModelAdmin,CustomBaseModelAdminWithSoftDeleteInherits fromadmin.ModelAdmin. When model is created viarake new:model...or via management command, admin file is generated automatically.This model admin overridesmodels.ImageFieldform field and displays fancy
thumbnail for images. By default, uses cached paginator and setsshow_full_result_counttoFalsefor performance improvements.Model Admin Propertiesshow_goback_buttonis set toTrueby default. You can disable via;classExampleAdmin(CustomBaseModelAdminWithSoftDelete):# ...show_goback_button=False# ...show_full_result_countis set toFalseby default.hide_deleted_atis set toTrueby default. This means, you will not see
that field while editing the instance.Example forPostmodel admin (auto generated).importloggingfromdjango.contribimportadminfromconsoleimportconsolefromvb_baseapp.adminimport(CustomBaseModelAdminWithSoftDelete,)from..modelsimportPost__all__=['PostAdmin']logger=logging.getLogger('app')console=console(source=__name__)@admin.register(Post)classPostAdmin(CustomBaseModelAdminWithSoftDelete):# hide_deleted_at = FalseBy default,deleted_atexcluded from admin form likecreated_atandupdated_atfields. You can also override this viahide_deleted_atattribute. Comment/Uncomment lines according to your needs! This works only inCustomBaseModelAdminWithSoftDelete.CustomBaseModelAdminWithSoftDeletealso comes with special admin action. You can
recover/make active (undelete) multiple objects like deleting feature of
Django’s default.Extra FeaturesWhen you’re dealing with soft-deleted objects, you’ll seeHARD DELETEandRECOVERbuttons in the change form. Hard delete really wipes the items
from database. Recover, recovers/undeletes object and related elements.You’ll also haveGO BACKbutton too :)MiddleWareCustomLocaleMiddlewareThis is mostly used for our custom projects. InjectsLANGUAGE_CODEvariable torequestobject./en/path/to/page/setsrequest.LANGUAGE_CODEtoenotherwisetr.# add this to your settings/base.pyMIDDLEWARE+=['vb_baseapp.middlewares.CustomLocaleMiddleware']TimezoneMiddlewareIf you have custom user model or you havetimezonefield in yourrequest.user,
this middleware activates timezone for user.Custom Error PagesYou have a browsable (only in development mode) and customizable error handler
functions and html templates now!. Templates are undertemplates/custom_errors/folder.GoodiesHtmlDebugMixinself.hdbg(arg, arg, arg, ...)method helps you to output/debug some data
in view layer.# example view: index.pyimportloggingfromdjango.views.generic.baseimportTemplateViewfromconsoleimportconsolefromvb_baseapp.mixinsimportHtmlDebugMixin__all__=['BlogView']logger=logging.getLogger('app')console=console(source=__name__)classBlogView(HtmlDebugMixin,TemplateView):template_name='blog/index.html'defget_context_data(self,**kwargs):self.hdbg('Hello from hdbg')kwargs=super().get_context_data(**kwargs)console.dir(self.request.user)returnkwargs{% hdbg %}tag is added by default in to yourtemplates/base.htmland works
only if the settingsDEBUGis set toTrue.{%loadstatici18n%}<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>{%blocktitle%}{%endblock%}</title>{%ifDJANGO_ENV=='development'%}<link rel="stylesheet" href="{%static'css/bulma.min.0.8.0.css'%}"><script defer src="{%static'js/fontawesome.5.3.1.all.js'%}"></script>{%else%}<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.8.0/css/bulma.min.css"><script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>{%endif%}<link rel="stylesheet" href="{%static'css/vb-baseapp.css'%}"><link rel="stylesheet" href="{%static'css/application.css'%}">{%blockextra_css%}{%endblock%}<script defer src="{%static'js/application.js'%}"></script></head><body>{%hdbg%}{%blockbody%}{%endblock%}{%blockextra_js%}{%endblock%}</body></html>If you don’t want to extend fromtemplates/base.htmlyou can use your
own template. You just need to add{% hdbg %}tag in to your template if
you still want to enable this feature.We have some mini helpers and tools shipped withvb_baseapp.consoleThis little tool helps you to output anything to console. This works only
in test and development mode. If you forget console declarations in your
code, do not worry... console checksDJANGO_ENVenvironment variable...fromconsoleimportconsoleconsole=console(source=__name__)console('hello','world')You can inspect python object via.dir()method:console.dir([])p=Post.objects.actives().first()console.dir(p)More information is availableherevb_baseapp.utils.numerifyLittle helper for catchingQUERY_STRINGparameters for numerical values:fromvb_baseapp.utilsimportnumerify>>>numerify("1")1>>>numerify("1a")-1>>>numerify("ab")-1>>>numerify("abc",default=44)44vb_baseapp.utils.save_fileWhile usingFileField, sometimes you need to handle uploaded files. In this
case, you need to useupload_toattribute. Take a look at the example:fromvb_baseapp.utilsimportsave_fileascustom_save_file:::classUser(AbstractBaseUser,PermissionsMixin):::avatar=models.FileField(upload_to=save_user_avatar,verbose_name=_('Profile Image'),null=True,blank=True,)::save_user_avatarreturnscustom_save_file’s return value. Default
configuration of forcustom_save_fileissave_file(instance, filename, upload_to='upload/%Y/%m/%d/'). Uploads are go to
such asMEDIA_ROOT/upload/2017/09/21/...Make your custom uploads like:fromvb_baseapp.utilsimportsave_fileascustom_save_filedefmy_custom_uploader(instance,filename):# do your stuff# at the end, call:returncustom_save_file(instance,filename,upload_to='images/%Y/')classMyModel(models.Model):image=models.FileField(upload_to='my_custom_uploader',verbose_name=_('Profile Image'),)SlackExceptionHandlervb_baseapp.utils.log.SlackExceptionHandlerYou can send errors/exceptions toslackchannel.
Just create a slack app, get the webhook URL and set asSLACK_HOOKenvironment variable. Due to slack message size limitation,tracebackis disabled.Example message contains:http statuserror messageexception messageuser.id or Nonefull pathhttpstatus:500ERROR(internalIP):InternalServerError:/__vb_baseapp__/
Exception:Usermatchingquerydoesnotexist.
user_id:anonymous(None)fullpath:/__vb_baseapp__/?foo=!You can enable/disable inconfig/settings/production.py/config/settings/heroku.py:::'loggers':{'django.request':{'handlers':['mail_admins','slack'],'level':'ERROR','propagate':False},# remove 'slack'}::vb_baseapp.storageFileNotFoundFileSystemStorageAfter shipping/deploying Django app, users start to upload files, right ?
Then you need to implement new features etc. You can get the dump of the
database but what about uploaded files ? Sometimes files are too much or
too big. If you call, let’s say, a model’sImageField’surlproperty,
local dev server logs lot’s offile not founderrors to console.Also breaks the look of application via broken image signs in browser.Now, you won’t see any errors...FileNotFoundFileSystemStorageis a
fake storage that handles non existing files. Returnsfile-not-found.jpgfromstatic/images/folder.This isdevelopment purposesonly! Do not use in the production!You don’t need to change/add anything to your code... It’s embeded toconfig/settings/development.py:::DEFAULT_FILE_STORAGE='vb_baseapp.storage.FileNotFoundFileSystemStorage':You can disable if you like to...OverwriteStorageOverwriteStoragehelps you to overwrite file when uploading from django
admin. Example usage:# in a modelfromvb_baseapp.storageimportOverwriteStorageclassMyModel(models.Model):::photo=models.ImageField(upload_to=save_media_photo,storage=OverwriteStorage(),)::Addstorageoption in your file related fields.AdminImageFileWidgetUse this widget in your admin forms. By default, It’s already enabled inCustomBaseModelAdmin. You can also inject this to Django’s defaultModelAdminvia example:fromvb_baseapp.admin.widgetsimportAdminImageFileWidgetclassMyAdmin(admin.ModelAdmin):formfield_overrides={models.FileField:{'widget':AdminImageFileWidget},}This widget usesPillow(Python Image Library) which ships with yourbase.piprequirements file. Show image preview, width x height if the file is image.context_processors.pyBy default,vb_baseappinjects few variables to you context:DJANGO_ENVIS_DEBUGLANGUAGE_CODECURRENT_GIT_TAGCURRENT_PYTHON_VERSIONCURRENT_DJANGO_VERSIONRemindersDefault timezone is set toUTC, please change this or use according to your
needs.# config/settings/base.py# ...TIME_ZONE='UTC'# ...Management Commandsvb_baseappships with three managements commands;create_app$pythonmanage.pycreate_appNAME_OF_APPCreates new Django application underapplications/and provides application
folder structure:applications/NAME_OF_APP/
├── admin
├── management
├── migrations
├── models
├── tests
├── views
├── __init__.py
├── apps.py
└── urls.pycreate_model$pythonmanage.pycreate_modelNAME_OF_APPNAME_OF_MODELSTYLE_OF_MODELCreates Django model. You have three different model style;django: Uses Django’smodels.Modelbasemodel: UsesCustomBaseModel(which inherits frommodels.Model)softdelete: UsesCustomBaseModelWithSoftDeleteAccording to your model choice, related files will be generated.create_custom_user_model$pythonmanage.pycreate_custom_user_modelNAME_OF_APPNAME_OF_MODELSTYLE_OF_MODELThis command will work only in the beginning state of development. Creating
a custom user model is prohibited in the middle of the development. You
must decide before you create other models or run initial migrations for
Django’s default.This command creates;Admin filesModel manager filesModel admin form filesModel filesfor given argumens. Let’s say you’ll start a fresh project and want to use
custom user model. First, you need to create an app:$pythonmanage.pycreate_appblog# follow the instructions$pythonmanage.pycreate_custom_user_modelblogCustomUsersoftdelete

SetAUTH_USER_MODELinconfigfile
models/custom_user.pycreated.
admin/custom_user.pycreated.
CustomUsermodeladdedtomodels/__init__.py
CustomUsermodeladdedtoadmin/__init__.py
CustomUserformsaddedtoadmin/forms/__init__.py
admin/forms/custom_user.pycreated.

Customuserinstallationcompleted.Nowpleasecheckyour;-blog/models/custom_user.py-blog/admin/custom_user.py-blog/admin/forms/custom_user.py

Also;-`email`fieldissetto`USERNAME_FIELD`-`first_name`and`last_name`aresetas`REQUIRED_FIELDS`-`middle_name`,`profile_image`areoptionals

Makesureifallok?Makeyourchangesbeforerunningmigrations:$pythonmanage.pymakemigrations--namecreate_custom_usersWe’ve createdCustomUsermodel from softdeletable object. Default fields
are:email:EmailFieldfirst_name:CharFieldmiddle_name: (optional)CharFieldlast_name:CharFieldprofile_image: (optional)FileFieldis_active: (optional)BooleanFieldis_staff: (optional)BooleanFieldand other fields inherited fromAbstractBaseUser:passwordlast_loginand other properties fromPermissionsMixin. You can add/change or remove
fields before creating migrations. Do not forget to check these files forCustomUserfor the sake of this example:admin/custom_user.pyadmin/forms/custom_user.pymodels/custom_user.pyAlso, this management commands setsAUTH_USER_MODELvalue inconfig/base.py.
You’ll see;AUTH_USER_MODEL='blog.CustomUser'since you’ve named the custom model asCustomUser.Rake TasksYou have some handy rake tasks if you like to useruby:)$rake-T

rakedb:migrate[database]# Run migration for given database (default: 'default')rakedb:roll_back[name_of_application,name_of_migration]# Roll-back (name of application, name of migration)rakedb:shell# run database shell ..rakedb:show[name_of_application]# Show migrations for an application (default: 'all')rakedb:update[name_of_application,name_of_migration,is_empty]# Update migration (name of application, name of migration?, is empty?)rakedefault# Default task: runserver_plus (Werkzeug)rakelocale:compile# Compile locale dictionaryrakelocale:update# Update locale dictionaryrakenew:application[name_of_application]# Create new Django applicationrakenew:model[name_of_application,name_of_model,type_of_model]# Create new Model for given application: django,basemodel,softdeleterakerunserver:default# Run: runserver (Django's default server)rakerunserver:default_ipdb# Run: runserver (Django's default server) + ipdb debug supportrakerunserver:plus# Run: runserver_plus (Werkzeug)rakerunserver:plus_ipdb# Run: runserver_plus (Werkzeug) + ipdb debug supportrakeshell[repl]# Run shell+ avail: ptpython,ipython,bpython default: ptpythonraketest:browse_coverage[port]# Browse test coverageraketest:coverage[cli_args]# Show test coverage (default: '--show-missing --ignore-errors --skip-covered')raketest:run[name_of_application,verbose]# Run tests for given applicationDefault task isrunserver:plus. Just typerakethat’s it!runserver:plususesrunserver_plus. This means you have lots of debugging options!runserverbased tasksrake runserver:default: runspython manage.py runserverrake runserver:default_ipdb: runs Django’s default server with debugging
feature. You can injectbreakpoint()in your code! Debugger kicks in!rake runserver:plus: runspython manage.py runserver_plus --nothreadingrake runserver:plus_ipdb: runsrunserver:pluswith debugging!rake db:migrate[database]Migrates database with given database name. Default isdefault. If you like
to work multiple databases:# example configurationDATABASES={'default':{'ENGINE':'django.db.backends.sqlite3','NAME':os.path.join(BASE_DIR,'db','development.sqlite3'),},'my_database':{'ENGINE':'django.db.backends.sqlite3','NAME':os.path.join(BASE_DIR,'db','my_database.sqlite3'),}}You can just callrake db:migrateor specify different database like:rake db:migrate[my_database]:)rake db:show[name_of_application]Show migration information:$rakedb:show[blog]blog[X]0001_create_post_category_and_tag[]0002_add_spot_field_to_post

$rakedb:migrate
Runningmigrationfor:defaultdatabase...
Operationstoperform:Applyallmigrations:admin,auth,blog,contenttypes,sessions
Runningmigrations:Applyingblog.0002_add_spot_field_to_post...OKrake db:roll_back[name_of_application,name_of_migration]Your database must be rollable :) To see available migrations:rake db:roll_back[NAME_OF_YOUR_APPLICATION]. Look at the list and choose your
target migration. You can use just the number as shortcut. In this example,
we’ll roll back to migration number 1, which has a name:0001_create_post_category_and_tag$rakedb:roll_back[blog]Pleaseselectyourmigration:
blog[X]0001_create_post_category_and_tag[X]0002_add_spot_field_to_post

$rakedb:roll_back[blog,1]Operationstoperform:Targetspecificmigration:0001_create_post_category_and_tag,fromblog
Runningmigrations:Renderingmodelstates...DONEUnapplyingblog.0002_add_spot_field_to_post...OK

$rakedb:show[blog]blog[X]0001_create_post_category_and_tag[]0002_add_spot_field_to_postrake db:update[name_of_application,name_of_migration,is_empty]When you add/change something in your model, you need to create migrations.
Let’s say you have added new field toPostmodel in yourblogapp:If you don’t providename_of_migrationparam, you’ll endup with auto
generated name such as000X_auto_YYYMMDD_HHMM. You can also create
empty migration via 3^rd parameter:yes$rakedb:update[blog,add_spot_field_to_post]Migrationsfor'blog':applications/blog/migrations/0002_add_spot_field_to_post.py-Addfieldspottopost

$rakedb:update[blog,add_new_field_to_post,yes]# empty migration exampleMigrationsfor'blog':applications/blog/migrations/0003_add_new_field_to_post.py

$catapplications/blog/migrations/0003_add_new_field_to_post.pyempty migration output:fromdjango.dbimportmigrationsclassMigration(migrations.Migration):dependencies=[('blog','0002_add_spot_field_to_post'),]operations=[]rake db:shellRuns default database client.rake new:application[name_of_application]Creates new application with given application name!$rakenew:application[blog]rake new:model[name_of_application,name_of_model,type_of_model]Creates new model! Available model types are:django(default),basemodelsoftdelete$rakenew:model[blog,Post]# will create model using Django’s `models.Model`$rakenew:model[blog,Post,basemodel]# will create model using our `CustomBaseModel`$rakenew:model[blog,Post,softdelete]# will create model using our `CustomBaseModelWithSoftDelete`rake locale:compileandrake locale:updateWhen you make changes in your application related to locales, run:rake locale:update.
When you finish editing yourdjango.pofile, runrake locale:compile.rake shell[repl]Runs Django repl/shell with useshell_plusof [django-extensions][01].rake shell. This loads everything to your shell! Also you can see the
SQL statements while playing in shell. We have couple different repls:ptpythonbpythonipythonDefault repl is:ptpython$rakeshell
$rakeshell[bpython]$rakeshell[ipython]rake test:run[name_of_application,verbose]If you don’t providename_of_applicationdefault value will beapplications.verboseis1by default.Examples:$raketest:run
$raketest:run[vb_baseapp,2]rake test:coverage[cli_args]Get the test report. Default is--show-missing --ignore-errors --skip-coveredforcli_argsparameter.$raketest:coveragerake test:browse_coverage[port]Serves generated html coverages underhtmlcovfolder viapython. Default port
is9001Run Tests Manually$DJANGO_ENV=testpythonmanage.pytestvb_baseapp-v2# or$DJANGO_ENV=testpythonmanage.pytestvb_baseapp.tests.test_user.CustomUserTestCase# run single unit$raketest:run[vb_baseapp]Manual UsageLet’s assume you need a model called:Page. Create a file underYOUR_APP/models/page.py:# example for Django’s default model# YOUR_APP/models/page.pyfromdjango.dbimportmodels__all__=['Page',]classPage(models.Model):# define your fields here...pass# YOUR_APP/models/__init__.py# append:from.pageimport*or, you can useCustomBaseModelorCustomBaseModelWithSoftDelete:fromdjango.dbimportmodels

fromvb_baseapp.modelsimportCustomBaseModelWithSoftDelete__all__=['Page']classPage(CustomBaseModelWithSoftDelete):# define your fields here...passNow make migrations etc... Use it asfrom YOUR_APP.models import Page:)LicenseThis project is licensed under MITContributer(s)Uğur "vigo" Özyılmazel- Creator, maintainerContributeAll PR’s are welcome!fork(https://github.com/vbyazilim/django-vb-baseapp/fork)Create yourbranch(git checkout -b my-features)commityours (git commit -am 'Add awesome features')pushyourbranch(git push origin my-features)Than create a newPull Request!Change Log2019-12-05Django 2.2.82019-12-04Add custom user model generatorVersion bump2019-11-30Update and fix typos in README file2019-11-28Add tests and travis integrationVersion bump2019-11-27Version bumpReady to use...2019-08-07Initial Beta relase: 1.0.0