Last active
December 28, 2016 22:18
-
-
Save diegosorrilha/a85cc0a6c47d8d248c8f6199fc233872 to your computer and use it in GitHub Desktop.
Script that creates a basic Django project with a simple home page, 3 tests and settings using python-decouple.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Shell script to create a basics Django project. | |
# This script require Python 3.x and pyenv | |
# This script was based in gist of @rg3915 => https://gist.github.com/rg3915/a264d0ade860d2f2b4bf | |
# The project contains: | |
# Settings config to Django 1.10 | |
# Admin config | |
# Tests in view home | |
# Download: | |
# curl https://gist.githubusercontent.com/diegosorrilha/a85cc0a6c47d8d248c8f6199fc233872/raw/6d1996f07d041010960db735e6da83fedf8c934e/django-start.sh -o django-start.sh | |
# Usage: | |
# source django-start.sh myproject | |
# Colors | |
red=`tput setaf 1` | |
green=`tput setaf 2` | |
yellow=`tput setaf 3` | |
reset=`tput sgr0` | |
echo "${green}>>> Deactivating the virtualenv${reset}" | |
deactivate | |
PROJECT=${1-myproject} | |
echo "${green}>>> The name of the project is ${yellow}'$PROJECT'${reset}" | |
echo "${green}>>> Remove djangoproject${reset}" | |
rm -rf djangoproject | |
echo "${green}>>> Creating djangoproject${reset}" | |
mkdir djangoproject | |
cd djangoproject | |
echo "${green}>>> Creating virtualenv${reset}" | |
python -m venv .venv | |
echo "${green}>>> .venv is created${reset}" | |
# active | |
sleep 2 | |
echo "${green}>>> activate the .venv${reset}" | |
source .venv/bin/activate | |
PS1="(`basename \"$VIRTUAL_ENV\"`)\e[1;34m:/\W\e[00m$ " | |
sleep 2 | |
# install Django | |
echo "${green}>>> Installing the Django and all dependencies${reset}" | |
pip install -U pip | |
cat << EOF > requirements-base.txt | |
dj-database-url==0.4.1 | |
dj-static==0.0.6 | |
Django==1.10 | |
python-decouple==3.0 | |
static3==0.7.0 | |
django-extensions==1.7.4 | |
django-test-without-migrations==0.4 | |
pytz | |
EOF | |
cat << EOF > requirements.txt | |
-r requirements-base.txt | |
gunicorn==19.4.1 | |
psycopg2==2.6.1 | |
EOF | |
cat << EOF > requirements-dev.txt | |
-r requirements-base.txt | |
ipython[notebook] | |
EOF | |
pip install -r requirements-dev.txt | |
# Create contrib/env-sample | |
echo "${green}>>> Creating the contrib/env-sample${reset}" | |
mkdir contrib | |
cat << EOF > contrib/env-sample | |
SECRET_KEY=THIS_IS_NOT_A_GOOD_SECRET | |
DEBUG=True | |
ALLOWED_HOSTS=127.0.0.1, .localhost, .herokuapp.com | |
EOF | |
echo "${green}>>> Copy env-sample to .env${reset}" | |
cp contrib/env-sample .env | |
echo "${green}>>> Creating the contrib/secret_gen.py${reset}" | |
cat << EOF > contrib/secret_gen.py | |
#!/usr/bin/env python | |
""" | |
Django SECRET_KEY generator. | |
""" | |
from django.utils.crypto import get_random_string | |
chars = 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)' | |
print(get_random_string(50, chars)) | |
EOF | |
echo "${green}>>> Creating .gitignore${reset}" | |
cat << EOF > .gitignore | |
__pycache__/ | |
*.py[cod] | |
*.sqlite3 | |
*.env | |
.idea | |
*.DS_Store | |
.venv/ | |
staticfiles/ | |
.ipynb_checkpoints/ | |
EOF | |
# Create the project | |
echo "${green}>>> Creating the project '$PROJECT' ...${reset}" | |
django-admin.py startproject $PROJECT . | |
cd $PROJECT | |
echo "${green}>>> Creating the app 'core' ...${reset}" | |
python ../manage.py startapp core | |
echo "${green}>>> Creating tests directory${reset}" | |
mkdir core/tests | |
touch core/tests/__init__.py | |
rm -f core/tests.py | |
cat << EOF > test_view_home.py | |
from django.test import TestCase | |
class HomeTest(TestCase): | |
def setUp(self): | |
self.response = self.client.get('/') | |
def test_get(self): | |
"""GET / must return status code 200""" | |
self.assertEqual(200, self.response.status_code) | |
def test_template(self): | |
"""Must use index.html""" | |
self.assertTemplateUsed(self.response, 'index.html') | |
def test_admin_link(self): | |
"""Must contains link to admin""" | |
self.assertContains(self.response, 'href="/admin/"') | |
EOF | |
echo "${green}>>> Creating static/css directory${reset}" | |
mkdir -p core/static/css | |
echo "${green}>>> Creating main.css${reset}" | |
cat << EOF > core/static/css/main.css | |
/* Sticky footer styles | |
-------------------------------------------------- */ | |
/* http://getbootstrap.com/examples/sticky-footer-navbar/sticky-footer-navbar.css */ | |
/* http://getbootstrap.com/2.3.2/examples/sticky-footer.html */ | |
html { | |
position: relative; | |
min-height: 100%; | |
} | |
body { | |
/* Margin bottom by footer height */ | |
margin-bottom: 60px; | |
} | |
#footer { | |
position: absolute; | |
bottom: 0; | |
width: 100%; | |
/* Set the fixed height of the footer here */ | |
height: 60px; | |
background-color: #101010; | |
color: #ccc; | |
} | |
.credit { | |
/* Center vertical text */ | |
margin: 20px 0; | |
} | |
/* Lastly, apply responsive CSS fixes as necessary */ | |
@media (max-width: 767px) { | |
body { | |
margin-bottom: 120px; | |
} | |
#footer { | |
height: 120px; | |
padding-left: 5px; | |
padding-right: 5px; | |
} | |
} | |
/* My personal styles. */ | |
.ok { | |
color: #44AD41; /*verde*/ | |
} | |
.no { | |
color: #DE2121; /*vermelho*/ | |
} | |
EOF | |
echo "${green}>>> Creating social.css${reset}" | |
cat << EOF > core/static/css/social.css | |
/* http://www.kodingmadesimple.com/2014/11/create-stylish-bootstrap-3-social-media-icons.html */ | |
.social { | |
margin: 0; | |
padding: 0; | |
} | |
.social ul { | |
margin: 0; | |
padding: 5px; | |
} | |
.social ul li { | |
margin: 5px; | |
list-style: none outside none; | |
display: inline-block; | |
} | |
.social i { | |
width: 40px; | |
height: 40px; | |
color: #FFF; | |
background-color: #909AA0; | |
font-size: 22px; | |
text-align:center; | |
padding-top: 12px; | |
border-radius: 50%; | |
-moz-border-radius: 50%; | |
-webkit-border-radius: 50%; | |
-o-border-radius: 50%; | |
transition: all ease 0.3s; | |
-moz-transition: all ease 0.3s; | |
-webkit-transition: all ease 0.3s; | |
-o-transition: all ease 0.3s; | |
-ms-transition: all ease 0.3s; | |
text-decoration: none; | |
} | |
.social .fa-facebook { | |
background: #4060A5; | |
} | |
.social .fa-twitter { | |
background: #00ABE3; | |
} | |
.social .fa-google-plus { | |
background: #e64522; | |
} | |
.social .fa-github { | |
background: #343434; | |
} | |
.social .fa-pinterest { | |
background: #cb2027; | |
} | |
.social .fa-linkedin { | |
background: #0094BC; | |
} | |
.social .fa-flickr { | |
background: #FF57AE; | |
} | |
.social .fa-instagram { | |
background: #375989; | |
} | |
.social .fa-vimeo-square { | |
background: #83DAEB; | |
} | |
.social .fa-stack-overflow { | |
background: #FEA501; | |
} | |
.social .fa-dropbox { | |
background: #017FE5; | |
} | |
.social .fa-tumblr { | |
background: #3a5876; | |
} | |
.social .fa-dribbble { | |
background: #F46899; | |
} | |
.social .fa-skype { | |
background: #00C6FF; | |
} | |
.social .fa-stack-exchange { | |
background: #4D86C9; | |
} | |
.social .fa-youtube { | |
background: #FF1F25; | |
} | |
.social .fa-xing { | |
background: #005C5E; | |
} | |
.social .fa-rss { | |
background: #e88845; | |
} | |
.social .fa-foursquare { | |
background: #09B9E0; | |
} | |
.social .fa-youtube-play { | |
background: #DF192A; | |
} | |
.social .fa-slack { | |
background: #4F3A4B; | |
} | |
.social .fa-whatsapp { | |
background: #65BC54; | |
} | |
.socialfooter { | |
margin: 0; | |
padding: 0; | |
} | |
.socialfooter ul { | |
margin: 0; | |
padding: 5px; | |
} | |
.socialfooter ul li { | |
margin: 5px; | |
list-style: none outside none; | |
display: inline-block; | |
} | |
.socialfooter i { | |
color: #FFF; | |
font-size: 22px; | |
text-align:center; | |
padding-top: 12px; | |
border-radius: 50%; | |
-moz-border-radius: 50%; | |
-webkit-border-radius: 50%; | |
-o-border-radius: 50%; | |
transition: all ease 0.3s; | |
-moz-transition: all ease 0.3s; | |
-webkit-transition: all ease 0.3s; | |
-o-transition: all ease 0.3s; | |
-ms-transition: all ease 0.3s; | |
text-decoration: none; | |
} | |
.socialfooter i:hover { | |
color: #00ABE3; | |
} | |
EOF | |
echo "${green}>>> Creating templates directory${reset}" | |
mkdir -p core/templates/core | |
echo "${green}>>> Creating base.html${reset}" | |
cat << EOF > core/templates/base.html | |
{% load static %} | |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> | |
<meta name="description" content="Django boilerplate"> | |
<meta name="author" content="rg3915"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> | |
<link rel="shortcut icon" href="https://www.djangoproject.com/favicon.ico"> | |
<title> | |
{% block title %}$PROJECT{% endblock title %} | |
</title> | |
<!-- Bootstrap core CSS --> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css"> | |
<link rel="stylesheet" href="{% static "css/main.css" %}"> | |
<link rel="stylesheet" href="{% static "css/social.css" %}"> | |
<!-- Bootstrap JS --> | |
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script> | |
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script> | |
<style type="text/css"> | |
body { | |
padding-top: 50px; | |
/*color: #5a5a5a;*/ | |
} | |
</style> | |
</head> | |
<body> | |
{% include "nav.html" %} | |
<div id="wrap"> | |
<div class="container"> | |
{% block content %}{% endblock content %} | |
</div> | |
</div> | |
{% include "footer.html" %} | |
</body> | |
</html> | |
EOF | |
echo "${green}>>> Creating index.html${reset}" | |
cat << EOF > core/templates/index.html | |
{% extends "base.html" %} | |
{% block content %} | |
<div class="jumbotron"> | |
<img src="http://imagens.tiespecialistas.com.br/2016/02/django-allauth.png" class="img-responsive" alt="Responsive image"> | |
</div> | |
{% endblock content %} | |
EOF | |
echo "${green}>>> Creating nav.html${reset}" | |
cat << EOF > core/templates/nav.html | |
<!-- Menu --> | |
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation"> | |
<div class="container-fluid"> | |
<div class="navbar-header"> | |
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"> | |
<span class="icon-bar"></span> | |
<span class="icon-bar"></span> | |
<span class="icon-bar"></span> | |
</button> | |
</div> | |
<div class="navbar-collapse collapse"> | |
<ul class="nav navbar-nav"> | |
<li class="current"><a href="#"><span class="glyphicon glyphicon-home"></span> Home</a></li> | |
</ul> | |
<ul class="nav navbar-nav navbar-right"> | |
<li><a href="{% url 'admin:index' %}"><span class="fa fa-cog"></span> Admin</a></li> | |
</ul> | |
</div> | |
</div> | |
</div> | |
EOF | |
echo "${green}>>> Creating footer.html${reset}" | |
cat << EOF > core/templates/footer.html | |
<div id="footer"> | |
<div class="container"> | |
<div class="socialfooter pull-center"> | |
<ul> | |
<li><a href="#"><i class="fa fa-facebook"></i></a></li> | |
<li><a href="#"><i class="fa fa-twitter"></i></a></li> | |
<li><a href="#"><i class="fa fa-google-plus"></i></a></li> | |
<li><a href="#"><i class="fa fa-github"></i></a></li> | |
<li><a href="#"><i class="fa fa-pinterest"></i></a></li> | |
<li><a href="#"><i class="fa fa-linkedin"></i></a></li> | |
<li><a href="#"><i class="fa fa-instagram"></i></a></li> | |
<li><a href="#"><i class="fa fa-skype"></i></a></li> | |
<li><a href="#"><i class="fa fa-slack"></i></a></li> | |
</ul> | |
</div> | |
</div> | |
</div> | |
EOF | |
# up one level | |
cd .. | |
# ********** EDITING FILES ********** | |
echo "${green}>>> Refactor .env${reset}" | |
# find SECRET_KEY | |
grep "SECRET_KEY" $PROJECT/settings.py > .env | |
# replace = | |
sed -i "s/ = /=/g" .env | |
# replace ' | |
sed -i "s/'//g" .env | |
cat << EOF >> .env | |
DEBUG=True | |
ALLOWED_HOSTS=127.0.0.1, .localhost, .herokuapp.com | |
EOF | |
echo "${green}>>> Editing settings.py${reset}" | |
# insert text in line below of string | |
cat << EOF > $PROJECT/settings.py | |
""" | |
Django settings for $PROJECT project. | |
Generated by 'django-admin startproject' using Django 1.10. | |
For more information on this file, see | |
https://docs.djangoproject.com/en/1.10/topics/settings/ | |
For the full list of settings and their values, see | |
https://docs.djangoproject.com/en/1.10/ref/settings/ | |
""" | |
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) | |
import os | |
from decouple import config, Csv | |
from dj_database_url import parse as dburl | |
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
# Quick-start development settings - unsuitable for production | |
# See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/ | |
SECRET_KEY = config('SECRET_KEY') | |
# SECURITY WARNING: don't run with debug turned on in production! | |
#DEBUG = True | |
DEBUG=config('DEBUG', default=False, cast=bool) | |
ALLOWED_HOSTS = config('ALLOWED_HOSTS', default=[], cast=Csv()) | |
DEFAULT_FROM_EMAIL = '[email protected]' | |
# Application definition | |
INSTALLED_APPS = ( | |
'django.contrib.admin', | |
'django.contrib.auth', | |
'django.contrib.contenttypes', | |
'django.contrib.sessions', | |
'django.contrib.messages', | |
'django.contrib.staticfiles', | |
'test_without_migrations', | |
'django_extensions', | |
'$PROJECT.core', | |
) | |
MIDDLEWARE_CLASSES = ( | |
'django.contrib.sessions.middleware.SessionMiddleware', | |
'django.middleware.common.CommonMiddleware', | |
'django.middleware.csrf.CsrfViewMiddleware', | |
'django.contrib.auth.middleware.AuthenticationMiddleware', | |
'django.contrib.auth.middleware.SessionAuthenticationMiddleware', | |
'django.contrib.messages.middleware.MessageMiddleware', | |
'django.middleware.clickjacking.XFrameOptionsMiddleware', | |
'django.middleware.security.SecurityMiddleware', | |
) | |
ROOT_URLCONF = '$PROJECT.urls' | |
TEMPLATES = [ | |
{ | |
'BACKEND': 'django.template.backends.django.DjangoTemplates', | |
'DIRS': [], | |
'APP_DIRS': True, | |
'OPTIONS': { | |
'context_processors': [ | |
'django.template.context_processors.debug', | |
'django.template.context_processors.request', | |
'django.contrib.auth.context_processors.auth', | |
'django.contrib.messages.context_processors.messages', | |
], | |
}, | |
}, | |
] | |
WSGI_APPLICATION = '$PROJECT.wsgi.application' | |
# Database | |
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases | |
default_dburl = 'sqlite:///' + os.path.join(BASE_DIR, 'db.sqlite3') | |
DATABASES = { | |
'default': config('DATABASE_URL', default=default_dburl, cast=dburl), | |
} | |
# Internationalization | |
# https://docs.djangoproject.com/en/1.10/topics/i18n/ | |
LANGUAGE_CODE = 'pt-br' | |
TIME_ZONE = 'UTC' | |
USE_I18N = True | |
USE_L10N = True | |
USE_TZ = True | |
# Static files (CSS, JavaScript, Images) | |
# https://docs.djangoproject.com/en/1.10/howto/static-files/ | |
STATIC_URL = '/static/' | |
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') | |
EOF | |
echo "${green}>>> Editing wsgi.py${reset}" | |
# insert text in line below of string | |
cat << EOF > $PROJECT/wsgi.py | |
""" | |
WSGI config for $PROJECT project. | |
It exposes the WSGI callable as a module-level variable named ``application``. | |
For more information on this file, see | |
https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/ | |
""" | |
import os | |
from dj_static import Cling | |
from django.core.wsgi import get_wsgi_application | |
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "$PROJECT.settings") | |
application = Cling(get_wsgi_application()) | |
EOF | |
echo "${green}>>> Editing urls.py${reset}" | |
cat << EOF > $PROJECT/urls.py | |
"""$PROJECT URL Configuration | |
The `urlpatterns` list routes URLs to views. For more information please see: | |
https://docs.djangoproject.com/en/1.10/topics/http/urls/ | |
Examples: | |
Function views | |
1. Add an import: from my_app import views | |
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') | |
Class-based views | |
1. Add an import: from other_app.views import Home | |
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') | |
Including another URLconf | |
1. Add an import: from blog import urls as blog_urls | |
2. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls)) | |
""" | |
from django.conf.urls import include, url | |
from django.contrib import admin | |
from $PROJECT.core.views import home | |
urlpatterns = [ | |
url(r'^$', home), | |
url(r'^admin/', include(admin.site.urls)), | |
] | |
EOF | |
echo "${green}>>> Editing views.py${reset}" | |
cat << EOF > $PROJECT/core/views.py | |
from django.shortcuts import render | |
def home(request): | |
return render(request, 'index.html') | |
EOF | |
# migrate | |
python manage.py makemigrations | |
python manage.py migrate | |
echo "${green}>>> Running tests${reset}" | |
python manage.py test | |
clear | |
echo "${yellow}>>> Great! Django App created! :D ${reset}" | |
echo "" | |
echo -n "Create superuser? (y/N) " | |
read answer | |
if [ "$answer" == "y" ]; then | |
echo "${green}>>> Creating a 'admin' user ...${reset}" | |
echo "${green}>>> The password must contain at least 8 characters.${reset}" | |
echo "${green}>>> Password suggestions: demodemo${reset}" | |
python manage.py createsuperuser --username='admin' --email='' | |
fi | |
echo "${green}>>> Running tests again${reset}" | |
python manage.py test | |
echo "${green}>>> Creating the first commit${reset}" | |
git init | |
git add . | |
git commit -m "first commit" | |
sleep 2 | |
clear | |
echo "${yellow}>>> Nice! $PROJECT created with superuser! :D${reset}" | |
echo "" | |
echo -n "Send project to heroku? (y/N)" | |
read answer | |
if [ "$answer" == "y" ]; then | |
curl https://gist.githubusercontent.com/diegosorrilha/68e2f411265c81e79ebc379a9ac26d49/raw/6b9c22779bbaf58a9de914ac78848f4701211e2b/django-start-deploy-heroku.sh -o ../django-start-deploy-heroku.sh | |
source ../django-start-deploy-heroku.sh $PROJECT | |
rm ../django-start-deploy-heroku.sh | |
else | |
clear | |
echo "${yellow}>>> OK! running $PROJECT in http://127.0.0.1:8000${reset}" | |
python manage.py runserver | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment