Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error when using "through" intermediate model #187

Open
eudaldt opened this issue Aug 9, 2021 · 3 comments
Open

Error when using "through" intermediate model #187

eudaldt opened this issue Aug 9, 2021 · 3 comments

Comments

@eudaldt
Copy link

eudaldt commented Aug 9, 2021

I think this was asked before but is it possible to define my own intermediate model with sortedm2m using through? In my case I have this model:

class Sample(models.Model):
   id_sample = models.AutoField(primary_key=True)
   name = models.CharField(unique=True, max_length=20)
   indexes = SortedManyToManyField(Index, through='SamplePoolIndexCand', blank=True)
   pools = SortedManyToManyField(Index, through='SamplePoolIndexCand', blank=True)
   gene_lists = SortedManyToManyField(Index, through='SamplePoolIndexCand', blank=True)

The intermediate table:

class SamplePoolIndexCand(models.Model):
    sample_id = models.ForeignKey(Sample, null=True, blank=True, on_delete=models.CASCADE, db_column='id_sample', 
    verbose_name='Mostra')
    pool_id = models.ForeignKey(Pool, null=True, blank=True, on_delete=models.CASCADE, db_column='id_pool', 
    verbose_name='Pool')    
    index_id = models.ForeignKey(Index, null=True, blank=True, on_delete=models.CASCADE, 
    db_column='id_index', verbose_name='Índex')
    gene_cand_list_id = models.ForeignKey(GeneCandList, null=True, blank=True, on_delete=models.CASCADE, 
    db_column='id_gene_cand_list', verbose_name='Llista de gens candidats')

I got this error message:

AssertionError: The model is used as an intermediate model by '<class 'myproject.models.SamplePoolIndexCand'>' but has no defined '_sort_field_name' attribute

How can I solve this?

@swizzlevixen
Copy link

Seems this was discussed in this older issue: #77

Unfortunately I'm not proficient enough in Django to give any advice. I have hit the same problem, trying to upgrade an old project that was working fine with a "through" model with django <= 2.2.x and django-sortedm2m <=1.5.x. I'm upgrading things piece by piece through to the latest versions of Django, Python, etc., but once I upgrade to Django 3.0, I have to upgrade django-sortedm2m to ==3.0.0 for Django 3 support… and then my through model is now broken:

❯ python -Wall manage.py runserver --settings=cinedex.settings.local
Performing system checks...

