0

I'm creating an api that will register users related to a customer and also to an organizational group, but I'm having problems registering the user's group.

whenever I try to register I get the following error:

"Direct assignment to the forward side of a many-to-many set is prohibited. Use grupo.set() instead."

Could you help me understand what I'm doing wrong?

my models.py

class Cliente(models.Model):
    nome = models.CharField(max_length=100, unique=True, null=True)
    documento = models.CharField(max_length=30, blank=True, default='00.000.000/0001-00')
    logo = models.CharField(max_length=255, blank=True, null=True)
    data_de_criacao = models.DateField(default=date.today)
    cliente_ativo = models.BooleanField(default=True)
    background_img = models.CharField(max_length=255, blank=True, null=True)
    cor_primaria = models.CharField(max_length=10, blank=True, null=True)
    cor_secundaria = models.CharField(max_length=10, blank=True, null=True)


    def __str__(self):
        return self.nome

class GrupoOrganizacional(models.Model):
    id_grupo = models.BigAutoField(primary_key=True, unique=True) 
    nome_grupo = models.CharField(max_length=100, blank=False, null=False)
    cliente = models.ForeignKey(Cliente, blank=True, null=True ,on_delete=models.CASCADE)

    def __str__(self):
        return self.nome_grupo
    
class Usuario(AbstractUser):
   
    objects = CustomUserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    cliente = models.ForeignKey(Cliente, blank=True, null=True ,on_delete=models.CASCADE)
    first_name = models.CharField(max_length=100, blank=False, null=False)
    last_name = models.CharField(max_length=100, blank=False, null=False)
    username = models.CharField(max_length=50, blank=True, null=True)
    email = models.EmailField(max_length=100, blank=False, null=False, unique=True, error_messages={'unique': "O email cadastrado já existe."})
    password = models.CharField(max_length=100, blank=False, null=False)
    usuario_ativo = models.BooleanField(default=True)
    grupo = models.ManyToManyField(GrupoOrganizacional, related_name='grupos')
    
    is_staff = models.BooleanField(default=True)
    is_superuser = models.BooleanField(default=False)

    def __str__(self):
        return "%s (%s)" %(self.first_name, self.cliente)

my views.py

class GruposViewSet(viewsets.ModelViewSet):
    ''' Lista todos os grupos cadastrados '''
    queryset = GrupoOrganizacional.objects.all()
    serializer_class = ListaGruposSerializer

    permission_classes = [IsAuthenticated, IsAdminUser]
    
class UsuariosViewSet(viewsets.ModelViewSet):
    ''' Lista todos os usuarios cadastrados'''
    queryset = Usuario.objects.all()
    serializer_class = UsuarioSerializer

    permission_classes = [IsAuthenticated, IsAdminUser]
    

my serializers.py

class GruposDoUsuarioSerializer(PrimaryKeyRelatedField ,serializers.ModelSerializer):
    '''Serializer para listar todos os grupos organizacionais cadastrados'''
    #grupo_id = serializers.PrimaryKeyRelatedField( many=True, read_only=True )

    class Meta:
        model = GrupoOrganizacional
        fields = ['id_grupo']


class UsuarioSerializer(serializers.ModelSerializer):
    '''Serializer para listar, criar e atualizar os usuarios'''
    
    grupo = GruposDoUsuarioSerializer(many=True, queryset=GrupoOrganizacional.objects.all()) # Quando usado, envia uma lista não suportada

    password = serializers.CharField(
        style={'input_type': 'password'},
        write_only=True,
        label="Senha"
    )

    password_confirm = serializers.CharField(
        style={'input_type': 'password'},
        write_only=True,
        label="Confirme a senha"
    )

    is_staff = serializers.BooleanField(
        label="Membro da Equipe",
        help_text="Indica que usuário consegue acessar o site de administração."
    )

    is_superuser = serializers.BooleanField(
        label="SuperUsuário",
        help_text="Indica que este usuário tem todas as permissões sem atribuí-las explicitamente."
    )

    class Meta:
        model = Usuario
        fields = ('id','cliente', 'first_name', 'last_name', 'username', 'email', 
        'password', 'password_confirm', 'usuario_ativo', 'grupo', 'is_staff', 'is_superuser')
        extra_kwargs = {'password': {'write_only': True}}

    def create(self, validated_data):
        '''Permite caradastrar e atualizar contas'''
        usuario = Usuario.objects.create(**validated_data)
        grupo_data = validated_data.get('id_grupo')
        password = self.validated_data.get('password')
        password_confirm = self.validated_data.get('password_confirm')

        for grupo_data in grupo_data:
            GrupoOrganizacional.objects.create(usuario=usuario, **grupo_data)
        if password != password_confirm:
            raise serializers.ValidationError({'password': 'As senhas não são iguais.'})
        
        #usuario.grupo.set(usuario)
        #usuario.set_grupo(grupo)

        usuario.set_password(password)
        usuario.save()
        
        
        return usuario

I had no problems with the customer registration, only in the group registration.

I've already tried to change my code based on the DRF documentation and some doubts from here, I realized that the problem is probably in the serializer.py, but I still can't understand what is causing the error

some references of what I tried to do:

At some point the groups stopped showing up on the api page, so I used this reference to solve the problem:

EDIT: I was able to solve all the problems by replacing all the valid answer data with a for:

for grupo in grupo_data:
            usuario.grupo.add(grupo)

1 Answers1

0

Try something like:

grupo_organizacional_list = []
for grupo_data in grupo_data:
    grupo_organizacional_list.append(GrupoOrganizacional(**grupo_data))

grupo_organizacional_list = GrupoOrganizacional.bulk_create(grupo_organizacional_list)
usario.grupo.set(*grupo_organizacional_list)

EDIT: With the traceback, the error seems to come from the first line of the create function. Try something like this:

    def create(self, validated_data):
        '''Permite caradastrar e atualizar contas'''
        grupo_data = validated_data.pop('grupo')
        usuario = Usuario.objects.create(**validated_data)

Removing the grupo field from the validated data before the creation occurred should prevent django from throwing the error

Alombaros
  • 1,280
  • 1
  • 2
  • 17
  • I tried to add this code snippet in my "def create(self, validated_data):" but the error keeps happening :/ – Rafael Vinicius Nov 07 '22 at 00:12
  • Could you include the trace ? – Alombaros Nov 07 '22 at 06:54
  • the text got too big to fit in the comment, so I put the new code and traceback https://gist.github.com/orafaelvinicius/c9ee68e8f7e57c8f6363ec6a5744a5f9 – Rafael Vinicius Nov 07 '22 at 12:06
  • Tanks for help, @Alombaros! Now i have a new error about "Usuario() got unexpected keyword arguments: 'password_confirm'". I need open a new question for this? (tryed move a code snippet "usuario = Usuario.objects.create(**validated_data)" for after variable "password_confirm" and change method for "pop", but not functioned) – Rafael Vinicius Nov 07 '22 at 13:11
  • If you do not manage to solve this on your own, yes open a new question. But it seems to be a similar issue. – Alombaros Nov 07 '22 at 14:03