iducam
iducam

Reputation: 79

Django AttributeError: <Object> needs to have a hvalue for field "id" before this many-to-many relationship can be used

My issue:

I'm trying to add some models to my database through the admin path on my django project, but this error keeps popping up when I try to create an instance of my model. The clean function exists to ensure that each Pokemon model can only have up to 2 types selected from a list of all possible types.

My code:

from django.db import models
from django.core.validators import MinValueValidator as min, MaxValueValidator as max
from django.core.exceptions import ValidationError


class PokeType(models.Model):
    poke_type = models.CharField(max_length=15)

    def __str__(self):
        return self.poke_type


#Current generation of games for gen_added field
gen = 8

class Pokemon(models.Model):
    poke_name = models.CharField(max_length=30)
    poke_type = models.ManyToManyField(PokeType, blank=True, null=True)
    evolves_from = False
    evolves_into = False
    gen_added = models.PositiveIntegerField(validators=[min(1), max(gen)])

     def clean(self):
         #Allow max of 2 poke_types to be selected
         if self.poke_type.count() > 2:
             raise ValidationError('A Pokemon has a maximum of two types.')


    class Meta:
        verbose_name_plural = 'Pokemon'
        abstract = True


class CustomPokemon(Pokemon):
    name = models.CharField(max_length=30)
    level = models.PositiveIntegerField(blank=True, null=True)

    def __str__(self):
        return self.name

What I (kind of) know:

Based on what I've seen from other people asking this question, I need to save the instance of my model before I can use the many-to-many relationship in the clean function. Also, I do not get the error when my clean function is commented out, so I've narrowed the issue down to that portion of the code.

What I've tried:

I thought that changing my if statement to check for the presence of the poke_type variable would help - i.e.

     def clean(self):
         #Allow max of 2 poke_types to be selected
         if self.poke_type and self.poke_type.count() > 2:
             raise ValidationError('A Pokemon has a maximum of two types.')

but the error is still raised. As I noted above, removing that function also allows me to proceed without raising that specific error, but breaks my ability to limit the max number of types selectable.

Next, I tried creating a save function to save the model and then give the model the poke_type attribute after that:

    def save(self):
        self.save()
        self.poke_type = models.ManyToManyField(PokeType, blank=True, null=True)

Is there a way to save the instance of the model as it's being added through the admin site or is that not possible?

Upvotes: 1

Views: 47

Answers (1)

Aayush Agrawal
Aayush Agrawal

Reputation: 1394

Make use of the m2m_changed signal instead

def poke_type_changed(sender, **kwargs):
    if kwargs['instance'].poke_type.count() > 2:
        raise ValidationError('A Pokemon has a maximum of two types.')


m2m_changed.connect(poke_type_changed, sender=Pokemon.poke_type.through)

Upvotes: 1

Related Questions