Exception in thread django-main-thread:
Traceback (most recent call last):
  File "/usr/local/Cellar/[email protected]/3.9.16/Frameworks/Python.framework/Versions/3.9/lib/python3.9/threading.py", line 980, in _bootstrap_inner
    self.run()
  File "/usr/local/Cellar/[email protected]/3.9.16/Frameworks/Python.framework/Versions/3.9/lib/python3.9/threading.py", line 917, in run
    self._target(*self._args, **self._kwargs)
  File "/Users/mboszko/.virtualenvs/cinedex-wZ1JZ5_O/lib/python3.9/site-packages/django/utils/autoreload.py", line 53, in wrapper
    fn(*args, **kwargs)
  File "/Users/mboszko/.virtualenvs/cinedex-wZ1JZ5_O/lib/python3.9/site-packages/django/core/management/commands/runserver.py", line 117, in inner_run
    self.check(display_num_errors=True)
  File "/Users/mboszko/.virtualenvs/cinedex-wZ1JZ5_O/lib/python3.9/site-packages/django/core/management/base.py", line 392, in check
    all_issues = self._run_checks(
  File "/Users/mboszko/.virtualenvs/cinedex-wZ1JZ5_O/lib/python3.9/site-packages/django/core/management/base.py", line 382, in _run_checks
    return checks.run_checks(**kwargs)
  File "/Users/mboszko/.virtualenvs/cinedex-wZ1JZ5_O/lib/python3.9/site-packages/django/core/checks/registry.py", line 72, in run_checks
    new_errors = check(app_configs=app_configs)
  File "/Users/mboszko/.virtualenvs/cinedex-wZ1JZ5_O/lib/python3.9/site-packages/django/core/checks/model_checks.py", line 34, in check_all_models
    errors.extend(model.check(**kwargs))
  File "/Users/mboszko/.virtualenvs/cinedex-wZ1JZ5_O/lib/python3.9/site-packages/django/db/models/base.py", line 1257, in check
    *cls._check_fields(**kwargs),
  File "/Users/mboszko/.virtualenvs/cinedex-wZ1JZ5_O/lib/python3.9/site-packages/django/db/models/base.py", line 1338, in _check_fields
    errors.extend(field.check(from_model=cls, **kwargs))
  File "/Users/mboszko/.virtualenvs/cinedex-wZ1JZ5_O/lib/python3.9/site-packages/sortedm2m/fields.py", line 204, in check
    self._check_through_sortedm2m()
  File "/Users/mboszko/.virtualenvs/cinedex-wZ1JZ5_O/lib/python3.9/site-packages/sortedm2m/fields.py", line 213, in _check_through_sortedm2m
    assert hasattr(rel.through, '_sort_field_name'), (
AssertionError: The model is used as an intermediate model by '<class 'theoptical.models.EpisodeGuest'>' but has no defined '_sort_field_name' attribute

If someone might be able to explain how I could fix this, I would every grateful. Otherwise I fear I may have to rebuild my django app from scratch, because there are so may moving parts to upgrade.

@merwok
Copy link

merwok commented Dec 27, 2022

Have you tried setting an attribute _sort_field_name on the intermediate model class?

@swizzlevixen
Copy link

swizzlevixen commented Dec 28, 2022

@merwok Forgive me, I haven't touched this code (or really, Django at all) in about 7–8 years, and it's currently a foreign country to me.

Yes, it looks like if I add the _sort_field_name attribute inside the EpisodeGuest class, that silences the error, but I'm not sure it's operating correctly. Would I then need to add a new field to that intermediate class matching the sort_value name? It doesn't seem to say in the Readme here what kind of field sort_value should be. I'm going on the assumption based on this line here:

sort_field = models.IntegerField(default=0)

Should it be like this in the intermediate model class?

class EpisodeGuest(models.Model):
    _sort_field_name = "sort_value"
    sort_value = models.IntegerField(default=0)

UPDATE: I tried this in my local development environment, made migrations, migrated the database, and even after adding a new EpisodeGuest to an episode in the admin, an inspection of the local database shows all sort_value values for the episode guests are still 0, so I'm not sure that it's actually using this value at all. 🤔

To provide more context in my project: theoptical.models consists of several classes linked to SortedManyToManyFields, with the EpisodeGuest class being one of them. I'll try to present a condensed version of the module with the relevant bits:

# coding=utf-8

from cinefex.models import Entity
from django.db import models
from sortedm2m.fields import SortedManyToManyField
[...]

[...]

class Episode(models.Model):
    # An episode of a podcast.

    [...]

    guests = SortedManyToManyField(
        Entity, through="EpisodeGuest", related_name="episode_guests", blank=True
    )  # Person

    def __repr__(self):
        return self.designation

    def __str__(self):
        return self.designation

class EpisodeGuest(models.Model):
    # Fixing sort field error for `django-sortedm2m` >= 1.5.0
    _sort_field_name = "sort_value"
    # extend these through…
    created = models.DateTimeField(auto_now_add=True, auto_now=False)
    updated = models.DateTimeField(auto_now_add=False, auto_now=True)
    episode = models.ForeignKey(Episode, on_delete=models.CASCADE)
    guest = models.ForeignKey(Entity, on_delete=models.CASCADE)
    guest_blurb = models.TextField(help_text="Markdown blurb.", blank=True)
    guest_links = models.ManyToManyField(ExternalLink, blank=True)
    # Special Guests get big headshots in the center column.
    is_special = models.BooleanField(
        default=False, help_text="Usually only one per episode."
    )

    def __repr__(self):
        return f"{self.episode.designation}: {self.guest.name}"

    def __str__(self):
        return f"{self.episode.designation}: {self.guest.name}"

Thanks for taking a look!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants