'clashes with the field' error

hello,

for your consideration.
i get this error:

app.Student.teacher: (models.E006) The field 'teacher' clashes with the field 'teacher' from model 'app.user'.

model.py

class User(AbstractUser):
    class Role(models.TextChoices):
        ADMIN = "ADMIN", "Admin"
        STUDENT = "STUDENT", "Student"
        TEACHER = "TEACHER", "Teacher"

    base_role = Role.ADMIN
    role = models.CharField(max_length=50, choices=Role.choices)

    def save(self, *args, **kwargs):
        if not self.pk:
            self.role = self.base_role
        return super().save(*args, **kwargs)


class TeacherManager(BaseUserManager):
    def get_queryset(self, *args, **kwargs):
        results = super().get_queryset(*args, **kwargs)
        return results.filter(role=User.Role.TEACHER)

class StudentManager(BaseUserManager):
    def get_queryset(self, *args, **kwargs):
        results = super().get_queryset(*args, **kwargs)
        return results.filter(role=User.Role.STUDENT)

class Teacher(User):
    base_role = User.Role.TEACHER
    teacher = TeacherManager()
    name = models.CharField(max_length=200)

class Student(User):
    base_role = User.Role.STUDENT
    student = StudentManager()
    teacher = models.ForeignKey(Teacher, max_length="50", on_delete=models.CASCADE)
    name = models.CharField(max_length=200)

class Exercise(models.Model):
    title = models.CharField(max_length=200)
    teacher = models.ForeignKey(Teacher, max_length="50", on_delete=models.CASCADE)
    def __str__(self):
        return self.title

i find this quite interesting, since the class User doesn’t have any field ‘teacher’…?

tnx!

td

renamed field ‘teacher’ to instructor:

instructor = models.ForeignKey(Teacher, max_length="50", on_delete=models.CASCADE)

makemigrations, no error. but during migration:

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, app
Running migrations:
  Applying app.0013_remove_studentprofile_teacher_and_more...Traceback (most recent call last):
  File "C:\Users\taxidriver\AppData\Local\Programs\Python\Python311\Lib\site-packages\django\db\models\options.py", line 681, in get_field
    return self.fields_map[field_name]
           ~~~~~~~~~~~~~~~^^^^^^^^^^^^
