为什么在使用django-allauth的初始迁移过程中出现错误?

时间:2019-02-05 01:19:31

标签: sql-server django django-allauth

基于django-allauth定义的模型,在初始数据库迁移期间出现错误。我没有定义任何其他模型,因此我的Django项目中没有迁移文件。

Django==2.1.5  
django-allauth==0.38.0  
django-pyodbc-azure==2.1.0.0  
Python 3.6.7  
MS SQL Server 16 Build Version 13.0.4474.0 (March 2018)  

当我使用SQLite时,此方法工作正常

> python manage.py showmigrations

account  
 [ ] 0001_initial  
 [ ] 0002_email_max_length  
admin  
 [ ] 0001_initial  
 [ ] 0002_logentry_remove_auto_add  
 [ ] 0003_logentry_add_action_flag_choices  
auth  
 [ ] 0001_initial  
 [ ] 0002_alter_permission_name_max_length  
 [ ] 0003_alter_user_email_max_length  
 [ ] 0004_alter_user_username_opts  
 [ ] 0005_alter_user_last_login_null  
 [ ] 0006_require_contenttypes_0002  
 [ ] 0007_alter_validators_add_error_messages  
 [ ] 0008_alter_user_username_max_length  
 [ ] 0009_alter_user_last_name_max_length  
contenttypes  
 [ ] 0001_initial  
 [ ] 0002_remove_content_type_name  
sessions  
 [ ] 0001_initial  
sites  
 [ ] 0001_initial  
 [ ] 0002_alter_domain_unique  
socialaccount  
 [ ] 0001_initial  
 [ ] 0002_token_max_lengths  
 [ ] 0003_extra_data_default_dict  
> python manage.py makemigrations  

No changes detected  

> python manage.py migrate

Operations to perform:  
  Apply all migrations: account, admin, auth, contenttypes, sessions, sites, socialaccount  
Running migrations:  
  Applying account.0001_initial...Traceback (most recent call last):  
  File "/home/testuser/envs/venv367/lib/python3.6/site-packages/django/db/backends/utils.py", line 85, in _execute  
    return self.cursor.execute(sql, params)  
  File "/home/testuser/envs/venv367/lib/python3.6/site-packages/sql_server/pyodbc/base.py", line 546, in execute  
    return self.cursor.execute(sql, params)  
pyodbc.ProgrammingError: ('42000', "[42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Foreign key 'account_emailaddress_user_id_2c513194_fk_users_customuser_id' references invalid table 'users_customuser'. (1767) (SQLExecDirectW)")  

The above exception was the direct cause of the following exception:  

Traceback (most recent call last):  
  File "manage.py", line 15, in <module>  
    execute_from_command_line(sys.argv)  
  File "/home/testuser/envs/venv367/lib/python3.6/site-packages/django/core/management/__init__.py", line 381, in execute_from_command_line  
    utility.execute()  
  File "/home/testuser/envs/venv367/lib/python3.6/site-packages/django/core/management/__init__.py", line 375, in execute  
    self.fetch_command(subcommand).run_from_argv(self.argv)  
  File "/home/testuser/envs/venv367/lib/python3.6/site-packages/django/core/management/base.py", line 316, in run_from_argv  
    self.execute(*args, **cmd_options)  
  File "/home/testuser/envs/venv367/lib/python3.6/site-packages/django/core/management/base.py", line 353, in execute  
    output = self.handle(*args, **options)  
  File "/home/testuser/envs/venv367/lib/python3.6/site-packages/django/core/management/base.py", line 83, in wrapped  
    res = handle_func(*args, **kwargs)  
  File "/home/testuser/envs/venv367/lib/python3.6/site-packages/django/core/management/commands/migrate.py", line 203, in handle  
    fake_initial=fake_initial,  
  File "/home/testuser/envs/venv367/lib/python3.6/site-packages/django/db/migrations/executor.py", line 117, in migrate  
    state = self._migrate_all_forwards(state, plan, full_plan, fake=fake, fake_initial=fake_initial)  
  File "/home/testuser/envs/venv367/lib/python3.6/site-packages/django/db/migrations/executor.py", line 147, in _migrate_all_forwards  
    state = self.apply_migration(state, migration, fake=fake, fake_initial=fake_initial)  
  File "/home/testuser/envs/venv367/lib/python3.6/site-packages/django/db/migrations/executor.py", line 244, in apply_migration  
    state = migration.apply(state, schema_editor)  
  File "/home/testuser/envs/venv367/lib/python3.6/site-packages/django/db/backends/base/schema.py", line 106, in __exit__  
    self.execute(sql)  
  File "/home/testuser/envs/venv367/lib/python3.6/site-packages/sql_server/pyodbc/schema.py", line 653, in execute  
    cursor.execute(sql, params)  
  File "/home/testuser/envs/venv367/lib/python3.6/site-packages/django/db/backends/utils.py", line 68, in execute  
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)  
  File "/home/testuser/envs/venv367/lib/python3.6/site-packages/django/db/backends/utils.py", line 77, in _execute_with_wrappers  
    return executor(sql, params, many, context)  
  File "/home/testuser/envs/venv367/lib/python3.6/site-packages/django/db/backends/utils.py", line 85, in _execute  
    return self.cursor.execute(sql, params)  
  File "/home/testuser/envs/venv367/lib/python3.6/site-packages/django/db/utils.py", line 89, in __exit__  
    raise dj_exc_value.with_traceback(traceback) from exc_value  
  File "/home/testuser/envs/venv367/lib/python3.6/site-packages/django/db/backends/utils.py", line 85, in _execute  
    return self.cursor.execute(sql, params)  
  File "/home/testuser/envs/venv367/lib/python3.6/site-packages/sql_server/pyodbc/base.py", line 546, in execute  
    return self.cursor.execute(sql, params)  
django.db.utils.ProgrammingError: ('42000', "[42000] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Foreign key 'account_emailaddress_user_id_2c513194_fk_users_customuser_id' references invalid table 'users_customuser'. (1767) (SQLExecDirectW)")  

这是帐户应用程序中的models.py文件。请注意,这是django-allauth软件包的一部分-不是我的代码。我的代码未定义任何模型。

from __future__ import unicode_literals

import datetime

from django.core import signing
from django.db import models, transaction
from django.utils import timezone
from django.utils.crypto import get_random_string
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _

from . import app_settings, signals
from .. import app_settings as allauth_app_settings
from .adapter import get_adapter
from .managers import EmailAddressManager, EmailConfirmationManager
from .utils import user_email


@python_2_unicode_compatible
class EmailAddress(models.Model):

    user = models.ForeignKey(allauth_app_settings.USER_MODEL,
                             verbose_name=_('user'),
                             on_delete=models.CASCADE)
    email = models.EmailField(unique=app_settings.UNIQUE_EMAIL,
                              max_length=app_settings.EMAIL_MAX_LENGTH,
                              verbose_name=_('e-mail address'))
    verified = models.BooleanField(verbose_name=_('verified'), default=False)
    primary = models.BooleanField(verbose_name=_('primary'), default=False)

    objects = EmailAddressManager()

    class Meta:
        verbose_name = _("email address")
        verbose_name_plural = _("email addresses")
        if not app_settings.UNIQUE_EMAIL:
            unique_together = [("user", "email")]

    def __str__(self):
        return "%s (%s)" % (self.email, self.user)

    def set_as_primary(self, conditional=False):
        old_primary = EmailAddress.objects.get_primary(self.user)
        if old_primary:
            if conditional:
                return False
            old_primary.primary = False
            old_primary.save()
        self.primary = True
        self.save()
        user_email(self.user, self.email)
        self.user.save()
        return True

    def send_confirmation(self, request=None, signup=False):
        if app_settings.EMAIL_CONFIRMATION_HMAC:
            confirmation = EmailConfirmationHMAC(self)
        else:
            confirmation = EmailConfirmation.create(self)
        confirmation.send(request, signup=signup)
        return confirmation

    def change(self, request, new_email, confirm=True):
        """
        Given a new email address, change self and re-confirm.
        """
        with transaction.atomic():
            user_email(self.user, new_email)
            self.user.save()
            self.email = new_email
            self.verified = False
            self.save()
            if confirm:
                self.send_confirmation(request)


@python_2_unicode_compatible
class EmailConfirmation(models.Model):

    email_address = models.ForeignKey(EmailAddress,
                                      verbose_name=_('e-mail address'),
                                      on_delete=models.CASCADE)
    created = models.DateTimeField(verbose_name=_('created'),
                                   default=timezone.now)
    sent = models.DateTimeField(verbose_name=_('sent'), null=True)
    key = models.CharField(verbose_name=_('key'), max_length=64, unique=True)

    objects = EmailConfirmationManager()

    class Meta:
        verbose_name = _("email confirmation")
        verbose_name_plural = _("email confirmations")

    def __str__(self):
        return "confirmation for %s" % self.email_address

    @classmethod
    def create(cls, email_address):
        key = get_random_string(64).lower()
        return cls._default_manager.create(email_address=email_address,
                                           key=key)

    def key_expired(self):
        expiration_date = self.sent \
            + datetime.timedelta(days=app_settings
                                 .EMAIL_CONFIRMATION_EXPIRE_DAYS)
        return expiration_date <= timezone.now()
    key_expired.boolean = True

    def confirm(self, request):
        if not self.key_expired() and not self.email_address.verified:
            email_address = self.email_address
            get_adapter(request).confirm_email(request, email_address)
            signals.email_confirmed.send(sender=self.__class__,
                                         request=request,
                                         email_address=email_address)
            return email_address

    def send(self, request=None, signup=False):
        get_adapter(request).send_confirmation_mail(request, self, signup)
        self.sent = timezone.now()
        self.save()
        signals.email_confirmation_sent.send(sender=self.__class__,
                                             request=request,
                                             confirmation=self,
                                             signup=signup)


