diff --git a/.gitignore b/.gitignore
index 96540b91b86f34830090127c634a32325544c05e..543740071faae3ece06627c568d09b220f1c357f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,7 @@
 .DS_Store
 __pycache__/
 *.egg-info/
-.env
+*.env
 .env_dev
 .idea/
 .vscode
@@ -19,3 +19,4 @@ notebooks/
 
 # Tests files
 .coverage
+.pytest_cache
diff --git a/backend/metagenedb/apps/catalog/factory/__init__.py b/backend/metagenedb/apps/catalog/factory/__init__.py
index 0bc64025b63e8f1466eb10721d165ec95de7c7b9..2621779eb9c22b7571e0b1dc3f0393e2eb058c39 100644
--- a/backend/metagenedb/apps/catalog/factory/__init__.py
+++ b/backend/metagenedb/apps/catalog/factory/__init__.py
@@ -1,2 +1,3 @@
 from .function import FunctionFactory  # noqa
+from .gene import GeneFactory  # noqa
 from .taxonomy import TaxonomyFactory  # noqa
diff --git a/backend/metagenedb/apps/catalog/factory/gene.py b/backend/metagenedb/apps/catalog/factory/gene.py
new file mode 100644
index 0000000000000000000000000000000000000000..ba0ca0faece9ed9d0680b30f0bdd4ffd964dbbf4
--- /dev/null
+++ b/backend/metagenedb/apps/catalog/factory/gene.py
@@ -0,0 +1,21 @@
+from factory import DjangoModelFactory, lazy_attribute
+from faker import Factory
+
+from metagenedb.apps.catalog import models
+
+faker = Factory.create()
+
+SELECTED_SOURCE = [i[0] for i in models.Function.SOURCE_CHOICES]
+
+
+class GeneFactory(DjangoModelFactory):
+    class Meta:
+        model = models.Gene
+
+    @lazy_attribute
+    def gene_id(self):
+        return str(faker.pyint())
+
+    @lazy_attribute
+    def length(self):
+        return str(faker.pyint())
diff --git a/backend/metagenedb/apps/catalog/models/__init__.py b/backend/metagenedb/apps/catalog/models/__init__.py
index 696898934e377dce77652053d8169a7c5aa8a77b..c80511a6f0f86e7711a7e6e9ded4a9f1a043b602 100644
--- a/backend/metagenedb/apps/catalog/models/__init__.py
+++ b/backend/metagenedb/apps/catalog/models/__init__.py
@@ -1,3 +1,3 @@
 from .function import Function, KeggOrthology  # noqa
-from .gene import Gene  # noqa
+from .gene import Gene, GeneFunction  # noqa
 from .taxonomy import Taxonomy  # noqa
diff --git a/backend/metagenedb/apps/catalog/serializers/gene.py b/backend/metagenedb/apps/catalog/serializers/gene.py
index 22e6094c8cfad37ce59b0f23a596a4cbfb8214bf..131c47edfe79a9dce774dd2599442b8e6406020c 100644
--- a/backend/metagenedb/apps/catalog/serializers/gene.py
+++ b/backend/metagenedb/apps/catalog/serializers/gene.py
@@ -3,8 +3,7 @@ import traceback
 
 from rest_framework import serializers
 from rest_framework.utils import model_meta
-from metagenedb.apps.catalog.models import Function, Gene, Taxonomy
-from metagenedb.apps.catalog.serializers import FunctionSerializer
+from metagenedb.apps.catalog.models import Function, Gene, GeneFunction, Taxonomy
 
 from .bulk_list import BulkListSerializer
 
@@ -16,15 +15,45 @@ class GeneListSerializer(BulkListSerializer):
     class Meta:
         model = Gene
 
+    def _generate_gene_function_mapping(self, values, genes):
+        """
+        Generate a list of GeneFunction pair to create relation between them
+        """
+        genes_dict = {gene.gene_id: gene for gene in genes}
+        mapping = []
+        for value in values:
+            for function in value['functions']:
+                mapping.append(GeneFunction(gene=genes_dict[value['gene_id']],
+                                            function=function))
+        return mapping
+
+    def _handle_functions(self, values):
+        """
+        :param values: each dictionnary has the 'functions' and 'gene_id' keys
+        :type values: LIST of DICT
+        """
+        # Get all Gene objects
+        gene_ids = [item['gene_id'] for item in values]
+        # Get all link with corresponding genes & Delete them
+        genes = Gene.objects.filter(gene_id__in=gene_ids)
+        GeneFunction.objects.filter(gene__in=genes).delete()
+        # Generate table for bulk_create of function <-> gene and create it
+        GeneFunction.objects.bulk_create(
+            self._generate_gene_function_mapping(values, genes)
+        )
+        print("hi")
+
     def create(self, validated_data):
         instances = super().create(validated_data)
         return instances
 
 
 class GeneSerializer(serializers.ModelSerializer):
