为什么 Django 管理模型页面在成功迁移后会引发 FieldDoesNotExist 异常?

发布于 2025-01-16 10:30:56 字数 9907 浏览 2 评论 0原文

为这个问题挠头。我只是向模型添加了一个新字段。

class Image(BaseModel):
    url = models.URLField(max_length=1000, null=True)
    content_type = models.ForeignKey(ContentType, on_delete=models.SET_NULL, null=True)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey()
    
    # new field added below
    class Type(models.TextChoices):
        HEADER = 'HEADER', _('Header')

    type = models.CharField(
        max_length=20,
        choices=Type.choices,
        null=True,
        blank=True
    )

    class Meta:
        db_table = 'Image'

然后我运行了python manage.py makemigrations,然后运行了python manage.py migrate。这是成功的,我可以在数据库中的表上看到新字段。 django_migrations 表中的信息看起来正确。

这是我的迁移文件:

# Generated by Django 3.0.5 on 2022-03-23 12:46

from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('example', '0082_auto_20220322_1937'),
    ]

    operations = [
        migrations.AddField(
            model_name='image',
            name='type',
            field=models.CharField(blank=True, choices=[('HEADER', 'Header')], max_length=20, null=True),
        ),
    ]

这是我的图像管理文件:

from .base import BaseModelAdmin


class ImageAdmin(BaseModelAdmin):
    list_display = ('id', 'url', 'content_type', 'content_object', 'type', 'is_deleted')
    list_filter = ('is_deleted',)

这是 ImageAdmin 继承自的 BaseModelAdmin:

from django.contrib.contenttypes.admin import GenericTabularInline
from django.contrib import admin


class BaseModelAdmin(admin.ModelAdmin):

    def get_actions(self, request):
        actions = super(BaseModelAdmin, self).get_actions(request)

        if 'delete_selected' in actions:
            del actions['delete_selected']

        return actions

    def has_delete_permission(self, request, obj=None):
        return False


class BaseStackedInline(admin.StackedInline):

    def get_actions(self, request):
        actions = super(BaseModelAdmin, self).get_actions(request)

        if 'delete_selected' in actions:
            del actions['delete_selected']

        return actions

    def has_delete_permission(self, request, obj=None):
        return False


class BaseGenericTabularInline(GenericTabularInline):

    def get_actions(self, request):
        actions = super(BaseModelAdmin, self).get_actions(request)

        if 'delete_selected' in actions:
            del actions['delete_selected']

        return actions

    def has_delete_permission(self, request, obj=None):
        return False

这是图像模型继承自的 BaseModel:

from django.db import models


class BaseManager(models.Manager):
    def get_or_none(self, *args, **kwargs):
        try:
            return self.get(*args, **kwargs)
        except self.model.DoesNotExist:
            return None


class BaseModel(models.Model):
    is_deleted = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    objects = BaseManager()

    def __str__(self):
        return F"ID: {self.id}"

    class Meta:
        abstract = True

我面临的问题是当我在 django admin 中访问该模型页面时,我收到以下内部服务器错误:

