Skip to content

Commit

Permalink
Simplify the User database model by dropping unused columns
Browse files Browse the repository at this point in the history
The following columns are dropped:

 * `password`
 * `date_joined`
 * `groups`
 * `is_active`
 * `is_staff`
 * `is_superuser`
 * `last_login`
 * `user_permissions`

These columns were originally added because the base user class of the
Django framework was used, which provided the ORM layer. However, these
attributes are not used at all and Django does not require them in order
to work properly. Therefore, we remove them from the models for both
backends.

Note that in principle, the `contrib` and `auth` applications currently
installed for Django, could also be removed without affecting the
functionality of `aiida-core` since it no longer requires the built in
authentication application of Django. However, since the initial
migration schema still references it, we cannot yet remove it fully.
If we were to reset the database migration schema, we could get rid of
these unnecessary dependencies.
  • Loading branch information
sphuber committed May 29, 2019
1 parent cab1762 commit 6588683
Show file tree
Hide file tree
Showing 24 changed files with 200 additions and 373 deletions.
4 changes: 2 additions & 2 deletions .ci/setup_profiles.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ then

# Setup the main profile
verdi setup --profile $TEST_AIIDA_BACKEND \
--email="aiida@localhost" --first-name=AiiDA --last-name=test --institution="AiiDA Team" --password 'secret' \
--email="aiida@localhost" --first-name=AiiDA --last-name=test --institution="AiiDA Team" \
--db-engine 'postgresql_psycopg2' --db-backend=$TEST_AIIDA_BACKEND --db-host="localhost" --db-port=5432 \
--db-name="$TEST_AIIDA_BACKEND" --db-username=postgres --db-password='' \
--repository="/tmp/repository_${TEST_AIIDA_BACKEND}/" --non-interactive

# Setup the test profile
verdi setup --profile test_$TEST_AIIDA_BACKEND \
--email="aiida@localhost" --first-name=AiiDA --last-name=test --institution="AiiDA Team" --password 'secret' \
--email="aiida@localhost" --first-name=AiiDA --last-name=test --institution="AiiDA Team" \
--db-engine 'postgresql_psycopg2' --db-backend=$TEST_AIIDA_BACKEND --db-host="localhost" --db-port=5432 \
--db-name="test_$TEST_AIIDA_BACKEND" --db-username=postgres --db-password='' \
--repository="/tmp/test_repository_test_${TEST_AIIDA_BACKEND}/" --non-interactive
Expand Down
14 changes: 7 additions & 7 deletions aiida/backends/djsite/db/migrations/0001_initial.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
from django.db import models, migrations
import django.db.models.deletion
import django.utils.timezone
from django.conf import settings

from aiida.backends.djsite.db.migrations import upgrade_schema_version

Expand All @@ -25,6 +24,7 @@


class Migration(migrations.Migration):

