Personalizar la tabla intermedia de una relación ManyToMany Django - Through

Personalizar la tabla intermedia de una relación ManyToMany Django - Through

En este artículo resolvemos el error: they are not compatible types (you cannot alter to or from M2M fields, or add or remove through= on M2M fields)

Hola, En el canal de youtube vimos un video donde explicamos cómo personalizamos la tabla intermedia que Django crea de forma automática en una relación ManyToMany, esto particularmente cuando necesitamos que esa tabla tenga algún campo extra, fuera de los ForeingKey de ambas tablas relacionadas por ManyToMnyField, 

 

Si seguimos el ejemplo del vídeo, el código en los models crearía de esta forma:

from django.db import models

class Habilidades(models.Model):

hablidad = models.CharField('Habilidad', max_length=50)

class meta:

verbose_name = 'Habilidad'

verbose_name_plural = 'Habilidades Empleados'

def __str__(self):

return str(self.id) + '-' + self.hablidad
 

class Empleado(models.Model):

""" Modelo para tabla empleado """

JOB_CHOICES = (

('0', 'CONTADOR'),

('1', 'ADMINISTRADOR'),

('2', 'ECONOMISTA'),

('3', 'OTRO'),

)

first_name = models.CharField('Nombres', max_length=60)

last_name = models.CharField('apellidos', max_length=60)

full_name = models.CharField(

'Nombres completos',

max_length=120,

blank=True

)

job = models.CharField(

'Trabajo',

max_length=1,

choices=JOB_CHOICES

)

habilidades = models.ManyToManyField(

Habilidades,

through='HabilidadEmpleado',

blank=True,

)

class Meta:

verbose_name = 'Mi Empleado'

verbose_name_plural = 'Empleados de la empresa'

ordering = ['-first_name', 'last_name']

def __str__(self):

return str(self.id) + '-' + self.first_name + '-' + self.last_name


class HabilidadEmpleado(models.Model):

habilidades = models.ForeignKey(

Habilidades,

on_delete=models.CASCADE,

blank=True, null=True

)

empleado = models.ForeignKey(

Empleado,

on_delete=models.CASCADE,

blank=True, null=True

)

description = models.CharField(

'descripcion',

max_length=100,

blank=True,

)

class meta:

verbose_name = 'Habilidad Empleado'

verbose_name_plural = 'Habilidades Empleados'

def __str__(self):

return str(self.id) + '-' + self.empleado.first_name

 

Hacemos la migraciones y todo funciona muy bien, sin embargo qué pasa cuando ya tenemos datos y nuestro proyecto ya está en marcha? pues hacer la migración termina siendo un dolor de cabeza, principalmente por este error: 

 

they are not compatible types (you cannot alter to or from M2M fields, or add or remove through= on M2M fields)

 

¿Como soluciono este error?

En principio el error surge por que ya existe una tabla intermedia que fue generada por una relación m2m, cuando hacemos el cambio nos damos cuenta que basicamente estamos creando una nueva tabla y obligando a Django a trabajar con esta nueva tabla, sobre que ya tiene un tabla con la que está trabajando y esto es un conflicto en la ORM de Django, para solucionar ello haremos lo siguiente: 

1 ) Debemos obligatoriamente crear una copia de seguridad de la BD, en el gestor de BD que estemos manejando y borrar el ultimo archivo generado por los makemigrations que causo el error. 

 

2) Ahora comentamos la línea MenyToMany y Comentamos la Nueva tabla creada. 

class Empleado(models.Model):

""" Modelo para tabla empleado """

 

JOB_CHOICES = (

('0', 'CONTADOR'),

('1', 'ADMINISTRADOR'),

('2', 'ECONOMISTA'),

('3', 'OTRO'),

)

first_name = models.CharField('Nombres', max_length=60)

last_name = models.CharField('apellidos', max_length=60)

full_name = models.CharField(

'Nombres completos',

max_length=120,

blank=True

)

job = models.CharField(

'Trabajo',

max_length=1,

choices=JOB_CHOICES

)

#habilidades = models.ManyToManyField(

#Habilidades,

#through='HabilidadEmpleado',

#blank=True,

#)

 

class Meta:

verbose_name = 'Mi Empleado'

verbose_name_plural = 'Empleados de la empresa'

ordering = ['-first_name', 'last_name']


 

def __str__(self):

return str(self.id) + '-' + self.first_name + '-' + self.last_name


# class HabilidadEmpleado(models.Model):

#habilidades = models.ForeignKey(

#Habilidades,

#on_delete=models.CASCADE,

#blank=True, null=True

#)

#empleado = models.ForeignKey(

#Empleado,

#on_delete=models.CASCADE,

#blank=True, null=True

#)

#description = models.CharField(

#'descripcion',

#max_length=100,

#blank=True,

#)

#class meta:

#verbose_name = 'Habilidad Empleado'

#verbose_name_plural = 'Habilidades Empleados'

#def __str__(self):

#return str(self.id) + '-' + self.empleado.first_name

3) Ahora realizamos el makemigrayions y el migrate, esto habrá borrado solamente el campo M2M de la base de datos, y todo los registros ahora ya no tienen esa información.

 

 4) Ahora descomentamos M2M y la nueva tabla, pero también le agregamos db_table, para que ahora esta nueva tabla se cree con el mismo nombre que tenía la tabla generada automáticamente antes por Django. 

class Empleado(models.Model):

""" Modelo para tabla empleado """

 

JOB_CHOICES = (

('0', 'CONTADOR'),

('1', 'ADMINISTRADOR'),

('2', 'ECONOMISTA'),

('3', 'OTRO'),

)

first_name = models.CharField('Nombres', max_length=60)

last_name = models.CharField('apellidos', max_length=60)

full_name = models.CharField(

'Nombres completos',

max_length=120,

blank=True

)

job = models.CharField(

'Trabajo',

max_length=1,

choices=JOB_CHOICES

)

habilidades = models.ManyToManyField(

Habilidades,

through='HabilidadEmpleado',

blank=True,

)

 

class Meta:

verbose_name = 'Mi Empleado'

verbose_name_plural = 'Empleados de la empresa'

ordering = ['-first_name', 'last_name']


 

def __str__(self):

return str(self.id) + '-' + self.first_name + '-' + self.last_name


class HabilidadEmpleado(models.Model):

habilidades = models.ForeignKey(

Habilidades,

on_delete=models.CASCADE,

blank=True, null=True

)

empleado = models.ForeignKey(

Empleado,

on_delete=models.CASCADE,

blank=True, null=True

)

description = models.CharField(

'descripcion',

max_length=100,

blank=True,

)

class meta:

db_table = 'empleado_empleado_habilidades'

verbose_name = 'Habilidad Empleado'

verbose_name_plural = 'Habilidades Empleados'

def __str__(self):

return str(self.id) + '-' + self.empleado.first_name

5) volvemos a hacer las migraciones

 

6) Ahora tomamos la copia de seguridad que mencionamos generar en el paso 1 y volcamos esa copia de seguridad y todos los datos del campo M2M se habran re establecido.

 

Si trabaja con Postgres puede ver aquí un articulo de como creamos una copia de seguridad con Psql. Link Aqui

Déjanos tu correo y entérate de mas de estos artículos: