gene.py 5.54 KB
Newer Older
1
2
3
import logging
import traceback

4
from rest_framework import serializers
5
from rest_framework.utils import model_meta
6
from metagenedb.apps.catalog.models import Function, Gene, GeneFunction, Source, Taxonomy
7

8
from .asymetricslugrelatedfield import AsymetricSlugRelatedField
9
from .bulk_list import BulkListSerializer
10
from .function import FunctionSerializer
11
from .source import SourceSerializer
12
13
from .taxonomy import SimpleTaxonomySerializer

14

15
16
_LOGGER = logging.getLogger(__name__)

17

18
19
20
21
22
class GeneListSerializer(BulkListSerializer):

    class Meta:
        model = Gene

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
    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)
        )

50
51
52
53
    def create(self, validated_data):
        instances = super().create(validated_data)
        return instances

54

55
class GeneSerializer(serializers.ModelSerializer):
56
57
    functions = AsymetricSlugRelatedField.from_serializer(
        FunctionSerializer,
58
59
        queryset=Function.objects.all(),
        slug_field='function_id',
60
        many=True,
61
        required=False,
62
    )
63
64
    taxonomy = AsymetricSlugRelatedField.from_serializer(
        SimpleTaxonomySerializer,
65
66
67
68
        queryset=Taxonomy.objects.all(),
        slug_field='tax_id',
        required=False,
    )
69
70
71
72
73
74
    source = AsymetricSlugRelatedField.from_serializer(
        SourceSerializer,
        queryset=Source.objects.all(),
        slug_field='name',
        required=False,
    )
75
76
77

    class Meta:
        model = Gene
78
        list_serializer_class = GeneListSerializer
Kenzo-Hugo Hillion's avatar
Kenzo-Hugo Hillion committed
79
        fields = ('gene_id', 'name', 'length', 'functions', 'taxonomy', 'sequence', 'source')
80

81
82
83
84
85
86
87
    def _extract_many_to_many(self, validated_data, info):
        many_to_many = {}
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in validated_data):
                many_to_many[field_name] = validated_data.pop(field_name)
        return many_to_many

88
89
90
    def _handle_functions(self, functions, instance):
        for function in functions:
            try:
91
                function = Function.objects.get(function_id=function)
92
93
94
95
                instance.functions.add(function)
                instance.full_clean()
                instance.save()
            except Function.DoesNotExist:
96
                _LOGGER.warning("%s not found for %s. Function ignored", function, instance.gene_id)
97
98
99
100

    def create(self, validated_data):
        ModelClass = self.Meta.model
        info = model_meta.get_field_info(ModelClass)
101
102
        # Remove many-to-many relationships from validated_data.
        many_to_many = self._extract_many_to_many(validated_data, info)
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125

        try:
            instance = ModelClass._default_manager.create(**validated_data)
        except TypeError:
            tb = traceback.format_exc()
            msg = (
                'Got a `TypeError` when calling `%s.%s.create()`. '
                'This may be because you have a writable field on the '
                'serializer class that is not a valid argument to '
                '`%s.%s.create()`. You may need to make the field '
                'read-only, or override the %s.create() method to handle '
                'this correctly.\nOriginal exception was:\n %s' %
                (
                    ModelClass.__name__,
                    ModelClass._default_manager.name,
                    ModelClass.__name__,
                    ModelClass._default_manager.name,
                    self.__class__.__name__,
                    tb
                )
            )
            raise TypeError(msg)

126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
        # Link existing many-to-many relationships after the instance is created.
        if many_to_many:
            for field_name, value in many_to_many.items():
                getattr(self, f'_handle_{field_name}', None)(value, instance)
        return instance

    def update(self, instance, validated_data):
        ModelClass = self.Meta.model
        info = model_meta.get_field_info(ModelClass)
        # Remove many-to-many relationships from validated_data.
        many_to_many = self._extract_many_to_many(validated_data, info)

        for attr, value in validated_data.items():
            if attr in info.relations and info.relations[attr].to_many:
                field = getattr(instance, attr)
                field.set(value)
            else:
                setattr(instance, attr, value)

        # Link existing many-to-many relationships.
146
147
148
        if many_to_many:
            for field_name, value in many_to_many.items():
                getattr(self, f'_handle_{field_name}', None)(value, instance)
149
150
        instance.save()

151
        return instance