Traceback (most recent call last):
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/contrib/admin/utils.py", line 262, in lookup_field
example_app  |     f = _get_non_gfk_field(opts, name)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/contrib/admin/utils.py", line 297, in _get_non_gfk_field
example_app  |     raise FieldDoesNotExist()
example_app  | django.core.exceptions.FieldDoesNotExist
example_app  |
example_app  | During handling of the above exception, another exception occurred:
example_app  |
example_app  | Traceback (most recent call last):
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/core/handlers/exception.py", line 34, in inner
example_app  |     response = get_response(request)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/core/handlers/base.py", line 145, in _get_response
example_app  |     response = self.process_exception_by_middleware(e, request)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/core/handlers/base.py", line 143, in _get_response
example_app  |     response = response.render()
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/response.py", line 105, in render
example_app  |     self.content = self.rendered_content
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/response.py", line 83, in rendered_content
example_app  |     return template.render(context, self._request)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/backends/django.py", line 61, in render
example_app  |     return self.template.render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 171, in render
example_app  |     return self._render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 163, in _render
example_app  |     return self.nodelist.render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 936, in render
example_app  |     bit = node.render_annotated(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 903, in render_annotated
example_app  |     return self.render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/loader_tags.py", line 150, in render
example_app  |     return compiled_parent._render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 163, in _render
example_app  |     return self.nodelist.render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 936, in render
example_app  |     bit = node.render_annotated(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 903, in render_annotated
example_app  |     return self.render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/loader_tags.py", line 150, in render
example_app  |     return compiled_parent._render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 163, in _render
example_app  |     return self.nodelist.render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 936, in render
example_app  |     bit = node.render_annotated(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 903, in render_annotated
example_app  |     return self.render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/loader_tags.py", line 62, in render
example_app  |     result = block.nodelist.render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 936, in render
example_app  |     bit = node.render_annotated(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 903, in render_annotated
example_app  |     return self.render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/loader_tags.py", line 62, in render
example_app  |     result = block.nodelist.render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 936, in render
example_app  |     bit = node.render_annotated(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 903, in render_annotated
example_app  |     return self.render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/contrib/admin/templatetags/base.py", line 33, in render
example_app  |     return super().render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/library.py", line 214, in render
example_app  |     _dict = self.func(*resolved_args, **resolved_kwargs)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/contrib/admin/templatetags/admin_list.py", line 342, in result_list
example_app  |     'results': list(results(cl)),
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/contrib/admin/templatetags/admin_list.py", line 318, in results
example_app  |     yield ResultList(None, items_for_result(cl, res, None))
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/contrib/admin/templatetags/admin_list.py", line 309, in __init__
example_app  |     super().__init__(*items)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/contrib/admin/templatetags/admin_list.py", line 232, in items_for_result
example_app  |     f, attr, value = lookup_field(field_name, result, cl.model_admin)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/contrib/admin/utils.py", line 273, in lookup_field
example_app  |     attr = getattr(obj, name)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/contrib/contenttypes/fields.py", line 243, in __get__
example_app  |     rel_obj = ct.get_object_for_this_type(pk=pk_val)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/contrib/contenttypes/models.py", line 175, in get_object_for_this_type
example_app  |     return self.model_class()._base_manager.using(self._state.db).get(**kwargs)
example_app  | AttributeError: 'NoneType' object has no attribute '_base_manager'

我已多次尝试恢复迁移并重新运行它。我还查看了其他类似的问题,但没有一个解决方案有效或解决了我的具体问题。

为什么会发生这种情况?

Scratching my head on this one. I've simply added a new field to a model.

class Image(BaseModel):
    url = models.URLField(max_length=1000, null=True)
    content_type = models.ForeignKey(ContentType, on_delete=models.SET_NULL, null=True)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey()
    
    # new field added below
    class Type(models.TextChoices):
        HEADER = 'HEADER', _('Header')

    type = models.CharField(
        max_length=20,
        choices=Type.choices,
        null=True,
        blank=True
    )

    class Meta:
        db_table = 'Image'

Then I ran python manage.py makemigrations followed by python manage.py migrate. This was successful and I can see the new field on the table in my database. The info from the django_migrations table looks correct.

Here is my migration file:

# Generated by Django 3.0.5 on 2022-03-23 12:46

from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('example', '0082_auto_20220322_1937'),
    ]

    operations = [
        migrations.AddField(
            model_name='image',
            name='type',
            field=models.CharField(blank=True, choices=[('HEADER', 'Header')], max_length=20, null=True),
        ),
    ]

Here is my Image Admin File:

from .base import BaseModelAdmin


class ImageAdmin(BaseModelAdmin):
    list_display = ('id', 'url', 'content_type', 'content_object', 'type', 'is_deleted')
    list_filter = ('is_deleted',)

Here is the BaseModelAdmin that ImageAdmin inherits from:

from django.contrib.contenttypes.admin import GenericTabularInline
from django.contrib import admin


class BaseModelAdmin(admin.ModelAdmin):

    def get_actions(self, request):
        actions = super(BaseModelAdmin, self).get_actions(request)

        if 'delete_selected' in actions:
            del actions['delete_selected']

        return actions

    def has_delete_permission(self, request, obj=None):
        return False


class BaseStackedInline(admin.StackedInline):

    def get_actions(self, request):
        actions = super(BaseModelAdmin, self).get_actions(request)

        if 'delete_selected' in actions:
            del actions['delete_selected']

        return actions

    def has_delete_permission(self, request, obj=None):
        return False


class BaseGenericTabularInline(GenericTabularInline):

    def get_actions(self, request):
        actions = super(BaseModelAdmin, self).get_actions(request)

        if 'delete_selected' in actions:
            del actions['delete_selected']

        return actions

    def has_delete_permission(self, request, obj=None):
        return False

Here is the BaseModel that the Image model inherits from:

from django.db import models


class BaseManager(models.Manager):
    def get_or_none(self, *args, **kwargs):
        try:
            return self.get(*args, **kwargs)
        except self.model.DoesNotExist:
            return None


class BaseModel(models.Model):
    is_deleted = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    objects = BaseManager()

    def __str__(self):
        return F"ID: {self.id}"

    class Meta:
        abstract = True

The problem I'm facing is when I visit that model page in the django admin, I get an internal server error with the following:

Traceback (most recent call last):
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/contrib/admin/utils.py", line 262, in lookup_field
example_app  |     f = _get_non_gfk_field(opts, name)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/contrib/admin/utils.py", line 297, in _get_non_gfk_field
example_app  |     raise FieldDoesNotExist()
example_app  | django.core.exceptions.FieldDoesNotExist
example_app  |
example_app  | During handling of the above exception, another exception occurred:
example_app  |
example_app  | Traceback (most recent call last):
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/core/handlers/exception.py", line 34, in inner
example_app  |     response = get_response(request)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/core/handlers/base.py", line 145, in _get_response
example_app  |     response = self.process_exception_by_middleware(e, request)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/core/handlers/base.py", line 143, in _get_response
example_app  |     response = response.render()
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/response.py", line 105, in render
example_app  |     self.content = self.rendered_content
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/response.py", line 83, in rendered_content
example_app  |     return template.render(context, self._request)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/backends/django.py", line 61, in render
example_app  |     return self.template.render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 171, in render
example_app  |     return self._render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 163, in _render
example_app  |     return self.nodelist.render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 936, in render
example_app  |     bit = node.render_annotated(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 903, in render_annotated
example_app  |     return self.render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/loader_tags.py", line 150, in render
example_app  |     return compiled_parent._render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 163, in _render
example_app  |     return self.nodelist.render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 936, in render
example_app  |     bit = node.render_annotated(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 903, in render_annotated
example_app  |     return self.render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/loader_tags.py", line 150, in render
example_app  |     return compiled_parent._render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 163, in _render
example_app  |     return self.nodelist.render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 936, in render
example_app  |     bit = node.render_annotated(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 903, in render_annotated
example_app  |     return self.render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/loader_tags.py", line 62, in render
example_app  |     result = block.nodelist.render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 936, in render
example_app  |     bit = node.render_annotated(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 903, in render_annotated
example_app  |     return self.render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/loader_tags.py", line 62, in render
example_app  |     result = block.nodelist.render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 936, in render
example_app  |     bit = node.render_annotated(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/base.py", line 903, in render_annotated
example_app  |     return self.render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/contrib/admin/templatetags/base.py", line 33, in render
example_app  |     return super().render(context)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/template/library.py", line 214, in render
example_app  |     _dict = self.func(*resolved_args, **resolved_kwargs)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/contrib/admin/templatetags/admin_list.py", line 342, in result_list
example_app  |     'results': list(results(cl)),
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/contrib/admin/templatetags/admin_list.py", line 318, in results
example_app  |     yield ResultList(None, items_for_result(cl, res, None))
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/contrib/admin/templatetags/admin_list.py", line 309, in __init__
example_app  |     super().__init__(*items)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/contrib/admin/templatetags/admin_list.py", line 232, in items_for_result
example_app  |     f, attr, value = lookup_field(field_name, result, cl.model_admin)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/contrib/admin/utils.py", line 273, in lookup_field
example_app  |     attr = getattr(obj, name)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/contrib/contenttypes/fields.py", line 243, in __get__
example_app  |     rel_obj = ct.get_object_for_this_type(pk=pk_val)
example_app  |   File "/usr/local/lib/python3.9/site-packages/django/contrib/contenttypes/models.py", line 175, in get_object_for_this_type
example_app  |     return self.model_class()._base_manager.using(self._state.db).get(**kwargs)
example_app  | AttributeError: 'NoneType' object has no attribute '_base_manager'

I've tried reverting the migration and rerunning it many times. I've also looked at other similar questions but none of the solutions worked or addressed my specific issue.

Why is this happening?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

当梦初醒 2025-01-23 10:30:56

我怀疑添加新字段并不是问题的原因 - 这只是巧合,问题是在您进行此更改的同时出现的。线索就在回溯中:

File "/usr/local/lib/python3.9/site-packages/django/contrib/contenttypes/fields.py", line 243, in __get__
rel_obj = ct.get_object_for_this_type(pk=pk_val)
File "/usr/local/lib/python3.9/site-packages/django/contrib/contenttypes/models.py", line 175, in get_object_for_this_type
return self.model_class()._base_manager.using(self._state.db).get(**kwargs)
AttributeError: 'NoneType' object has no attribute '_base_manager'

具体来说,错误来自 contenttypes 框架 - 即,您的 content_type 字段在数据库中至少一个现有模型实例上被破坏。 model_class () 方法返回空值而不是模型类。

最可能的原因是您删除了之前由通用外键引用的模型,或者您已从 INSTALLED_APPS 中删除了提供该模型的应用。

您应该能够通过运行 来解决该问题remove_stale_contenttypes 管理命令。如果这不起作用,那么可能需要手动检查数据库以找出问题所在(清空整个数据库也将有助于验证这是否是问题所在)。

FieldDoesNotExist 异常不是问题。它将被正确处理如果您的内容类型没有问题,则为 Django。


旁注:我认为这与您的问题没有任何关系,但使用与 Python 冲突的名称并不是一个好主意 内置函数,因为它可能会导致令人困惑的行为。

I suspect that adding a new field isn't the cause of the problem - it's just coincidence that the problem arose at the same time as you made this change. The clue is in the traceback:

File "/usr/local/lib/python3.9/site-packages/django/contrib/contenttypes/fields.py", line 243, in __get__
rel_obj = ct.get_object_for_this_type(pk=pk_val)
File "/usr/local/lib/python3.9/site-packages/django/contrib/contenttypes/models.py", line 175, in get_object_for_this_type
return self.model_class()._base_manager.using(self._state.db).get(**kwargs)
AttributeError: 'NoneType' object has no attribute '_base_manager'

Specifically, the error is coming from the contenttypes framework - i.e., your content_type field is broken on at least one existing model instance in the database. The model_class() method is returning a null value instead of the model class.

The most likely cause of this is that you have deleted a model which was previously being referred to by your generic foreign key, or you have removed the app which provided that model from INSTALLED_APPS.

You should be able to resolve the issue by running the remove_stale_contenttypes management command. If that doesn't work, then it's probably going to be a case of manually inspecting the database to find out where the problem is (emptying the whole database will also help verify whether this is the issue).

The FieldDoesNotExist exception is not a problem. It would be properly handled by Django if there wasn't an issue with your content types.


Side note: I don't think it has anything to do with your problem, but it's not a good idea to use names that clash with Python built-in functions as it can lead to confusing behaviour.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文