0

Running a small db on pythonanywhere, and am trying to set up a scheduled .backup of my sqlite3 database. Is there any way in the command line to add a time/date stamp to the filename, so that it doesn't overwrite the previous days backup?

Here's the code I'm using, if it matters:

sqlite3 db.sqlite3
.backup dbbackup.sqlite3
.quit

Running every 24 hours. The previous day's backup gets overwritten, though. I'd love to just be able to save it as dbbackup.timestamp.sqlite3 or something, so I could have multiple backups available.

Thanks!

Josh
  • 280
  • 2
  • 13

1 Answers1

0

I suggest you to handle this case with management commands and cronjob.

This an example how to do it; save this file eg in yourapp/management/commands/dbackup.py, don't forget to add __init__.py files.

yourapp/management/__init__.py
yourapp/management/commands/__init__.py
yourapp/management/commands/dbackup.py

But, previously add these lines below to your settings.py

USERNAME_SUPERUSER = 'yourname`
PASSWORD_SUPERUSER = `yourpassword`
EMAIL_SUPERUSER = `youremail@domain.com`
DATABASE_NAME = 'db.sqlite3'

The important tree path project if you deploying at pythonanywhere;

/home/yourusername/yourproject/manage.py
/home/yourusername/yourproject/db.sqlite3
/home/yourusername/yourproject/yourproject/settings.py
/home/yourusername/yourproject/yourapp/management/commands/dbackup.py

Add these script below into yourapp/management/commands/dbackup.py, you also can custom this script as you need.

import os
import time
from django.conf import settings
from django.contrib.auth.models import User
from django.core.management.base import (BaseCommand, CommandError)

USERNAME_SUPERUSER = settings.USERNAME_SUPERUSER
PASSWORD_SUPERUSER = settings.PASSWORD_SUPERUSER
EMAIL_SUPERUSER = settings.EMAIL_SUPERUSER
DATABASE_NAME = settings.DATABASE_NAME #eg: 'db.sqlite3'


class Command(BaseCommand):
    help = ('Command to deploy and backup the latest database.')

    def add_arguments(self, parser):
        parser.add_argument(
            '-b', '--backup', action='store_true',
            help='Just backup command confirmation.'
        )

    def success_info(self, info):
        return self.stdout.write(self.style.SUCCESS(info))

    def error_info(self, info):
        return self.stdout.write(self.style.ERROR(info))

    def handle(self, *args, **options):
        backup = options['backup']

        if backup == False:
            return self.print_help()

        # Removing media files, if you need to remove all media files
        # os.system('rm -rf media/images/')
        # self.success_info("[+] Removed media files at `media/images/`")

        # Removing database `db.sqlite3`
        if os.path.isfile(DATABASE_NAME):
            # backup the latest database, eg to: `db.2017-02-03.sqlite3`
            backup_database = 'db.%s.sqlite3' % time.strftime('%Y-%m-%d')
            os.rename(DATABASE_NAME, backup_database)
            self.success_info("[+] Backup the database `%s` to %s" % (DATABASE_NAME, backup_database))

            # remove the latest database
            os.remove(DATABASE_NAME)
            self.success_info("[+] Removed %s" % DATABASE_NAME)

        # Removing all files migrations for `yourapp`
        def remove_migrations(path):
            exclude_files = ['__init__.py', '.gitignore']
            path = os.path.join(settings.BASE_DIR, path)

            filelist = [
                f for f in os.listdir(path)
                if f.endswith('.py')
                and f not in exclude_files
            ]
            for f in filelist:
                os.remove(path + f)
            self.success_info('[+] Removed files migrations for {}'.format(path))

        # do remove all files migrations
        remove_migrations('yourapp/migrations/')

        # Removing all `.pyc` files
        os.system('find . -name *.pyc -delete')
        self.success_info('[+] Removed all *.pyc files.')

        # Creating database migrations
        # These commands should re-generate the new database, eg: `db.sqlite3`
        os.system('python manage.py makemigrations')
        os.system('python manage.py migrate')
        self.success_info('[+] Created database migrations.')

        # Creating a superuser
        user = User.objects.create_superuser(
            username=USERNAME_SUPERUSER,
            password=PASSWORD_SUPERUSER,
            email=EMAIL_SUPERUSER
        )
        user.save()
        self.success_info('[+] Created a superuser for `{}`'.format(USERNAME_SUPERUSER))

Setup this command with crontab

$ sudo crontab -e

And add these following below lines;

# [minute] [hour] [date] [month] [year]
59 23 * * * source ~/path/to/yourenv/bin/activate && cd ~/path/to/yourenv/yourproject/ && ./manage.py dbackup -b

But, if you need to deploy at pythonanywhere, you just need to add these..

Daily at [hour] : [minute] UTC, ... fill the hour=23 and minute=59

source /home/yourusername/.virtualenvs/yourenv/bin/activate && cd /home/yourusername/yourproject/ && ./manage.py dbackup -b

Update 1

I suggest you to update the commands to execute the file of manage.py such as os.system('python manage.py makemigrations') with function of call_command;

from django.core.management import call_command
call_command('collectstatic', verbosity=3, interactive=False)
call_command('migrate', 'myapp', verbosity=3, interactive=False)

...is equal to the following commands typed in terminal:

$ ./manage.py collectstatic --noinput -v 3
$ ./manage.py migrate myapp --noinput -v 3

See running management commands from django docs.

Update 2

Previous condition is if you need to re-deploy your project and using a fresh database. But, if you only want to backup it by renaming the database, you can using module of shutil.copyfile

import os
import time
import shutil
from django.conf import settings
from django.core.management.base import (BaseCommand, CommandError)

DATABASE_NAME = settings.DATABASE_NAME #eg: 'db.sqlite3'


class Command(BaseCommand):
    help = ('Command to deploy and backup the latest database.')

    def add_arguments(self, parser):
        parser.add_argument(
            '-b', '--backup', action='store_true',
            help='Just backup command confirmation.'
        )

    def success_info(self, info):
        return self.stdout.write(self.style.SUCCESS(info))

    def error_info(self, info):
        return self.stdout.write(self.style.ERROR(info))

    def handle(self, *args, **options):
        backup = options['backup']

        if backup == False:
            return self.print_help()

        if os.path.isfile(DATABASE_NAME):
            # backup the latest database, eg to: `db.2017-02-29.sqlite3`
            backup_database = 'db.%s.sqlite3' % time.strftime('%Y-%m-%d')
            shutil.copyfile(DATABASE_NAME, backup_database)
            self.success_info("[+] Backup the database `%s` to %s" % (DATABASE_NAME, backup_database))
binpy
  • 3,994
  • 3
  • 17
  • 54
  • Thanks! Will try this out and report back. – Josh Feb 03 '17 at 00:56
  • The code seems to work for renaming the db.sqlite3 to the db.currentdate.sqlite3, but then it does not restore the original db.sqlite3. Did I miss something? – Josh Feb 04 '17 at 01:12
  • simply way, you can use module `shutil.copyfile(src, dst)`, related answers: http://stackoverflow.com/a/123212/6396981 – binpy Feb 04 '17 at 02:33
  • That worked! Couple small changes (still needed from django.conf import settings in header, and there was an extra " ' " on line 29), but otherwise works perfectly. Thank you!! – Josh Feb 04 '17 at 03:13
  • oh, I see.. I'll update it.. sorry about missing... you're welcome.. :) – binpy Feb 04 '17 at 04:30