KeyError: 'instructor'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\taxidriver\Desktop\uemp2\taxidriver2\manage.py", line 22, in <module>
    main()
  File "C:\Users\taxidriver\Desktop\uemp2\taxidriver2\manage.py", line 18, in main
    execute_from_command_line(sys.argv)
  File "C:\Users\taxidriver\AppData\Local\Programs\Python\Python311\Lib\site-packages\django\core\management\__init__.py", line 442, in execute_from_command_line
    utility.execute()
  File "C:\Users\taxidriver\AppData\Local\Programs\Python\Python311\Lib\site-packages\django\core\management\__init__.py", line 436, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "C:\Users\taxidriver\AppData\Local\Programs\Python\Python311\Lib\site-packages\django\core\management\base.py", line 412, in run_from_argv
    self.execute(*args, **cmd_options)
  File "C:\Users\taxidriver\AppData\Local\Programs\Python\Python311\Lib\site-packages\django\core\management\base.py", line 458, in execute
    output = self.handle(*args, **options)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\taxidriver\AppData\Local\Programs\Python\Python311\Lib\site-packages\django\core\management\base.py", line 106, in wrapper
    res = handle_func(*args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\taxidriver\AppData\Local\Programs\Python\Python311\Lib\site-packages\django\core\management\commands\migrate.py", line 356, in handle
    post_migrate_state = executor.migrate(
                         ^^^^^^^^^^^^^^^^^
  File "C:\Users\taxidriver\AppData\Local\Programs\Python\Python311\Lib\site-packages\django\db\migrations\executor.py", line 135, in migrate
    state = self._migrate_all_forwards(
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\taxidriver\AppData\Local\Programs\Python\Python311\Lib\site-packages\django\db\migrations\executor.py", line 167, in _migrate_all_forwards
    state = self.apply_migration(
            ^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\taxidriver\AppData\Local\Programs\Python\Python311\Lib\site-packages\django\db\migrations\executor.py", line 252, in apply_migration
    state = migration.apply(state, schema_editor)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\taxidriver\AppData\Local\Programs\Python\Python311\Lib\site-packages\django\db\migrations\migration.py", line 132, in apply
    operation.database_forwards(
  File "C:\Users\taxidriver\AppData\Local\Programs\Python\Python311\Lib\site-packages\django\db\migrations\operations\fields.py", line 231, in database_forwards
    from_field = from_model._meta.get_field(self.name)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\taxidriver\AppData\Local\Programs\Python\Python311\Lib\site-packages\django\db\models\options.py", line 683, in get_field
    raise FieldDoesNotExist(
django.core.exceptions.FieldDoesNotExist: Student has no field named 'instructor'

hi fellow djangonians,

i started new and have the following models:

class CustomUser(AbstractUser):
    pass
    # add additional fields in here

    def __str__(self):
        return self.username

class Teacher(CustomUser):
    name = models.CharField(max_length=200)

class Student(CustomUser):
    name = models.CharField(max_length=200)
    teacher = models.ForeignKey(Teacher, on_delete=models.CASCADE)

class Exercise(models.Model):
    title = models.CharField(max_length=200)
    teacher = models.ForeignKey(Teacher, on_delete=models.CASCADE)

and get the same error again. i don’t know why, because why shouldn’t there be a field “teacher_id” in both the Exercise and Student tables?

ueplan.Student.teacher: (models.E006) The field 'teacher' clashes with the field 'teacher' from model 'ueplan.customuser'.

settings.py

AUTH_USER_MODEL = "ueplan.CustomUser"

also tried

from django.contrib.auth.models import AbstractUser
from django.db import models
from django.conf import settings

class CustomUser(AbstractUser):
    pass
    # add additional fields in here

    def __str__(self):
        return self.username

class Teacher(CustomUser):
    name = models.CharField(max_length=200)

class Student(CustomUser):
    name = models.CharField(max_length=200)
    teacher = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="+",)

class Exercise(models.Model):
    title = models.CharField(max_length=200)
    teacher = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="+",)

still:

ueplan.Student.teacher: (models.E006) The field 'teacher' clashes with the field 'teacher' from model 'ueplan.customuser'.

tnx for any help!

td

You do not want to try to have multiple user classes.

Your users should all be in one model, with (perhaps) attributes or (more appropriately) group membership to define the differences between the two.

You’ll want to refactor your models to simplify this structure.

hi ken,

i now have this setup:

from django.contrib.auth.models import AbstractUser
from django.db import models

class CustomUser(AbstractUser):
    is_teacher = models.BooleanField('teacher status', default=False)
    # add additional fields here, if needed

    def __str__(self):
        return self.username

class ExerciseManager(models.Manager):
    def create_exercise(self, title, teacher):
        exercise = self.create(title=title, teacher=teacher)
        exercise.save()
        return exercise

class Exercise(models.Model):
    teacher = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='teacher_exercises',limit_choices_to={'is_teacher': True})
    title = models.CharField(max_length=50, unique=True)
    text = models.TextField(default="Noch kein Text...")
    created_at = models.DateTimeField(auto_now_add=True)
    students = models.ManyToManyField(CustomUser, related_name='student_exercises', blank=True, limit_choices_to={'is_teacher': False})
    objects = ExerciseManager()

    def __str__(self):
        return self.title

building my custom backend, everything working well so far.

just can’t get my head around how to realize a one-to-many relationship when both teachers and students are in the same database(table/model)?

any hint appreciated!

td

Is it truly a one-to-many, or is it actually a many-to-many?

Can a student be a student to multiple teachers? (M2M)

Or is a student a student to only one teacher? (O2M)

hello ken,

Can a student be a student to multiple teachers? (M2M)

he could be, i guess,
but from the reality of the usecase (i use a ‘beta’ version) it is clearly so, that a student has only one teacher. think of the app as enterprise software for a business that has some teachers. the students never have two teachers. so i think o2m would accurately model this.
if the app was to be like a worldwide storefront for students to find different teachers in all kinds of trades then i think thats more of a m2m scenario.

but i also wonder if would m2m be even “wrong” in my use case. would it cause some problems (e.g. when a teacher cancels his account and leaves the student accounts stranded), or would it even have more optionality? i lack the experience to assess that.

given both options were viable, i would go for the one thats easier to implement. i also lack the experience to assess which one that would be.

given my resources (time, developing skills) i need to keep it as simple as possible. my gut says o2m.

very much interested in any thoughts on that!

td

Great! Very helpful explanation.

First, I need to backtrack and correct something I wrote earlier.

I misread the code you had posted here:

This is actually an appropriate way to do this - What you have done is created two different types of profile models, which is an acceptable way to do this. (You are actually creating three separate models here, using what Django refers to as multi-table inheritance.)

However, what I suggest you change here is:

  • Change the foreign key field in Student to refer to Teacher. (I don’t think you want to be able to assign a Student to another Student.
  • Change the foreign key field in Exercise to refer to Student. (I don’t think you want to assign exercises to Teacher
  • Assign appropriate related_name to each of those relationships. (Perhaps “students” and “exercises”)

Now, to address this most recent topic, I’d agree that an O2M is sufficient at this point. It may end up being a little easier that way.

So to do that when you’re creating a single CustomUser model as in your later post, is that you create the ForeignKey field referencing the special name ‘self’. That allows you to create an FK relationship in a model to another instance of that same model.

Either the revised original method or your current method will work for you - it’s up to you to decide which you are more comfortable with.

hello ken,

thank you very much for your input!

of course in my juvenile manner i already stormed on head over heels and produced this model.py, based on the second approach:

from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils import timezone
import datetime
from django.urls import reverse, reverse_lazy

class CustomUser(AbstractUser):
    is_teacher = models.BooleanField('teacher status', default=False)

    def __str__(self):
        return self.username

class ExerciseManager(models.Manager):
    def create_exercise(self, title, teacher):
        exercise = self.create(title=title, teacher=teacher)
        exercise.save()
        return exercise

class Category(models.Model):
    teacher = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='teacher_categories',limit_choices_to={'is_teacher': True})
    title = models.CharField(max_length=100, unique=True)
    shuffle =  models.BooleanField('Shuffle', default=True)
    shuffled_at = models.DateTimeField(auto_now=True)
    timeout = models.PositiveSmallIntegerField('TimeOut', default='3')

    def get_absolute_url(self):
        return reverse('categories')

    def timed_out(self):
        now = timezone.now()
        return now - datetime.timedelta(days=self.timeout) >= self.shuffled_at

    def __str__(self):
        return self.title

class ExerciseManager(models.Manager):
    
    def get_queryset(self):
        return super(ExerciseManager, self).get_queryset().filter(id=1)


class Exercise(models.Model):
    teacher = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='teacher_exercises',limit_choices_to={'is_teacher': True})
    students = models.ManyToManyField(CustomUser, related_name='student_exercises', blank=True, limit_choices_to={'is_teacher': False})
    category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='category_exercises', null=True, blank=True)
    title = models.CharField(max_length=100)
    text = models.TextField(default="Noch kein Text...")
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    objects = models.Manager()
    ofthisstudent = ExerciseManager()

    def get_absolute_url(self):
        return reverse('category_detail', kwargs={'pk': self.category.id})

    def __str__(self):
        return self.title    

i will try to implement the ‘self’ FK asap, although already mindtwisting :slight_smile: but i need that student teacher relation to load only the related students and their related items from the db when a teacher is logged in.

its getting already complicated for me, even with such a minute project. thank god its not more models. but so far, everything seems to work (quite surprisingly) and sometimes i get a glimpse of why django is a powerful tool (when in right hands).

now i need to work on the login and most importantly and most cluelessly on the “exercise-editor”. a normal form wont suffice. some rich-text features are needed, and most importantly media-file-upload (to the cloud, preferably).

so tnx again, and very glad about this and any possible future help!

have a nice day

td

FWIW, I had a similar issue.

We were enhancing our Model and moving from a single “id” field to a proper FK to a child Model.

The old Model was like:

class MyModel(models.Model):
    # At this point simply knowing the "id" was enough
    child_id = models.IntegerField()

The new Model was like:

class MyModel(models.Model):
    # Need to retain for backwards compatibility until the new app is rolled out to all server instances
    # Then we can remove this field and keep the new
    child_id = models.IntegerField()
    # We needed more information for the "child" and so added a new Model
    child = models.ForeignKey(ChildModel, default=None, null=True, on_delete=models.CASCADE

The problem was that models.ForeignKey() added an attname of child_id for the FK.

This led to the clash error on child_id.

We worked around it by subclassing models.ForeignKey with:

class NonClashingForeignKey(models.ForeignKey):

    def get_attname(self):
        return "fk_%s_id" % self.name