5.1 Django¶
When we are implementing simple Django application and we want to start developer server (runserver command), we
need to take care of few steps before using the server:
- update requiretments from setup.py or requiretments.txt
- apply migrations
- start celery
- start runserver
By using Baelfire, we can automate this process, so all these steps will be made before starting the runserver but only when needed.
Code from this example is avalible here: https://github.com/socek/bael-django
5.1.1 Create the application¶
First of all, we need to create simple Django application for tests. It will not have any views, because all we want for now is to make sure the runserver will start. We will not go into details here, because it is not a Django tutorial. We will start with the setup.py file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # -*- encoding: utf-8 -*-
from setuptools import find_packages
from setuptools import setup
install_requires = [
'baelfire==0.5',
'Django==1.11.2',
]
if __name__ == '__main__':
setup(
name='bdjango',
packages=find_packages(),
install_requires=install_requires,
)
|
I like to have simple makefile to create virtualenv and install first, so here it is:
1 2 3 4 5 6 | venv_bdjango/bin/python: venv_bdjango setup.py
./venv_bdjango/bin/python setup.py develop
@touch venv_bdjango/bin/python
venv_bdjango:
virtualenv venv_bdjango
|
Now we can run it and activate virtualenv.
$ make
virtualenv venv_bdjango
Using base prefix '/usr'
New python executable in /home/socek/projects/bael-django/venv_bdjango/bin/python3
Also creating executable in /home/socek/projects/bael-django/venv_bdjango/bin/python
Installing setuptools, pip, wheel...done.
./venv_bdjango/bin/python setup.py develop
running develop
running egg_info
creating bdjango.egg-info
(...)
Using /home/socek/projects/bael-django/venv_bdjango/lib/python3.6/site-packages
Finished processing dependencies for bdjango==0.0.0
$ source ./venv_bdjango/bin/activate
(venv_bdjango) $
Now we can create our application and start runserver.
(venv_bdjango) $ django-admin startproject mysite
(venv_bdjango) $ python mysite/manage.py runserver
Performing system checks...
System check identified no issues (0 silenced).
You have 13 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
June 30, 2017 - 10:34:49
Django version 1.11.2, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Our simple Django application is ready.
5.1.2 Baelfire package and first task¶
First step in creating automatizion is to create package that will be outside of the django app, which we have created a moment ego.
(venv_bdjango) $ mkdir -p bdjango/
(venv_bdjango) $ touch bdjango/__init__.py
And the first task which will run python setup.py develop
1 2 3 4 5 6 7 8 9 10 11 | from baelfire.dependencies import AlwaysTrue
from baelfire.task import SubprocessTask
class UpdateRequirements(SubprocessTask):
def create_dependecies(self):
self.build_if(AlwaysTrue())
def build(self):
self.popen('python setup.py develop')
|
Now we can start our first task and see what it will do:
(venv_bdjango) $ bael -t bdjango.tasks:UpdateRequirements
* INFO bdjango.tasks.UpdateRequirements: Running *
running develop
running egg_info
writing bdjango.egg-info/PKG-INFO
writing dependency_links to bdjango.egg-info/dependency_links.txt
writing requirements to bdjango.egg-info/requires.txt
writing top-level names to bdjango.egg-info/top_level.txt
reading manifest file 'bdjango.egg-info/SOURCES.txt'
writing manifest file 'bdjango.egg-info/SOURCES.txt'
running build_ext
(...)
Ok, but now we have hardcoded path for python executable and for setup.py. This will not work if we change our workdir.
(venv_bdjango) $ cd mysite
(venv_bdjango) mysite/ $ bael -t bdjango.tasks:UpdateRequirements
* INFO bdjango.tasks.UpdateRequirements: Running *
python: can't open file 'setup.py': [Errno 2] No such file or directory
* ERROR bdjango.tasks.UpdateRequirements: Error: Command error (2): *
* ERROR baelfire.application.application: Error in .baelfire.report *
Traceback (most recent call last):
File "/home/socek/projects/bael-django/venv_bdjango/bin/bael", line 11, in <module>
load_entry_point('baelfire', 'console_scripts', 'bael')()
File "/home/socek/projects/bael-django/venv_bdjango/lib/python3.6/site-packages/baelfire-0.5.0-py3.6.egg/baelfire/application/application.py", line 117, in run
Application().run()
File "/home/socek/projects/bael-django/venv_bdjango/lib/python3.6/site-packages/baelfire-0.5.0-py3.6.egg/baelfire/application/application.py", line 113, in run
self.run_command_or_print_help(args)
File "/home/socek/projects/bael-django/venv_bdjango/lib/python3.6/site-packages/baelfire-0.5.0-py3.6.egg/baelfire/application/application.py", line 86, in run_command_or_print_help
task.run()
File "/home/socek/projects/bael-django/venv_bdjango/lib/python3.6/site-packages/baelfire-0.5.0-py3.6.egg/baelfire/task/task.py", line 59, in run
self._step_build()
File "/home/socek/projects/bael-django/venv_bdjango/lib/python3.6/site-packages/baelfire-0.5.0-py3.6.egg/baelfire/task/task.py", line 69, in _step_build
self.phase_build()
File "/home/socek/projects/bael-django/venv_bdjango/lib/python3.6/site-packages/baelfire-0.5.0-py3.6.egg/baelfire/task/task.py", line 108, in phase_build
self.build()
File "/home/socek/projects/bael-django/bdjango/tasks.py", line 11, in build
self.popen('python setup.py develop')
File "/home/socek/projects/bael-django/venv_bdjango/lib/python3.6/site-packages/baelfire-0.5.0-py3.6.egg/baelfire/task/process.py", line 41, in popen
self._post_popen()
File "/home/socek/projects/bael-django/venv_bdjango/lib/python3.6/site-packages/baelfire-0.5.0-py3.6.egg/baelfire/task/process.py", line 66, in _post_popen
raise CommandError(self.spp.returncode)
baelfire.error.CommandError: Error: Command error (2):
So now we will implement Core class and do some path configurations.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | from os.path import dirname
from baelfire.core import Core
class BdCore(Core):
def phase_settings(self):
super(BdCore, self).phase_settings()
self.paths.set('project', self.get_project_dir(), is_root=True)
self.paths.set('venv', 'venv_bdjango', parent='project')
self.paths.set('venv:bin', 'bin', parent='venv')
self.paths.set('exe:python', 'python', parent='venv:bin')
self.paths.set('setuppy', 'setup.py', parent='project')
def get_project_dir(self):
project_dir = __file__
for index in range(2):
project_dir = dirname(project_dir)
return project_dir
|
At line 9 we are setting the main project path. The rest of the paths are created depending on this main path. Now we need to implement these settings in our task.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | from baelfire.dependencies import AlwaysTrue
from baelfire.task import SubprocessTask
class UpdateRequirements(SubprocessTask):
def create_dependecies(self):
self.build_if(AlwaysTrue())
def build(self):
self.popen('{python} {setuppy} develop'.format(
python=self.paths.get('exe:python'),
setuppy=self.paths.get('setuppy')),
)
|
Last step for this paragraph is to make task with proper Core installed, so we need to create new file with an endpoint.
1 2 3 4 5 6 | from bdjango.core import BdCore
from bdjango.tasks import UpdateRequirements
def update_requirements():
return UpdateRequirements(BdCore())
|
And now we can run it:
(venv_bdjango) $ cd mysite
(venv_bdjango) mysite/ $ bael -t bdjango.cmd:update_requirements
* INFO bdjango.tasks.UpdateRequirements: Running *
running develop
running egg_info
creating bdjango.egg-info
writing bdjango.egg-info/PKG-INFO
writing dependency_links to bdjango.egg-info/dependency_links.txt
writing requirements to bdjango.egg-info/requires.txt
writing top-level names to bdjango.egg-info/top_level.txt
writing manifest file 'bdjango.egg-info/SOURCES.txt'
reading manifest file 'bdjango.egg-info/SOURCES.txt'
writing manifest file 'bdjango.egg-info/SOURCES.txt'
running build_ext
Creating /home/socek/projects/bael-django/venv_bdjango/lib/python3.6/site-packages/bdjango.egg-link (link to .)
Removing bdjango 0.0.0 from easy-install.pth file
Adding bdjango 0.0.0 to easy-install.pth file
(...)
5.1.3 First real dependency¶
So now we have a script, which will always run python setup.py develop. But we need to run this only, when setup.py
has rebuilded. That is why we need to change the task implementation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | from baelfire.task import FileTask
from baelfire.task import SubprocessTask
from baelfire.dependencies import FileChanged
class UpdateRequirements(SubprocessTask, FileTask):
output_name = 'flags:requirements'
def create_dependecies(self):
self.build_if(FileChanged('setuppy'))
def build(self):
self.popen('{python} {setuppy} develop'.format(
python=self.paths.get('exe:python'),
setuppy=self.paths.get('setuppy')),
)
open(self.output, 'w').close()
|
And also, we need to add some configurations to the core.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | from os.path import dirname
from baelfire.core import Core
class BdCore(Core):
def phase_settings(self):
super(BdCore, self).phase_settings()
self.paths.set('project', self.get_project_dir(), is_root=True)
self.paths.set('venv', 'venv_bdjango', parent='project')
self.paths.set('venv:bin', 'bin', parent='venv')
self.paths.set('exe:python', 'python', parent='venv:bin')
self.paths.set('setuppy', 'setup.py', parent='project')
self.paths.set('bdjango', 'bdjango', parent='project')
self.paths.set('flags', 'flags', parent='bdjango')
self.paths.set('flags:requirements', 'req.flag', parent='flags')
def get_project_dir(self):
project_dir = __file__
for index in range(2):
project_dir = dirname(project_dir)
return project_dir
|
In this example, when we make update and we will not change the setup.py file, the script will not start the rebuild.
In those scripts we use bdjango/flags directory, as a place for storing the flags. You should create a folder before
running the bael application.
(venv_bdjango) $ mkdir -p bdjango/flags
(venv_bdjango) $ bael -t bdjango.cmd:update_requirements
* INFO bdjango.tasks.UpdateRequirements: Running *
running develop
running egg_info
writing bdjango.egg-info/PKG-INFO
writing dependency_links to bdjango.egg-info/dependency_links.txt
writing requirements to bdjango.egg-info/requires.txt
writing top-level names to bdjango.egg-info/top_level.txt
reading manifest file 'bdjango.egg-info/SOURCES.txt'
writing manifest file 'bdjango.egg-info/SOURCES.txt'
running build_ext
Creating /home/socek/projects/bael-django/venv_bdjango/lib/python3.6/site-packages/bdjango.egg-link (link to .)
bdjango 0.0.0 is already the active version in easy-install.pth
(...)
(venv_bdjango) $ bael -t bdjango.cmd:update_requirements
(venv_bdjango) $ touch setup.py
(venv_bdjango) $ bael -t bdjango.cmd:update_requirements
* INFO bdjango.tasks.UpdateRequirements: Running *
running develop
running egg_info
writing bdjango.egg-info/PKG-INFO
writing dependency_links to bdjango.egg-info/dependency_links.txt
writing requirements to bdjango.egg-info/requires.txt
writing top-level names to bdjango.egg-info/top_level.txt
reading manifest file 'bdjango.egg-info/SOURCES.txt'
writing manifest file 'bdjango.egg-info/SOURCES.txt'
running build_ext
Creating /home/socek/projects/bael-django/venv_bdjango/lib/python3.6/site-packages/bdjango.egg-link (link to .)
bdjango 0.0.0 is already the active version in easy-install.pth
(...)
5.1.4 Run Task in chain¶
If we can create a task for updating the requiretments, why not create a task for creating flags folder? It is very simple to do that.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | from os import mkdir
from baelfire.dependencies import FileChanged
from baelfire.dependencies import TaskRebuilded
from baelfire.task import FileTask
from baelfire.task import SubprocessTask
class CreateFlagsFolder(FileTask):
output_name = 'flags'
def build(self):
mkdir(self.output)
class UpdateRequirements(SubprocessTask, FileTask):
output_name = 'flags:requirements'
def create_dependecies(self):
self.build_if(TaskRebuilded(CreateFlagsFolder()))
self.build_if(FileChanged('setuppy'))
def build(self):
self.popen('{python} {setuppy} develop'.format(
python=self.paths.get('exe:python'),
setuppy=self.paths.get('setuppy')),
)
open(self.output, 'w').close()
|
CreateFlagsFolder is pretty simple. FileTask has a buildf_if(FileDoesNotExists(self.output)) in the dependency
list, so we do not need to implement it. Also we added 1 more dependency in line 22, which indicates that the
UpdateRequirements task will first run CreateFlagsFolder and rebuild itself if the CreateFlagsFolder has
rebuild.
(venv_bdjango) $ bael -t bdjango.cmd:update_requirements
(venv_bdjango) $ rm -rf bdjango/flags
(venv_bdjango) $ bael -t bdjango.cmd:update_requirements
* INFO bdjango.tasks.CreateFlagsFolder: Running *
* INFO bdjango.tasks.UpdateRequirements: Running *
running develop
running egg_info
writing bdjango.egg-info/PKG-INFO
writing dependency_links to bdjango.egg-info/dependency_links.txt
writing requirements to bdjango.egg-info/requires.txt
writing top-level names to bdjango.egg-info/top_level.txt
reading manifest file 'bdjango.egg-info/SOURCES.txt'
writing manifest file 'bdjango.egg-info/SOURCES.txt'
running build_ext
Creating /home/socek/projects/bael-django/venv_bdjango/lib/python3.6/site-packages/bdjango.egg-link (link to .)
bdjango 0.0.0 is already the active version in easy-install.pth
At this point, we have already created the flags folder by hand, that is why the first run did nothing. But after removing the folder, the whole chain has been rebuild.
Most of the Python projects use requiretments.txt file instead of setup.py. In our sample project we will use both, just for the sake of creating Baelfire tasks.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | from os import mkdir
from baelfire.dependencies import FileChanged
from baelfire.dependencies import TaskRebuilded
from baelfire.task import FileTask
from baelfire.task import SubprocessTask
from baelfire.task import Task
class CreateFlagsFolder(FileTask):
output_name = 'flags'
def build(self):
mkdir(self.output)
class BaseRequirements(SubprocessTask, FileTask):
def create_dependecies(self):
super(BaseRequirements, self).create_dependecies()
self.build_if(TaskRebuilded(CreateFlagsFolder()))
def _update_flag(self):
open(self.output, 'w').close()
class SetupPyDevelop(BaseRequirements):
output_name = 'flags:setuppy'
def create_dependecies(self):
super(SetupPyDevelop, self).create_dependecies()
self.build_if(FileChanged('requirementst_production'))
def build(self):
self.popen(
'{pip} install -r {file}'.format(
pip=self.paths.get('exe:pip'),
file=self.paths.get('requirementst_production')))
self._update_flag()
class UpdateRequirementsProduction(BaseRequirements):
output_name = 'flags:requirements'
def create_dependecies(self):
super(UpdateRequirementsProduction, self).create_dependecies()
self.build_if(FileChanged('setuppy'))
def build(self):
self.popen(
'{python} {setuppy} develop'.format(
python=self.paths.get('exe:python'),
setuppy=self.paths.get('setuppy')))
self._update_flag()
class UpdateRequirements(Task):
def create_dependecies(self):
self.run_before(SetupPyDevelop())
self.run_before(UpdateRequirementsProduction())
def build(self):
pass
|
Our goal is to update setup.py develop and requiretments.txt in different tasks. I did not wanted to change
cmd.py file, so I moved what we had in UpdateRequirements into SetupPyDevelop and created base class
BaseRequirements in order to not repeat the same code for UpdateRequirementsProduction. UpdateRequirements
was created to link those two tasks, but it does not need to build anything.
Some changes needs to be done in the core file as well. Also, we have a new file: requiretments.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | from os.path import dirname
from baelfire.core import Core
class BdCore(Core):
def phase_settings(self):
super(BdCore, self).phase_settings()
self.paths.set('project', self.get_project_dir(), is_root=True)
self.paths.set('venv', 'venv_bdjango', parent='project')
self.paths.set('venv:bin', 'bin', parent='venv')
self.paths.set('exe:python', 'python', parent='venv:bin')
self.paths.set('exe:pip', 'pip', parent='venv:bin')
self.paths.set('setuppy', 'setup.py', parent='project')
self.paths.set('requirementst_production', 'requirements.txt', parent='project')
self.paths.set('bdjango', 'bdjango', parent='project')
self.paths.set('flags', 'flags', parent='bdjango')
self.paths.set('flags:requirements', 'req.flag', parent='flags')
self.paths.set('flags:setuppy', 'setuppy.flag', parent='flags')
def get_project_dir(self):
project_dir = __file__
for index in range(2):
project_dir = dirname(project_dir)
return project_dir
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | baelfire==0.5.0
coverage==4.4.1
Django==1.11.2
Jinja2==2.9.6
MarkupSafe==1.0
mock==2.0.0
MorfDict==0.4.1
pbr==3.1.1
py==1.4.34
pytest==3.1.2
pytest-cov==2.5.1
pytz==2017.2
PyYAML==3.12
six==1.10.0
|
So now we can run our newly created baelfire tasks.
(venv_bdjango) $ touch requirements.txt
(venv_bdjango) $ bael -t bdjango.cmd:update_requirements
* INFO bdjango.tasks.UpdateRequirementsProduction: Running *
Requirement already satisfied: baelfire==0.5.0 in ./venv_bdjango/lib/python3.6/site-packages/baelfire-0.5.0-py3.6.egg (from -r /home/socek/projects/bael-django/requirements.txt (line 1))
Requirement already satisfied: coverage==4.4.1 in ./venv_bdjango/lib/python3.6/site-packages/coverage-4.4.1-py3.6-linux-x86_64.egg (from -r /home/socek/projects/bael-django/requirements.txt (line 2))
Requirement already satisfied: Django==1.11.2 in ./venv_bdjango/lib/python3.6/site-packages/Django-1.11.2-py3.6.egg (from -r /home/socek/projects/bael-django/requirements.txt (line 3))
Requirement already satisfied: Jinja2==2.9.6 in ./venv_bdjango/lib/python3.6/site-packages/Jinja2-2.9.6-py3.6.egg (from -r /home/socek/projects/bael-django/requirements.txt (line 4))
Requirement already satisfied: MarkupSafe==1.0 in ./venv_bdjango/lib/python3.6/site-packages/MarkupSafe-1.0-py3.6-linux-x86_64.egg (from -r /home/socek/projects/bael-django/requirements.txt (line 5))
Requirement already satisfied: mock==2.0.0 in ./venv_bdjango/lib/python3.6/site-packages/mock-2.0.0-py3.6.egg (from -r /home/socek/projects/bael-django/requirements.txt (line 6))
Requirement already satisfied: MorfDict==0.4.1 in ./venv_bdjango/lib/python3.6/site-packages/MorfDict-0.4.1-py3.6.egg (from -r /home/socek/projects/bael-django/requirements.txt (line 7))
Requirement already satisfied: pbr==3.1.1 in ./venv_bdjango/lib/python3.6/site-packages/pbr-3.1.1-py3.6.egg (from -r /home/socek/projects/bael-django/requirements.txt (line 8))
Requirement already satisfied: py==1.4.34 in ./venv_bdjango/lib/python3.6/site-packages/py-1.4.34-py3.6.egg (from -r /home/socek/projects/bael-django/requirements.txt (line 9))
Requirement already satisfied: pytest==3.1.2 in ./venv_bdjango/lib/python3.6/site-packages/pytest-3.1.2-py3.6.egg (from -r /home/socek/projects/bael-django/requirements.txt (line 10))
Requirement already satisfied: pytest-cov==2.5.1 in ./venv_bdjango/lib/python3.6/site-packages/pytest_cov-2.5.1-py3.6.egg (from -r /home/socek/projects/bael-django/requirements.txt (line 11))
Requirement already satisfied: pytz==2017.2 in ./venv_bdjango/lib/python3.6/site-packages/pytz-2017.2-py3.6.egg (from -r /home/socek/projects/bael-django/requirements.txt (line 12))
Requirement already satisfied: PyYAML==3.12 in ./venv_bdjango/lib/python3.6/site-packages/PyYAML-3.12-py3.6-linux-x86_64.egg (from -r /home/socek/projects/bael-django/requirements.txt (line 13))
Requirement already satisfied: six==1.10.0 in ./venv_bdjango/lib/python3.6/site-packages/six-1.10.0-py3.6.egg (from -r /home/socek/projects/bael-django/requirements.txt (line 14))
Requirement already satisfied: setuptools in ./venv_bdjango/lib/python3.6/site-packages (from pytest==3.1.2->-r /home/socek/projects/bael-django/requirements.txt (line 10))
(venv_bdjango) $ touch setup.py
(venv_bdjango) $ bael -t bdjango.cmd:update_requirements
* INFO bdjango.tasks.SetupPyDevelop: Running *
running develop
running egg_info
writing bdjango.egg-info/PKG-INFO
writing dependency_links to bdjango.egg-info/dependency_links.txt
writing requirements to bdjango.egg-info/requires.txt
writing top-level names to bdjango.egg-info/top_level.txt
reading manifest file 'bdjango.egg-info/SOURCES.txt'
writing manifest file 'bdjango.egg-info/SOURCES.txt'
running build_ext
Creating /home/socek/projects/bael-django/venv_bdjango/lib/python3.6/site-packages/bdjango.egg-link (link to .)
bdjango 0.0.0 is already the active version in easy-install.pth
(...)
5.1.5 Runserver¶
Main purpose of the Baelfire in our sample project is to start runserver with all the dependencies. For now, we have implemented dependency of updating packages to proper version. It is enough to implement a task for starting the developer’s server.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | from os import mkdir
from baelfire.dependencies import AlwaysTrue
from baelfire.dependencies import FileChanged
from baelfire.dependencies import TaskRebuilded
from baelfire.task import FileTask
from baelfire.task import SubprocessTask
from baelfire.task import Task
class CreateFlagsFolder(FileTask):
output_name = 'flags'
def build(self):
mkdir(self.output)
class BaseRequirements(SubprocessTask, FileTask):
def create_dependecies(self):
super(BaseRequirements, self).create_dependecies()
self.build_if(TaskRebuilded(CreateFlagsFolder()))
def _update_flag(self):
open(self.output, 'w').close()
class SetupPyDevelop(BaseRequirements):
output_name = 'flags:setuppy'
def create_dependecies(self):
super(SetupPyDevelop, self).create_dependecies()
self.build_if(FileChanged('setuppy'))
def build(self):
self.popen(
'{python} {setuppy} develop'.format(
python=self.paths.get('exe:python'),
setuppy=self.paths.get('setuppy')))
self._update_flag()
class UpdateRequirementsProduction(BaseRequirements):
output_name = 'flags:requirements'
def create_dependecies(self):
super(UpdateRequirementsProduction, self).create_dependecies()
self.build_if(FileChanged('requirementst_production'))
def build(self):
self.popen(
'{pip} install -r {file}'.format(
pip=self.paths.get('exe:pip'),
file=self.paths.get('requirementst_production')))
self._update_flag()
class UpdateRequirements(Task):
def create_dependecies(self):
self.run_before(SetupPyDevelop())
self.run_before(UpdateRequirementsProduction())
def build(self):
pass
class StartRunserver(SubprocessTask):
def create_dependecies(self):
self.run_before(UpdateRequirements())
self.build_if(AlwaysTrue())
def build(self):
self.popen(
'{python} {manage} runserver'.format(
python=self.paths.get('exe:python'),
manage=self.paths.get('manage')))
|
The StartRunserver task has been implemented. In line 75 we have added AlwaysTrue dependency which will result in
rebuilding this task every time it will be started no matter the dependency checking.
Now we need to implement endpoint for starting new task.
1 2 3 4 5 6 7 8 9 10 11 | from bdjango.core import BdCore
from bdjango.tasks import StartRunserver
from bdjango.tasks import UpdateRequirements
def update_requirements():
return UpdateRequirements(BdCore())
def start_runserver():
return StartRunserver(BdCore())
|
No new settings needs to be done, so we will just test this run.
(venv_bdjango) $ bael -t bdjango.cmd:start_runserver
* INFO bdjango.tasks.StartRunserver: Running *
Performing system checks...
System check identified no issues (0 silenced).
You have 13 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
July 03, 2017 - 11:00:12
Django version 1.11.2, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
(CTRL +C -> ...)
(venv_bdjango) $ touch setup.py
bael -t bdjango.cmd:start_runserver
* INFO bdjango.tasks.SetupPyDevelop: Running *
running develop
running egg_info
writing bdjango.egg-info/PKG-INFO
writing dependency_links to bdjango.egg-info/dependency_links.txt
writing requirements to bdjango.egg-info/requires.txt
writing top-level names to bdjango.egg-info/top_level.txt
reading manifest file 'bdjango.egg-info/SOURCES.txt'
writing manifest file 'bdjango.egg-info/SOURCES.txt'
running build_ext
Creating /home/socek/projects/bael-django/venv_bdjango/lib/python3.6/site-packages/bdjango.egg-link (link to .)
bdjango 0.0.0 is already the active version in easy-install.pth
(...)
* INFO bdjango.tasks.StartRunserver: Running *
Performing system checks...
System check identified no issues (0 silenced).
You have 13 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
July 03, 2017 - 11:00:21
Django version 1.11.2, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
As you can see on the listing above, running the runserver command will start reinstall requiretments before starting the server application.
5.1.6 Migrations¶
You have probably noticed that runserver is yelling at us (color of the message is red, which means yelling!), that we did not made migrations.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | from os import mkdir
from baelfire.dependencies import AlwaysTrue
from baelfire.dependencies import FileChanged
from baelfire.dependencies import TaskRebuilded
from baelfire.task import FileTask
from baelfire.task import SubprocessTask
from baelfire.task import Task
class CreateFlagsFolder(FileTask):
output_name = 'flags'
def build(self):
mkdir(self.output)
class BaseRequirements(SubprocessTask, FileTask):
def create_dependecies(self):
super(BaseRequirements, self).create_dependecies()
self.build_if(TaskRebuilded(CreateFlagsFolder()))
def _update_flag(self):
open(self.output, 'w').close()
class SetupPyDevelop(BaseRequirements):
output_name = 'flags:setuppy'
def create_dependecies(self):
super(SetupPyDevelop, self).create_dependecies()
self.build_if(FileChanged('setuppy'))
def build(self):
self.popen(
'{python} {setuppy} develop'.format(
python=self.paths.get('exe:python'),
setuppy=self.paths.get('setuppy')))
self._update_flag()
class UpdateRequirementsProduction(BaseRequirements):
output_name = 'flags:requirements'
def create_dependecies(self):
super(UpdateRequirementsProduction, self).create_dependecies()
self.build_if(FileChanged('requirementst_production'))
def build(self):
self.popen(
'{pip} install -r {file}'.format(
pip=self.paths.get('exe:pip'),
file=self.paths.get('requirementst_production')))
self._update_flag()
class UpdateRequirements(Task):
def create_dependecies(self):
self.run_before(SetupPyDevelop())
self.run_before(UpdateRequirementsProduction())
def build(self):
pass
class BaseManagePy(SubprocessTask):
def create_dependecies(self):
self.run_before(UpdateRequirements())
def _manage(self, command):
self.popen(
'{python} {manage} {command}'.format(
python=self.paths.get('exe:python'),
manage=self.paths.get('manage'),
command=command))
class ApplyMigrations(BaseManagePy):
def create_dependecies(self):
super(ApplyMigrations, self).create_dependecies()
self.build_if(AlwaysTrue())
def build(self):
self._manage('migrate')
class StartRunserver(BaseManagePy):
def create_dependecies(self):
super(StartRunserver, self).create_dependecies()
self.run_before(ApplyMigrations())
self.build_if(AlwaysTrue())
def build(self):
self._manage('runserver')
|
Now we have 2 tasks which needs to use manage.py, so I made base class BaseManagePy with default dependency,
which make sure that the requiretments will be updated before running manage.py command. At this point we may want
to add AlwaysTrue dependency here, but it will be a design flaw. In the future, you may want to use manage.py
command which does not need to be rebuild every time it will be started.
(venv_bdjango) $ bael -t bdjango.cmd:start_runserver
* INFO bdjango.tasks.ApplyMigrations: Running *
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying sessions.0001_initial... OK
* INFO bdjango.tasks.StartRunserver: Running *
Performing system checks...
System check identified no issues (0 silenced).
July 03, 2017 - 13:00:18
Django version 1.11.2, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Now, every time we will start runserver, we will run migrations before.
As you can see, we are using UpdateRequirements in two places, but the Baelfire does not complain and run it only once.
5.1.7 Custom dependency - migration¶
It is a good idea to rebuild migrations everytime we start the runserver, but it is a better idea to rebuild this only
when new migrations will be avalible. The task should search for newly created migration
scripts and then rebuild. If at this point you want to add some ifs in the build method, then stop it
please and read the whole documentation again. We will make custom dependency instead. This new dependency will search
thru all migration scripts and find the newest one. Then it will compare mtime of the file with the mtime of flag file.
This is how we will know if the migrations has been applied without connecting to the database. This algorythm is not
covering all of the situation that can happend, but for the sake of the example, let’s just say it is enough.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | from os import walk
from os.path import getmtime
from os.path import join
from baelfire.dependencies import FileChanged
class MigrationsChanged(FileChanged):
migrations_dirname = 'migrations'
def _map_mtime(self, root):
"""
convert filename into full path with mtime
"""
def make(file):
path = join(root, file)
mtime = getmtime(path)
return path, mtime
return make
@property
def path(self):
# list of files to check
checkfiles = []
for root, dirs, files in walk(self.paths.get('src')):
# check only paths ending with "migrations"
if root.endswith(self.migrations_dirname):
# filter for only '.py' files
files = filter(lambda file: file.endswith('.py'), files)
checkfiles += list(map(self._map_mtime(root), files))
# get the newest file, and return its full path, so the output_file will be compared only to one file
return max(checkfiles, key=lambda obj: obj[1])[0]
|
The dependency is ready, so we can change our tasks.py and core.py.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | from os import mkdir
from baelfire.dependencies import AlwaysTrue
from baelfire.dependencies import FileChanged
from baelfire.dependencies import FileDoesNotExists
from baelfire.dependencies import TaskRebuilded
from baelfire.task import FileTask
from baelfire.task import SubprocessTask
from baelfire.task import Task
from bdjango.dependency import MigrationsChanged
class CreateFlagsFolder(FileTask):
output_name = 'flags'
def build(self):
mkdir(self.output)
class BaseRequirements(SubprocessTask, FileTask):
def create_dependecies(self):
super(BaseRequirements, self).create_dependecies()
self.build_if(TaskRebuilded(CreateFlagsFolder()))
def _update_flag(self):
open(self.output, 'w').close()
class SetupPyDevelop(BaseRequirements):
output_name = 'flags:setuppy'
def create_dependecies(self):
super(SetupPyDevelop, self).create_dependecies()
self.build_if(FileChanged('setuppy'))
def build(self):
self.popen(
'{python} {setuppy} develop'.format(
python=self.paths.get('exe:python'),
setuppy=self.paths.get('setuppy')))
self._update_flag()
class UpdateRequirementsProduction(BaseRequirements):
output_name = 'flags:requirements'
def create_dependecies(self):
super(UpdateRequirementsProduction, self).create_dependecies()
self.build_if(FileChanged('requirementst_production'))
def build(self):
self.popen(
'{pip} install -r {file}'.format(
pip=self.paths.get('exe:pip'),
file=self.paths.get('requirementst_production')))
self._update_flag()
class UpdateRequirements(Task):
def create_dependecies(self):
self.run_before(SetupPyDevelop())
self.run_before(UpdateRequirementsProduction())
def build(self):
pass
class BaseManagePy(SubprocessTask):
def create_dependecies(self):
self.run_before(UpdateRequirements())
def _manage(self, command):
self.popen(
'{python} {manage} {command}'.format(
python=self.paths.get('exe:python'),
manage=self.paths.get('manage'),
command=command))
class ApplyMigrations(FileTask, BaseManagePy):
output_name = 'flags:migrations'
def create_dependecies(self):
super(ApplyMigrations, self).create_dependecies()
self.build_if(FileDoesNotExists(self.output_name))
self.build_if(MigrationsChanged())
def build(self):
self._manage('migrate')
open(self.output, 'w').close()
class StartRunserver(BaseManagePy):
def create_dependecies(self):
super(StartRunserver, self).create_dependecies()
self.run_before(ApplyMigrations())
self.build_if(AlwaysTrue())
def build(self):
self._manage('runserver')
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | from os.path import dirname
from baelfire.core import Core
class BdCore(Core):
def phase_settings(self):
super(BdCore, self).phase_settings()
self.paths.set('project', self.get_project_dir(), is_root=True)
self.paths.set('venv', 'venv_bdjango', parent='project')
self.paths.set('venv:bin', 'bin', parent='venv')
self.paths.set('exe:python', 'python', parent='venv:bin')
self.paths.set('exe:pip', 'pip', parent='venv:bin')
self.paths.set('setuppy', 'setup.py', parent='project')
self.paths.set('requirementst_production', 'requirements.txt', parent='project')
self.paths.set('bdjango', 'bdjango', parent='project')
self.paths.set('flags', 'flags', parent='bdjango')
self.paths.set('flags:requirements', 'req.flag', parent='flags')
self.paths.set('flags:setuppy', 'setuppy.flag', parent='flags')
self.paths.set('flags:migrations', 'migrations.flag', parent='flags')
self.paths.set('src', 'mysite', parent='project')
self.paths.set('manage', 'manage.py', parent='src')
def get_project_dir(self):
project_dir = __file__
for index in range(2):
project_dir = dirname(project_dir)
return project_dir
|
Now everytime we will start our runserver the migration will be started when needed.
(venv_bdjango) $ bael -t bdjango.cmd:start_runserver
* INFO bdjango.tasks.ApplyMigrations: Running *
Operations to perform:
Apply all migrations: admin, auth, contenttypes, hello, sessions
Running migrations:
No migrations to apply.
* INFO bdjango.tasks.StartRunserver: Running *
Performing system checks...
System check identified no issues (0 silenced).
July 05, 2017 - 06:58:09
Django version 1.11.2, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
(...)
(venv_bdjango) $ bael -t bdjango.cmd:start_runserver
* INFO bdjango.tasks.StartRunserver: Running *
Performing system checks...
System check identified no issues (0 silenced).
July 05, 2017 - 06:59:58
Django version 1.11.2, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
(venv_bdjango) $ python manage.py makemigrations
Migrations for 'hello':
hello/migrations/0002_hello_year.py
- Add field year to hello
(venv_bdjango) $ bael -t bdjango.cmd:start_runserver
* INFO bdjango.tasks.ApplyMigrations: Running *
Operations to perform:
Apply all migrations: admin, auth, contenttypes, hello, sessions
Running migrations:
Applying hello.0002_hello_year... OK
* INFO bdjango.tasks.StartRunserver: Running *
Performing system checks...
System check identified no issues (0 silenced).
July 05, 2017 - 07:00:28
Django version 1.11.2, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
I did not include the code of the models which created the migration scripts. The whole code is avalible on github (link above).
5.1.8 Tasks in the background - celery¶
Many projects needs to use task schedulers, which works outside of the webserver. For this tutorial we will use celery 4.0.2. For the simplicity, we don’t care if the celery is connecting to the broker.
For the celery we could use normal task, but the downside is that we would need to run this in one terminal and the
runserver in second one. In most cases we would need the celery to be run, but not to be visible. We could start the
celery in the background, but sometimes we would like to see what is happening with the celery process. I prefer to use
screen for starting the celery process and attach/detach whenever I like. For this we will use ScreenTask and
AttachScreenTask.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | from os.path import dirname
from baelfire.task.screen import ScreenCore
class BdCore(ScreenCore):
def phase_settings(self):
super(BdCore, self).phase_settings()
self.paths.set('project', self.get_project_dir(), is_root=True)
self.paths.set('venv', 'venv_bdjango', parent='project')
self.paths.set('venv:bin', 'bin', parent='venv')
self.paths.set('exe:python', 'python', parent='venv:bin')
self.paths.set('exe:pip', 'pip', parent='venv:bin')
self.paths.set('exe:celery', 'celery', parent='venv:bin')
self.paths.set('setuppy', 'setup.py', parent='project')
self.paths.set('requirementst_production', 'requirements.txt', parent='project')
self.paths.set('bdjango', 'bdjango', parent='project')
self.paths.set('flags', 'flags', parent='bdjango')
self.paths.set('flags:requirements', 'req.flag', parent='flags')
self.paths.set('flags:setuppy', 'setuppy.flag', parent='flags')
self.paths.set('flags:migrations', 'migrations.flag', parent='flags')
self.paths.set('pid:celery', 'celery.pid', parent='flags')
self.paths.set('src', 'mysite', parent='project')
self.paths.set('manage', 'manage.py', parent='src')
def get_project_dir(self):
project_dir = __file__
for index in range(2):
project_dir = dirname(project_dir)
return project_dir
|
We had to update core.py, because Screen tasks needs to have exe:screen value in paths. Also we have added path
for celery pidfile.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | from os import mkdir
from baelfire.dependencies import AlwaysTrue
from baelfire.dependencies import FileChanged
from baelfire.dependencies import FileDoesNotExists
from baelfire.dependencies import PidIsNotRunning
from baelfire.dependencies import TaskRebuilded
from baelfire.task import FileTask
from baelfire.task import SubprocessTask
from baelfire.task import Task
from baelfire.task.screen import AttachScreenTask
from baelfire.task.screen import ScreenTask
from bdjango.dependency import MigrationsChanged
class CreateFlagsFolder(FileTask):
output_name = 'flags'
def build(self):
mkdir(self.output)
class BaseRequirements(SubprocessTask, FileTask):
def create_dependecies(self):
super(BaseRequirements, self).create_dependecies()
self.build_if(TaskRebuilded(CreateFlagsFolder()))
def _update_flag(self):
open(self.output, 'w').close()
class SetupPyDevelop(BaseRequirements):
output_name = 'flags:setuppy'
def create_dependecies(self):
super(SetupPyDevelop, self).create_dependecies()
self.build_if(FileChanged('setuppy'))
def build(self):
self.popen(
'{python} {setuppy} develop'.format(
python=self.paths.get('exe:python'),
setuppy=self.paths.get('setuppy')))
self._update_flag()
class UpdateRequirementsProduction(BaseRequirements):
output_name = 'flags:requirements'
def create_dependecies(self):
super(UpdateRequirementsProduction, self).create_dependecies()
self.build_if(FileChanged('requirementst_production'))
def build(self):
self.popen(
'{pip} install -r {file}'.format(
pip=self.paths.get('exe:pip'),
file=self.paths.get('requirementst_production')))
self._update_flag()
class UpdateRequirements(Task):
def create_dependecies(self):
self.run_before(SetupPyDevelop())
self.run_before(UpdateRequirementsProduction())
def build(self):
pass
class BaseManagePy(SubprocessTask):
def create_dependecies(self):
self.run_before(UpdateRequirements())
def _manage(self, command):
self.popen(
'{python} {manage} {command}'.format(
python=self.paths.get('exe:python'),
manage=self.paths.get('manage'),
command=command))
class ApplyMigrations(FileTask, BaseManagePy):
output_name = 'flags:migrations'
def create_dependecies(self):
super(ApplyMigrations, self).create_dependecies()
self.build_if(FileDoesNotExists(self.output_name))
self.build_if(MigrationsChanged())
self.run_before(UpdateRequirements())
def build(self):
self._manage('migrate')
open(self.output, 'w').close()
class StartCelery(ScreenTask):
screen_name = 'mysite_celery'
def create_dependecies(self):
self.run_before(ApplyMigrations())
self.build_if(PidIsNotRunning(pid_file_name='pid:celery'))
def build(self):
self._screen_run(
['{celery} -A mysite worker -l info --pidfile={pidfile}'.format(
celery=self.paths.get('exe:celery'),
pidfile=self.paths.get('pid:celery'))],
cwd=self.paths.get('src'))
class AttachCelery(AttachScreenTask):
detached_task = StartCelery
class StartRunserver(BaseManagePy):
def create_dependecies(self):
super(StartRunserver, self).create_dependecies()
self.run_before(ApplyMigrations())
self.run_before(StartCelery())
self.build_if(AlwaysTrue())
def build(self):
self._manage('runserver')
|
In this file we have added StartCelery task, which will start celery worker in the background and AttachCelery
for attaching the celery worker. StartRunserver also have a new dependency which will start celery before starting
runserver.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | from bdjango.core import BdCore
from bdjango.tasks import AttachCelery
from bdjango.tasks import StartRunserver
from bdjango.tasks import UpdateRequirements
def update_requirements():
return UpdateRequirements(BdCore())
def start_runserver():
return StartRunserver(BdCore())
def attach_celery():
return AttachCelery(BdCore())
|
Here we only are adding endpoint for attaching the celery.
Starting runserver:
Attaching celery:
(venv_bdjango) $ bael -t bdjango.cmd:start_runserver
* INFO bdjango.tasks.StartCelery: Running *
* INFO bdjango.tasks.StartRunserver: Running *
Performing system checks...
System check identified no issues (0 silenced).
July 05, 2017 - 11:06:20
Django version 1.11.2, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
(...)
(venv_bdjango) $ bael -t bdjango.cmd:start_runserver
* INFO bdjango.tasks.StartRunserver: Running *
Performing system checks...
System check identified no issues (0 silenced).
July 05, 2017 - 11:06:20
Django version 1.11.2, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
(...)
# attaching to the celery
(venv_bdjango) $ bael -t bdjango.cmd:attach_celery
-------------- celery@gringo v4.0.2 (latentcall)
---- **** -----
--- * *** * -- Linux-4.11.7-1-ARCH-x86_64-with-arch 2017-07-05 11:07:20
-- * - **** ---
- ** ---------- [config]
- ** ---------- .> app: mysite:0x7fa4b1700d68
- ** ---------- .> transport: amqp://guest:**@localhost:5672//
- ** ---------- .> results: disabled://
- *** --- * --- .> concurrency: 8 (prefork)
-- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
--- ***** -----
-------------- [queues]
.> celery exchange=celery(direct) key=celery
[tasks]
. mysite.celery.debug_task
5.1.9 Finally - your own command line¶
It is not very comfortable to use bael command for all this tasks. But it is very simple to make your own command
line tool. You can just inherite from baelfire.application.application.Application and make your own task names for
-t switch.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | from baelfire.application.application import Application
from bdjango.core import BdCore
from bdjango.tasks import AttachCelery
from bdjango.tasks import StartRunserver
from bdjango.tasks import UpdateRequirements
class BdApplication(Application):
tasks = {
'update': UpdateRequirements,
'runserver': StartRunserver,
'celery': AttachCelery,
}
def get_task(self, name):
task = self.tasks[name]
return task(BdCore())
def run():
BdApplication().run()
|
Also you need to update setup.py file and run python setup.py develop in order to have new command line tool.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | # -*- encoding: utf-8 -*-
from setuptools import find_packages
from setuptools import setup
install_requires = [
'baelfire==0.5.1',
'Django==1.11.2',
'celery==4.0.2',
]
if __name__ == '__main__':
setup(
name='bdjango',
packages=find_packages(),
install_requires=install_requires,
entry_points={
'console_scripts': [
'bdcmd=bdjango.cmd:run',
]
},
)
|
Finally, the quick and simple run:
(venv_bdjango) $ bdcmd -t runserver
* INFO bdjango.tasks.StartRunserver: Running *
Performing system checks...
System check identified no issues (0 silenced).
July 05, 2017 - 12:30:05
Django version 1.11.2, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.