-    functions = FunctionSerializer(
+    functions = serializers.SlugRelatedField(
+        queryset=Function.objects.all(),
+        slug_field='function_id',
         many=True,
-        required=False
+        required=False,
     )
     taxonomy = serializers.SlugRelatedField(
         queryset=Taxonomy.objects.all(),
@@ -47,12 +76,12 @@ class GeneSerializer(serializers.ModelSerializer):
     def _handle_functions(self, functions, instance):
         for function in functions:
             try:
-                function = Function.objects.get(function_id=function.get('function_id'))
+                function = Function.objects.get(function_id=function)
                 instance.functions.add(function)
                 instance.full_clean()
                 instance.save()
             except Function.DoesNotExist:
-                _LOGGER.warning(f"{function.get('function_id')} not found for {instance.gene_id}. Function ignored")
+                _LOGGER.warning("%s not found for %s. Function ignored", function, instance.gene_id)
 
     def create(self, validated_data):
         ModelClass = self.Meta.model
diff --git a/backend/metagenedb/apps/catalog/serializers/test_bulk_list.py b/backend/metagenedb/apps/catalog/serializers/test_bulk_list.py
index 10b0ec3f2b977b9eac18474c752da39e844f7969..b5e73770ee680d44f88a5e3c24109c98a2ce3b97 100644
--- a/backend/metagenedb/apps/catalog/serializers/test_bulk_list.py
+++ b/backend/metagenedb/apps/catalog/serializers/test_bulk_list.py
@@ -52,6 +52,29 @@ class TestExtractManyToMany(BaseTestBulkListSerializerMethods):
         self.assertDictEqual(tested_dict, expected_dict)
         self.assertNotEqual(ori_list, self.data)
 
+    def test_extract_many_to_many_two_fields(self):
+        self.info.relations = {
+            'field1': Mock(to_many=True),
+            'field2': Mock(to_many=True)
+        }
+        ori_list = deepcopy(self.data)
+        expected_dict = {
+            'keys': {'field1', 'field2'},
+            'values': {
+                'field1': [
+                    {'field1': 'value1', 'id': 'entry_1'},
+                    {'field1': 'value3', 'id': 'entry_2'}
+                ],
+                'field2': [
+                    {'field2': 'value2', 'id': 'entry_1'},
+                    {'field2': 'value4', 'id': 'entry_2'}
+                ]
+            }
+        }
+        tested_dict = self.bulk_list_serializer._extract_many_to_many(self.data, self.info, 'id')
+        self.assertDictEqual(tested_dict, expected_dict)
+        self.assertNotEqual(ori_list, self.data)
+
     def test_extract_no_many_to_many(self):
         self.info.relations = {
             'field1': Mock(to_many=False),
@@ -67,18 +90,18 @@ class TestExtractManyToMany(BaseTestBulkListSerializerMethods):
         self.assertListEqual(ori_list, self.data)
 
 
-class TestGetDbIndexFields(BaseTestBulkListSerializerMethods):
+class TestGetAllKeyFields(BaseTestBulkListSerializerMethods):
 
-    def test_get_db_index_fields(self):
+    def test_get_all_key_fields(self):
         expected_keys = ['field1', 'field2']
         tested_keys = self.bulk_list_serializer._get_all_key_fields(self.data)
         for key in expected_keys:
             self.assertIn(key, tested_keys)
 
 
-class TestGetKeyFields(BaseTestBulkListSerializerMethods):
+class TestGetDbIndexFields(BaseTestBulkListSerializerMethods):
 
-    def test_get_all_key_fields(self):
+    def test_get_db_index_fields(self):
         self.info.fields = {
             'field1': Mock(db_index=True),
             'field2': Mock(db_index=False)
@@ -87,7 +110,7 @@ class TestGetKeyFields(BaseTestBulkListSerializerMethods):
         tested_keys = self.bulk_list_serializer._get_db_index_fields(self.info)
         self.assertListEqual(tested_keys, expected_keys)
 
-    def test_get_all_key_fields_no_keys(self):
+    def test_get_db_index_fields_no_keys(self):
         self.info.fields = {
             'field1': Mock(db_index=False),
             'field2': Mock(db_index=False)
diff --git a/backend/metagenedb/apps/catalog/serializers/test_gene.py b/backend/metagenedb/apps/catalog/serializers/test_gene.py
new file mode 100644
index 0000000000000000000000000000000000000000..10e99b878e9d608749e926c2cb4e278e2fc6e013
--- /dev/null
+++ b/backend/metagenedb/apps/catalog/serializers/test_gene.py
@@ -0,0 +1,53 @@
+from unittest import TestCase
+
+from rest_framework.test import APITestCase
+
+from metagenedb.apps.catalog.factory import FunctionFactory, GeneFactory
+from metagenedb.apps.catalog.models import GeneFunction
+from metagenedb.apps.catalog.serializers.gene import GeneListSerializer
+
+
+class GeneListSerializerTest(GeneListSerializer):
+    """
+    overload to skip __init__() and just test the _extract_many_to_many method.
+    """
+
+    def __init__(self):
+        pass
+
+
+class TestGenerateGeneFunctionMapping(TestCase):
+
+    def test_generate_gene_function_mapping(self):
+        genes = [GeneFactory.build(gene_id='gene_1')]
+        values = [{
+            'gene_id': 'gene_1',
+            'functions': [FunctionFactory.build()]
+        }]
+        expected_object = GeneFunction(gene=genes[0], function=values[0]['functions'][0])
+        serializer = GeneListSerializerTest()
+        tested_object = serializer._generate_gene_function_mapping(values, genes)[0]
+        self.assertEqual(tested_object.gene, expected_object.gene)
+        self.assertEqual(tested_object.function, expected_object.function)
+
+
+class TestHandleFunctions(APITestCase):
+
+    def test_handle_functions(self):
+        """
+        @TODO function in the gene object is not found but the link is created
+        """
+        gene = GeneFactory.create()
+        function = FunctionFactory.create()
+        values = [{
+            'gene_id': gene.gene_id,
+            'functions': [function]
+        }]
+        self.assertNotEqual(gene.functions, [function])
+        self.assertEqual(GeneFunction.objects.all().count(), 0)
+        serializer = GeneListSerializerTest()
+        serializer._handle_functions(values)
+        link = GeneFunction.objects.all()
+        self.assertEqual(link.count(), 1)
+        self.assertEqual(link[0].gene.gene_id, gene.gene_id)
+        self.assertEqual(link[0].function.function_id, function.function_id)