shacker / django-todo

A multi-user, multi-group todo/ticketing system for Django projects. Includes CSV import and integrated mail tracking.
http://django-todo.org
BSD 3-Clause "New" or "Revised" License
819 stars 285 forks source link

admin.E040) ModelAdmin must define "search_fields", because it's referenced by AttachmentAdmin.autocomplete_fields #87

Closed mikegropp closed 5 years ago

mikegropp commented 5 years ago

Fresh installation of Django-todo and I am getting this error:


Watching for file changes with StatReloader Performing system checks...

Exception in thread django-main-thread: Traceback (most recent call last): File "/usr/lib/python3.7/threading.py", line 917, in _bootstrap_inner self.run() File "/usr/lib/python3.7/threading.py", line 865, in run self._target(*self._args, *self._kwargs) File "/home/mike/test/venv/lib/python3.7/site-packages/django/utils/autoreload.py", line 54, in wrapper fn(args, **kwargs) File "/home/mike/test/venv/lib/python3.7/site-packages/django/core/management/commands/runserver.py", line 117, in inner_run self.check(display_num_errors=True) File "/home/mike/test/venv/lib/python3.7/site-packages/django/core/management/base.py", line 436, in check raise SystemCheckError(msg) django.core.management.base.SystemCheckError: SystemCheckError: System check identified some issues:

ERRORS: <class 'todo.admin.AttachmentAdmin'>: (admin.E040) ModelAdmin must define "search_fields", because it's referenced by AttachmentAdmin.autocomplete_fields.

System check identified 1 issue (0 silenced).


Packages:


asn1crypto 0.24.0
attrs 17.4.0
Automat 0.6.0
bleach 3.1.0
certifi 2018.1.18
cffi 1.12.3
chardet 3.0.4
click 6.7
colorama 0.3.7
configobj 5.0.6
constantly 15.1.0
cryptography 2.1.4
Django 2.2.5
django-extensions 2.2.1
django-tempus-dominus 5.1.2.6.post1 django-todo 2.4.8
djangorestframework 3.10.3
djangorestframework-simplejwt 4.3.0
httplib2 0.9.2
hyperlink 17.3.1
idna 2.6
incremental 16.10.1
netifaces 0.10.4
Pillow 6.1.0
pip 19.2.3
pkgconfig 1.5.1
psycopg2-binary 2.8.3
pyasn1 0.4.2
pyasn1-modules 0.2.1
pycairo 1.18.1
pycparser 2.19
PyGObject 3.34.0
PyJWT 1.7.1
pyOpenSSL 17.5.0
pyserial 3.4
python-debian 0.1.32
pytz 2019.2
PyYAML 3.12
requests 2.18.4
requests-unixsocket 0.1.5
service-identity 16.0.0
setuptools 41.2.0
six 1.12.0
sqlparse 0.3.0
ssh-import-id 5.6
systemd-python 234
Twisted 17.9.0
Unidecode 1.1.1
urllib3 1.22
virtualenv 16.7.5
webencodings 0.5.1
wheel 0.33.6
zope.interface 4.3.2

shacker commented 5 years ago

This is under Django 2.2.5? Not sure I've tested that recent version. Should be trivial to fix; Can you submit a quick patch? If not, I'll get to it soon.

mikegropp commented 5 years ago

Uninstalled 2.2.5 and installed 2.2.1, but throws the same error. Not sure where to start. I tried adding search fields, but to no avail.

mikegropp commented 5 years ago

I've narrowed down the problem to the project settings.py file. I will test different settings to see where the issue is at and if I can resolve it. I'll report back in either case.

mikegropp commented 5 years ago

I believe the issue is my custom user. If I remove this part, the server will run without that error, but with the custom user in it won't.

Project settings.py file


import os

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(file)))

SECRET_KEY = ""

DEBUG = True

ALLOWED_HOSTS = []

INSTALLED_APPS = [ "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", "django.contrib.sites", "rest_framework", "crm", "users", "todo", "tempus_dominus", "django_extensions", ]

TEMPUS_DOMINUS_INCLUDE_ASSETS = True

SITE_ID = 1

AUTH_USER_MODEL = "users.User"

MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", ]

ROOT_URLCONF = "testproject.urls"

TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "DIRS": [], "APP_DIRS": True, "OPTIONS": { "context_processors": [ "django.template.context_processors.debug", "django.template.context_processors.request", "django.contrib.auth.context_processors.auth", "django.contrib.messages.context_processors.messages", "django.template.context_processors.media", ] }, } ]

WSGI_APPLICATION = "testproject.wsgi.application"

DATABASES = { "default": { "ENGINE": "django.db.backends.postgresql_psycopg2", "NAME": "", "USER": "", "PASSWORD": "", "HOST": "localhost", "PORT": "", } }

AUTH_PASSWORD_VALIDATORS = [ { "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator" }, {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"}, {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"}, {"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"}, ]

LANGUAGE_CODE = "en-us"

TIME_ZONE = "Asia/Shanghai"

USE_I18N = False

USE_L10N = True

USE_TZ = False

STATIC_URL = "/static/"

STATICFILES_DIRS = [os.path.join(BASE_DIR, "static")]

STATIC_ROOT = os.path.join(BASE_DIR, "static_cdn")

MEDIA_ROOT = os.path.join(BASE_DIR, "media")

MEDIA_URL = '/media/'

REST_FRAMEWORK = { "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",) }


Custom users app admin.py file:


from django.contrib import admin from .models import User from django.contrib.auth.models import Group

admin.site.register(User)


Custom suers app models.py file:


from django.db import models from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager from django.contrib.auth.models import Group

from django.contrib.auth.models import PermissionsMixin

class UserManager(BaseUserManager): def create_user( self, username, email, first_name, last_name, password=None, is_active=True, is_staff=False, is_admin=False, ): if not username: raise ValueError("Users must have a username.") if not email: raise ValueError("Users must have an email.") if not password: raise ValueError("Users must have a password.") user_obj = self.model( email=self.normalize_email(email), username=username, first_name=first_name, last_name=last_name, ) user_obj.set_password(password) user_obj.save(using=self._db) return user_obj

def create_staffuser(self, username, email, first_name, last_name, password):
    user = self.create_user(
        username, email, first_name, last_name, password=password
    )
    user.staff = True
    user.save(using=self._db)
    return user

def create_superuser(self, username, email, first_name, last_name, password=None):
    user = self.create_user(
        username, email, first_name, last_name, password=password
    )
    user.staff = True
    user.admin = True
    user.save(using=self._db)
    return user

class User(AbstractBaseUser, PermissionsMixin): username = models.CharField(max_length=255, unique=True) email = models.EmailField(max_length=255, unique=True) first_name = models.CharField(max_length=255) last_name = models.CharField(max_length=255) active = models.BooleanField(default=True) staff = models.BooleanField(default=False) superuser = models.BooleanField(default=False)

USERNAME_FIELD = "username"
REQUIRED_FIELDS = ["email", "first_name", "last_name"]

objects = UserManager()

def __str__(self):
    return self.first_name

def get_full_name(self):
    return self.first_name + " " + self.last_name

def full_name(self):
    return self.get_full_name()

def get_short_name(self):
    return self.first_name

def has_perm(self, perm, obj=None):
    return True

def has_module_perms(self, app_label):
    return True

@property
def is_active(self):
    return self.active

@property
def is_staff(self):
    return self.staff

@property
def is_superuser(self):
    return self.superuser

Any insights would be appreciated. Todo is a great app. I've made some customizations, and I hope I can still use it.

shacker commented 5 years ago

Yes, it makes sense that this is the result of having a custom user model. If you look in todo's admin.py, you see:

class AttachmentAdmin(admin.ModelAdmin):
    list_display = ("task", "added_by", "timestamp", "file")
    autocomplete_fields = ["added_by", "task"]

It's those autocomplete fields that are triggering this. They do some nice magic, but they need to know which fields in the target model should be searched as the user types in real time. One of the fields you see here is "added_by" and that references the project User. The Django default User model defines one or more search_fields on the User model admin but your project has none. Just do something like this in your admin.py, assuming your user model is PersonAdmin.

class PersonAdmin(admin.ModelAdmin):
    list_display = ('name', 'age')  # whatever
    search_fields = ('username',)

You can keep search_fields like that, or adjust to contain whatever you want to be searchable.

Closing this, but let me know if this doesn't fix it.

mikegropp commented 5 years ago

Thanks for the insight. It worked!