class EmailConfirmationHMAC:

    def __init__(self, email_address):
        self.email_address = email_address

    @property
    def key(self):
        return signing.dumps(
            obj=self.email_address.pk,
            salt=app_settings.SALT)

    @classmethod
    def from_key(cls, key):
        try:
            max_age = (
                60 * 60 * 24 * app_settings.EMAIL_CONFIRMATION_EXPIRE_DAYS)
            pk = signing.loads(
                key,
                max_age=max_age,
                salt=app_settings.SALT)
            ret = EmailConfirmationHMAC(EmailAddress.objects.get(pk=pk))
        except (signing.SignatureExpired,
                signing.BadSignature,
                EmailAddress.DoesNotExist):
            ret = None
        return ret

    def confirm(self, request):
        if not self.email_address.verified:
            email_address = self.email_address
            get_adapter(request).confirm_email(request, email_address)
            signals.email_confirmed.send(sender=self.__class__,
                                         request=request,
                                         email_address=email_address)
            return email_address

    def send(self, request=None, signup=False):
        get_adapter(request).send_confirmation_mail(request, self, signup)
        signals.email_confirmation_sent.send(sender=self.__class__,
                                             request=request,
                                             confirmation=self,
                                             signup=signup)

这是django-allauth软件包中的0001_initial.py文件。不是我的代码。

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
import django.utils.timezone
from django.conf import settings

UNIQUE_EMAIL = getattr(settings, 'ACCOUNT_UNIQUE_EMAIL', True)


class Migration(migrations.Migration):

    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
    ]

    operations = [
        migrations.CreateModel(
            name='EmailAddress',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                ('email', models.EmailField(unique=UNIQUE_EMAIL, max_length=75, verbose_name='e-mail address')),
                ('verified', models.BooleanField(default=False, verbose_name='verified')),
                ('primary', models.BooleanField(default=False, verbose_name='primary')),
                ('user', models.ForeignKey(verbose_name='user', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
            ],
            options={
                'verbose_name': 'email address',
                'verbose_name_plural': 'email addresses',
            },
            bases=(models.Model,),
        ),
        migrations.CreateModel(
            name='EmailConfirmation',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                ('created', models.DateTimeField(default=django.utils.timezone.now, verbose_name='created')),
                ('sent', models.DateTimeField(null=True, verbose_name='sent')),
                ('key', models.CharField(unique=True, max_length=64, verbose_name='key')),
                ('email_address', models.ForeignKey(verbose_name='e-mail address', to='account.EmailAddress', on_delete=models.CASCADE)),
            ],
            options={
                'verbose_name': 'email confirmation',
                'verbose_name_plural': 'email confirmations',
            },
            bases=(models.Model,),
        ),
    ]

    if not UNIQUE_EMAIL:
        operations += [
            migrations.AlterUniqueTogether(
                name='emailaddress',
                unique_together=set([('user', 'email')]),
            ),
        ]

我希望干净迁移,并创建表。实际上在错误发生之前创建了一些表。

2 个答案:

答案 0 :(得分:1)

错误消息:

  

pyodbc.ProgrammingError :(“ 42000”,“ [42000] [Microsoft] [ODBC驱动程序17   for SQL Server] [SQL Server]外键   'account_emailaddress_user_id_2c513194_fk_users_customuser_id'   引用无效的表'users_customuser'。 (1767)(SQLExecDirectW)“)

告诉您迁移正在尝试通过名称users_customuser查找自定义用户表。这告诉我,您的设置文件中的某个位置可能有:

AUTH_USER_MODEL = 'users.customuser'

开始一个项目之前的Django文档highly recommend creating a custom user model,但是您不必这样做。如果您的设置中有上述设置,则必须在CustomUser应用中创建users模型。最简单的方法是使用上面的Django说明中所述的from django.contrib.auth.models import AbstractUser(不是AbstractBaseUser)。另外,您也可以从代码中删除AUTH_USER_MODEL = 'users.customuser'并坚持使用Django的默认用户模型。如果您使用自定义用户模型,则首先为该应用创建迁移-假设应用名称为users,请执行以下操作:

python manage.py makemigrations users

如果您从设置中删除了AUTH_USER_MODEL = ...,那么您应该可以只进行python manage.py migrate

答案 1 :(得分:1)

由于所有身份验证与升级django版本不匹配而导致的所有错误,您只需升级所有身份验证即可满足错误要求。

尝试一下:

pip install --upgrade django-allauth