mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-07-28 00:49:49 +03:00
Add LazyLoadModelSerializer
This commit is contained in:
parent
a1b35bb44b
commit
56985d3e1f
|
@ -16,6 +16,7 @@ import traceback
|
||||||
from collections import OrderedDict, defaultdict
|
from collections import OrderedDict, defaultdict
|
||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
|
|
||||||
|
from django.apps import apps
|
||||||
from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured
|
from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured
|
||||||
from django.core.exceptions import ValidationError as DjangoValidationError
|
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
@ -1014,6 +1015,27 @@ class ModelSerializer(Serializer):
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def check_meta(cls):
|
||||||
|
"""
|
||||||
|
Check presence of Meta and Meta.model and test whether Meta.model is
|
||||||
|
not abstract.
|
||||||
|
"""
|
||||||
|
assert hasattr(cls, 'Meta'), (
|
||||||
|
'Class {serializer_class} missing "Meta" attribute'.format(
|
||||||
|
serializer_class=cls.__class__.__name__
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assert hasattr(cls.Meta, 'model'), (
|
||||||
|
'Class {serializer_class} missing "Meta.model" attribute'.format(
|
||||||
|
serializer_class=cls.__class__.__name__
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if model_meta.is_abstract_model(cls.Meta.model):
|
||||||
|
raise ValueError(
|
||||||
|
'Cannot use ModelSerializer with Abstract Models.'
|
||||||
|
)
|
||||||
|
|
||||||
# Determine the fields to apply...
|
# Determine the fields to apply...
|
||||||
|
|
||||||
def get_fields(self):
|
def get_fields(self):
|
||||||
|
@ -1024,20 +1046,7 @@ class ModelSerializer(Serializer):
|
||||||
if self.url_field_name is None:
|
if self.url_field_name is None:
|
||||||
self.url_field_name = api_settings.URL_FIELD_NAME
|
self.url_field_name = api_settings.URL_FIELD_NAME
|
||||||
|
|
||||||
assert hasattr(self, 'Meta'), (
|
self.check_meta()
|
||||||
'Class {serializer_class} missing "Meta" attribute'.format(
|
|
||||||
serializer_class=self.__class__.__name__
|
|
||||||
)
|
|
||||||
)
|
|
||||||
assert hasattr(self.Meta, 'model'), (
|
|
||||||
'Class {serializer_class} missing "Meta.model" attribute'.format(
|
|
||||||
serializer_class=self.__class__.__name__
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if model_meta.is_abstract_model(self.Meta.model):
|
|
||||||
raise ValueError(
|
|
||||||
'Cannot use ModelSerializer with Abstract Models.'
|
|
||||||
)
|
|
||||||
|
|
||||||
declared_fields = copy.deepcopy(self._declared_fields)
|
declared_fields = copy.deepcopy(self._declared_fields)
|
||||||
model = getattr(self.Meta, 'model')
|
model = getattr(self.Meta, 'model')
|
||||||
|
@ -1664,3 +1673,41 @@ class HyperlinkedModelSerializer(ModelSerializer):
|
||||||
field_kwargs = get_nested_relation_kwargs(relation_info)
|
field_kwargs = get_nested_relation_kwargs(relation_info)
|
||||||
|
|
||||||
return field_class, field_kwargs
|
return field_class, field_kwargs
|
||||||
|
|
||||||
|
|
||||||
|
class LazyLoadModelSerializer(ModelSerializer):
|
||||||
|
"""
|
||||||
|
Lazy loads the model from django.apps.apps during instance creation.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_model(cls):
|
||||||
|
"""
|
||||||
|
Splits the app_label and model_name from Meta.model and fetches model
|
||||||
|
from model registry.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
app_label, model_name = cls.Meta.model.split(".")
|
||||||
|
except ValueError:
|
||||||
|
raise ValueError(
|
||||||
|
"Please provide the app_label and model_name in dot notation."
|
||||||
|
)
|
||||||
|
return apps.get_model(app_label, model_name=model_name)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load_model(cls):
|
||||||
|
"""
|
||||||
|
Loads the model and sets it.
|
||||||
|
"""
|
||||||
|
cls.check_meta()
|
||||||
|
if not isinstance(cls.Meta.model, str):
|
||||||
|
return
|
||||||
|
|
||||||
|
cls.Meta.model = cls.get_model()
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Overridden to load the model.
|
||||||
|
"""
|
||||||
|
cls.load_model()
|
||||||
|
return super().__new__(cls, *args, **kwargs)
|
||||||
|
|
|
@ -6,6 +6,7 @@ from collections import ChainMap
|
||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from django.apps import apps
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from rest_framework import exceptions, fields, relations, serializers
|
from rest_framework import exceptions, fields, relations, serializers
|
||||||
|
@ -669,6 +670,11 @@ class TestDeclaredFieldInheritance:
|
||||||
assert len(Child._declared_fields) == 1
|
assert len(Child._declared_fields) == 1
|
||||||
assert len(Grandchild._declared_fields) == 1
|
assert len(Grandchild._declared_fields) == 1
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def deregister_model(model_class):
|
||||||
|
app_models = apps.all_models[model_class._meta.app_label]
|
||||||
|
del app_models[model_class._meta.model_name]
|
||||||
|
|
||||||
def test_meta_field_disabling(self):
|
def test_meta_field_disabling(self):
|
||||||
# Declaratively setting a field on a child class will *not* prevent
|
# Declaratively setting a field on a child class will *not* prevent
|
||||||
# the ModelSerializer from generating a default field.
|
# the ModelSerializer from generating a default field.
|
||||||
|
@ -691,6 +697,24 @@ class TestDeclaredFieldInheritance:
|
||||||
assert len(Child().get_fields()) == 2
|
assert len(Child().get_fields()) == 2
|
||||||
assert len(Grandchild().get_fields()) == 2
|
assert len(Grandchild().get_fields()) == 2
|
||||||
|
|
||||||
|
self.deregister_model(MyModel)
|
||||||
|
|
||||||
|
def test_lazy_load_model_serializer(self):
|
||||||
|
class MyModel(models.Model):
|
||||||
|
f1 = models.CharField(max_length=10)
|
||||||
|
f2 = models.CharField(max_length=10)
|
||||||
|
|
||||||
|
model_string = f"{MyModel._meta.app_label}.{MyModel._meta.model_name}"
|
||||||
|
|
||||||
|
class MyModelSerializer(serializers.LazyLoadModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = model_string
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
assert len(MyModelSerializer().get_fields()) == 3
|
||||||
|
|
||||||
|
self.deregister_model(MyModel)
|
||||||
|
|
||||||
def test_multiple_inheritance(self):
|
def test_multiple_inheritance(self):
|
||||||
class A(serializers.Serializer):
|
class A(serializers.Serializer):
|
||||||
field = serializers.CharField()
|
field = serializers.CharField()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user