dependencies = [
('auth', '0001_initial'),
]
Expand Down Expand Up @@ -89,7 +89,7 @@ class Migration(migrations.Migration):
('auth_params', models.TextField(default=u'{}')),
('metadata', models.TextField(default=u'{}')),
('enabled', models.BooleanField(default=True)),
('aiidauser', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
('aiidauser', models.ForeignKey(to='db.DbUser')),
],
options={
},
Expand Down Expand Up @@ -291,7 +291,7 @@ class Migration(migrations.Migration):
('module_class', models.TextField()),
('script_path', models.TextField()),
('script_md5', models.CharField(max_length=255)),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=django.db.models.deletion.PROTECT)),
('user', models.ForeignKey(to='db.DbUser', on_delete=django.db.models.deletion.PROTECT)),
],
options={
},
Expand Down Expand Up @@ -326,7 +326,7 @@ class Migration(migrations.Migration):
('calculations', models.ManyToManyField(related_name='workflow_step', to='db.DbNode')),
('parent', models.ForeignKey(related_name='steps', to='db.DbWorkflow')),
('sub_workflows', models.ManyToManyField(related_name='parent_workflow_step', to='db.DbWorkflow')),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=django.db.models.deletion.PROTECT)),
('user', models.ForeignKey(to='db.DbUser', on_delete=django.db.models.deletion.PROTECT)),
],
options={
},
Expand Down Expand Up @@ -367,7 +367,7 @@ class Migration(migrations.Migration):
model_name='dbnode',
name='user',
field=models.ForeignKey(related_name='dbnodes', on_delete=django.db.models.deletion.PROTECT,
to=settings.AUTH_USER_MODEL),
to='db.DbUser'),
preserve_default=True,
),
migrations.AddField(
Expand Down Expand Up @@ -396,7 +396,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='dbgroup',
name='user',
field=models.ForeignKey(related_name='dbgroups', to=settings.AUTH_USER_MODEL),
field=models.ForeignKey(related_name='dbgroups', to='db.DbUser'),
preserve_default=True,
),
migrations.AlterUniqueTogether(
Expand All @@ -422,7 +422,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='dbcomment',
name='user',
field=models.ForeignKey(to=settings.AUTH_USER_MODEL),
field=models.ForeignKey(to='db.DbUser'),
preserve_default=True,
),
migrations.AddField(
Expand Down
68 changes: 68 additions & 0 deletions aiida/backends/djsite/db/migrations/0035_simplify_user_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# -*- coding: utf-8 -*-
###########################################################################
# Copyright (c), The AiiDA team. All rights reserved. #
# This file is part of the AiiDA code. #
# #
# The code is hosted on GitHub at https://github.com/aiidateam/aiida_core #
# For further information on the license, see the LICENSE.txt file #
# For further information please visit http://www.aiida.net #
###########################################################################
# pylint: disable=invalid-name,too-few-public-methods
"""Simplify the `DbUser` model."""
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
from __future__ import absolute_import

# Remove when https://github.com/PyCQA/pylint/issues/1931 is fixed
# pylint: disable=no-name-in-module,import-error,no-member
from django.db import migrations

from aiida.backends.djsite.db.migrations import upgrade_schema_version

REVISION = '1.0.35'
DOWN_REVISION = '1.0.34'


class Migration(migrations.Migration):
"""Simplify the `DbUser` model by dropping unused columns."""

dependencies = [
('db', '0034_drop_node_columns_nodeversion_public'),
]

operations = [
migrations.RemoveField(
model_name='dbuser',
name='password',
),
migrations.RemoveField(
model_name='dbuser',
name='date_joined',
),
migrations.RemoveField(
model_name='dbuser',
name='groups',
),
migrations.RemoveField(
model_name='dbuser',
name='is_active',
),
migrations.RemoveField(
model_name='dbuser',
name='is_staff',
),
migrations.RemoveField(
model_name='dbuser',
name='is_superuser',
),
migrations.RemoveField(
model_name='dbuser',
name='last_login',
),
migrations.RemoveField(
model_name='dbuser',
name='user_permissions',
),
upgrade_schema_version(REVISION, DOWN_REVISION)
]
2 changes: 1 addition & 1 deletion aiida/backends/djsite/db/migrations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class DeserializationException(AiidaException):
pass


LATEST_MIGRATION = '0034_drop_node_columns_nodeversion_public'
LATEST_MIGRATION = '0035_simplify_user_model'


def _update_schema_version(version, apps, schema_editor):
Expand Down
67 changes: 15 additions & 52 deletions aiida/backends/djsite/db/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,16 @@

import contextlib
import six
from six.moves import zip, range
from six.moves import range
from django.db import models as m
from django.contrib.auth.models import (
AbstractBaseUser, BaseUserManager, PermissionsMixin)
from django.contrib.postgres.fields import JSONField
from django.utils.encoding import python_2_unicode_compatible
from django.core.exceptions import ObjectDoesNotExist
from django.db.models.query import QuerySet

from aiida.common import timezone
from aiida.common.utils import get_new_uuid
from aiida.common.exceptions import (ConfigurationError, DbContentError)
from aiida.backends.djsite.settings import AUTH_USER_MODEL
from aiida.common.exceptions import DbContentError
import aiida.backends.djsite.db.migrations as migrations
from aiida.backends.utils import AIIDA_ATTRIBUTE_SEP

Expand Down Expand Up @@ -70,55 +67,21 @@ def get_queryset(self):
return AiidaQuerySet(self.model, using=self._db)


class DbUserManager(BaseUserManager):
def create_user(self, email, password=None, **extra_fields):
"""
Creates and saves a User with the given email (that is the
username) and password.
"""
now = timezone.now()
if not email:
raise ValueError('The given email must be set')
email = BaseUserManager.normalize_email(email)
user = self.model(email=email,
is_staff=False, is_active=True, is_superuser=False,
last_login=now, date_joined=now, **extra_fields)

user.set_password(password)
user.save(using=self._db)
return user

def create_superuser(self, email, password, **extra_fields):
u = self.create_user(email, password, **extra_fields)
u.is_staff = True
u.is_active = True
u.is_superuser = True
u.save(using=self._db)
return u


class DbUser(AbstractBaseUser, PermissionsMixin):
"""
This class replaces the default User class of Django
"""
class DbUser(m.Model):
"""Class that represents a user as the owner of a specific Node."""

is_anonymous = False
is_authenticated = True

USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ()

# Set unique email field
email = m.EmailField(unique=True, db_index=True)
first_name = m.CharField(max_length=254, blank=True)
last_name = m.CharField(max_length=254, blank=True)
institution = m.CharField(max_length=254, blank=True)

is_staff = m.BooleanField(default=False,
help_text='Designates whether the user can log into this admin '
'site.')
is_active = m.BooleanField(default=True,
help_text='Designates whether this user should be treated as '
'active. Unselect this instead of deleting accounts.')
date_joined = m.DateTimeField(default=timezone.now)

USERNAME_FIELD = 'email'

objects = DbUserManager()


@python_2_unicode_compatible
class DbNode(m.Model):
Expand Down Expand Up @@ -156,7 +119,7 @@ class DbNode(m.Model):
ctime = m.DateTimeField(default=timezone.now, db_index=True, editable=False)
mtime = m.DateTimeField(auto_now=True, db_index=True, editable=False)
# Cannot delete a user if something is associated to it
user = m.ForeignKey(AUTH_USER_MODEL, on_delete=m.PROTECT, related_name='dbnodes')
user = m.ForeignKey(DbUser, on_delete=m.PROTECT, related_name='dbnodes')

# Direct links
outputs = m.ManyToManyField('self', symmetrical=False,
Expand Down Expand Up @@ -1243,7 +1206,7 @@ class DbGroup(m.Model):
# The owner of the group, not of the calculations
# On user deletion, remove his/her groups too (not the calcuations, only
# the groups
user = m.ForeignKey(AUTH_USER_MODEL, on_delete=m.CASCADE,
user = m.ForeignKey(DbUser, on_delete=m.CASCADE,
related_name='dbgroups')

class Meta:
Expand Down Expand Up @@ -1307,7 +1270,7 @@ class DbAuthInfo(m.Model):
information.
"""
# Delete the DbAuthInfo if either the user or the computer are removed
aiidauser = m.ForeignKey(AUTH_USER_MODEL, on_delete=m.CASCADE)
aiidauser = m.ForeignKey(DbUser, on_delete=m.CASCADE)
dbcomputer = m.ForeignKey(DbComputer, on_delete=m.CASCADE)
auth_params = JSONField(default=dict) # contains mainly the remoteuser and the private_key

Expand Down Expand Up @@ -1336,7 +1299,7 @@ class DbComment(m.Model):
ctime = m.DateTimeField(default=timezone.now, editable=False)
mtime = m.DateTimeField(auto_now=True, editable=False)
# Delete the comments of a deleted user (TODO: check if this is a good policy)
user = m.ForeignKey(AUTH_USER_MODEL, on_delete=m.CASCADE)
user = m.ForeignKey(DbUser, on_delete=m.CASCADE)
content = m.TextField(blank=True)

def __str__(self):
Expand Down
43 changes: 21 additions & 22 deletions aiida/backends/djsite/manage.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,32 @@
from __future__ import division
from __future__ import print_function
from __future__ import absolute_import
import sys

import click

if __name__ == "__main__":
from django.core.management import execute_from_command_line

# Copy sys.argv
actual_argv = sys.argv[:]
from aiida.cmdline.params import options

# Check if there is also a cmdline option is --aiida-profile=PROFILENAME
try:
first_cmdline_option = sys.argv[1]
except IndexError:
first_cmdline_option = None

profile_name = None # Use the default profile if not specified
if first_cmdline_option is not None:
cmdprefix = "--aiida-profile="
if first_cmdline_option.startswith(cmdprefix):
profile_name = first_cmdline_option[len(cmdprefix):]
# I remove the argument I just read
actual_argv = [actual_argv[0]] + actual_argv[2:]
@click.command()
@options.PROFILE(required=True)
@click.argument('command', nargs=-1)
def main(profile, command):
"""Simple wrapper around the Django command line tool that first loads an AiiDA profile."""
from django.core.management import execute_from_command_line

# Load the profile
# Load the general load_dbenv.
from aiida.manage.configuration import load_profile
from aiida.manage.manager import get_manager
load_profile(profile_name)
get_manager().get_backend()

execute_from_command_line(actual_argv)
load_profile(profile=profile.name)
manager = get_manager()
manager._load_backend(schema_check=False)

# The `execute_from_command` expects a list of command line arguments where the first is the program name that one
# would normally call directly. Since this is now replaced by our `click` command we just spoof a random name.
argv = ['basename'] + list(command)
execute_from_command_line(argv)


if __name__ == '__main__':
main()
4 changes: 0 additions & 4 deletions aiida/backends/djsite/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
'debug':
Expand All @@ -113,8 +112,5 @@
INSTALLED_APPS = [
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'aiida.backends.djsite.db',
]
Loading

0 comments on commit 6588683

Please sign in to comment.