I am looking for a way to get a serializer from a model class. This is so that I can easily serialize model data, without having to harcode the serializer name and I figured something like this would do:
#mymodels.py
from django.db import models
import myserializers
class Model(models.Model):
name = models.CharField(max_length=50)
serializer = serializers.Serializer
#myserializers.py
from rest_framework import serializers
import mymodels
class Serializer(serializers.ModelSerializer):
class Meta:
model = mymodels.Model
fields = ('field1', 'field2')
The model is for an intermediary relationship, and I want a function that will act something like this:
def serialize(to_serialize):
return type(to_serialize).serializer(to_serialize).data
But this raises a AttributeErrordue to import errors. Am I going about this the completely wrong way? Is there an easier way of doing this, or is there a way to make this work somewhat like the above?
Thanks in advance.
Try this way may be work for you:
class Model(models.Model):
name = models.CharField(max_length=50)
serializer = serializers.Serializer
class Meta:
serializer_class = SerailizerClassName
In this code serializer set in model
now use the serializer in method
def serialize(model_object):
return model_object.__class__.Meta.serializer_class(model_object).data
Related
I created a serializer and an API endpoint so I can retrieve some data from a Django DB in my React app but getting this error message:
AttributeError: 'ProgrammingChallengesView' object has no attribute 'get'
Here is my models.py:
#creating programming challenges
class ProgrammingChallenges(models.Model):
challenge_id = models.AutoField(primary_key=True)
challenge_name = models.CharField(max_length=200)
challenge_description = models.TextField()
challenge_expectations = models.TextField()
my serializer:
from accounts.models import ProgrammingChallenges
...
class ProgrammingChallengesView(serializers.ModelSerializer):
class Meta:
model = ProgrammingChallenges
fields = '__all__'
and my urls.py:
path('api/programming_challenges/', ProgrammingChallengesView, name='programming_challenges'),
Thanks to the comments; I clearly didn't understand that a serializer only transforms my data to make it available through an API. I still had to create a view for my API's endpoint.
I opted to create a ReadOnlyModelView because I only want to GET data from this endpoint.
Here is what I wrote in my views:
class ProgrammingChallengesView(ReadOnlyModelViewSet):
serializer_class = ProgrammingChallengesSerializer
queryset = ProgrammingChallenges.objects.all()
#action(detail=False)
def get_list(self, request):
pass
and in my urls.py:
path('api/programming_challenges/', ProgrammingChallengesView.as_view({'get':'list'}), name='programming_challenges'),
I think you shouldn't hurry read the docs again. You are trying to use serializers as views.
Models - are representation of db tables as class.
Serializer serializes the data to json.
View accepts the reqeust from client and returns a Response.
Code shoudld be:
models.py
class ProgrammingChallenge(models.Model):
name = models.CharField(max_length=200)
description = models.TextField()
expectations = models.TextField()
Your model name should be ProgrammingChallenge(singular) not ProgrammingChallenges(plural).
You should't add prefix challenge before all field names. Because we already know that the fields are in a Model called ProgrammingChallenge. And it is easy to access them like ProgrammingChallenge.name than ProgrammingChallenge.challenge_name
You don't have to add field id manually. Django model automatically adds id field as primary_key
serializer.py
from accounts.models import ProgrammingChallenge
...
class ProgrammingChallengeSerializer(serializers.ModelSerializer):
class Meta:
model = ProgrammingChallenge
fields = '__all__'
No problem in serialize.
Now, main problem is you don't have any view. You definetly read docs. You can use APIView, generic views or viewset. In this example i'm going to use ViewSet that handles CRUD operations built in.
viewsets.py
from rest_framework.viewsets import ModelViewSet
from .models import ProgrammingChallenge
from .serializers import ProgrammingChallengSerializer
class ProgrammingChallengViewSet(ModelViewSet):
queryset = ProgrammingChallenge.objects.all()
serializer_class = ProgrammingChallengeSerializer
urls.py
from rest_framework.routers import SimpleRouter
from .viewsets import ProgrammingChallenge
router = SimpleRouter()
router.register('challengr', ProgrammingChallengeViewSet)
urlpatterns = router.urls
Another advantage of using viewset, it also generate all endpoint for it's CRUD methods automatically via routes.
It should help you to start your first project.
AGAIN, READ THE DOCS!
Is there a way how to dynamically generate a Django rest framework serializers?
Considering this:
class BlogSerializer(serializers.ModelSerializer):
class Meta:
model = models.Blog
fields = get_all_model_fields(models.Blog)
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = models.Post
fields = get_all_model_fields(models.Post)
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = models.User
fields = get_all_model_fields(models.User)
I am wondering if something like following example could be possible:
from django.apps import apps
models = [model for model in apps.get_models()]
for model in models:
type(model.__name__+'Serializer',(serializers.ModelSerializer,),{
type("Meta",(),{
"model":model,
"fields": get_all_model_fields(model)
})
})
Or is there any other way how to generate DRF serializers?
Here's a function to build a ModelSerializer for a given Model (tested with Django 3.2, DRF 3.12.4 and Python 3.8):
from functools import lru_cache
from typing import Type
from django.db import models
from rest_framework import serializers
#lru_cache(maxsize=0)
def model_serializer(model: Type[models.Model]) -> Type[serializers.ModelSerializer]:
meta_class = types.new_class("Meta")
setattr(meta_class, "model", model)
setattr(meta_class, "fields", "__all__")
result = types.new_class(
model.__name__ + "Serializer", (serializers.ModelSerializer,), {}
)
setattr(result, "Meta", meta_class)
return result
If you are certain that you will call this function only once for each serializer, you can omit the #lru_cache to preserve some memory.
Example usage:
class MyModel(models.Model):
some = models.CharField(max_length=123)
other = models.IntegerField()
MyModelSerializer = model_serializer(MyModel)
my_serializer = MyModelSerializer({"some": "abc", "other": 1234})
my_serializer.is_valid(True)
To add the serializers for all your models to the current module's namespace:
from django.apps import apps
for model in apps.get_models():
serializer_type = model_serializer(model)
globals()[serializer_type.__name__] = serializer_type
Your approach will work - but for Django to find about these serializers you have to assign them to the module namespace.
In your code, you just call type - the serializer class is created and "thrown away" immediately. Even if the base Django class serializers.ModelSerializer keep a reference to it in a registry, you would not be able to import your serializers and make use of them.
All you have to do is to add them to the dictionary returned by globals(). Likewise, the namespace you create for the class also has to be a dictionary -since you are calling "type" but not actually assigning its name as "Meta", you create a set, not a dictionary, and your call to type will fail as it was expecting a dictionary.
So, I did not check if the code actually will work, but the idea, based on yours is:
from django.apps import apps
models = [model for model in apps.get_models()]
for model in models:
name = f"{model.__name__}Serializer"
globals()[name] = type(name,(serializers.ModelSerializer,),{
"Meta": type("Meta",(),{
"model":model,
"fields": get_all_model_fields(model)
})
})
del models, model, name
I am assuming by the error in the title, once more here for clarity
'CityListViewSet' should either include a `serializer_class` attribute,
or override the `get_serializer_class()` method.
that my serializer isn't connected to my view, which in my code it should be. I'm not really sure where the bug is in this one. I wonder if any of you have seen something similar?
Here is the code.
Router:
router.register(r'city-list', CityListViewSet, base_name='city-list')
view:
class CityListViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Venue.objects.values('city').distinct()
serializer = CitySerializer(queryset, many=True)
ordering_fields = ('city',)
ordering = ('city',)
serializer:
class CitySerializer(serializers.ModelSerializer):
class Meta:
model = City
fields =('city',)
what is it that would be causing such an assertion error with the code seemly wired up correctly?
The exception says it itself. You need a serializer_class attribute. You have serializer.
Add this code snippet to your views.py file
class CityListViewSet(viewsets.ReadOnlyModelViewSet): # (viewsets.ModelViewSet)
serializer_class = CitySerializer
queryset = City.objects.values('city').distinct()
serializer = CitySerializer(queryset, many=True)
ordering_fields = ('city',)
ordering = ('city',)
error says you define a serializer attribute, you need to correct with writing
serializer_class attribute in your code,
serializer_class = yourCreatedSerializer
serializer = CitySerializer(queryset, many=True)
The above line should be replaced with
serializer_class = CitySerializer(queryset, many=True)
Here you used a different model name:
view:
class CityListViewSet(viewsets.ReadOnlyModelViewSet): #(viewsets.ModelViewSet)
queryset = City.objects.values('city').distinct()
serializer = CitySerializer(queryset, many=True)
ordering_fields = ('city',)
ordering = ('city',)
import -> from .serializers import TaskSerializers,CitySerializer
serializer:
class CitySerializer(serializers.ModelSerializer):
class Meta:
model = City
fields =('city',)
i got this error when declared post method in view and trying to send post data without serialize, if you are doing the request from javascript i solved it using JSON.stringify()
you have to override the user just add
from django.contrib.auth.models import User
from rest_framework.permissions import IsAdminUser
and in createViewList
permission_classes = [IsAdminUser]
Rename this to
serializer = CitySerializer(queryset, many=True)
This
serializer_class = yourCreatedSerializer
Your job is done
In my model:
from django.contrib.auth.models import User
class Restaurant(models.Model):
manager = models.ForeignKey(User, on_delete=models.PROTECT,
null=True, blank=False, related_name="manager")
in my serializers.py
class RestaurantSerializer(CoreHyperlinkedModelSerializer):
class Meta:
model = Restaurant
in my views.py
class RestaurantViewSet(viewsets.ModelViewSet):
queryset = Restaurant.objects.order_by('id').all()
serializer_class = RestaurantSerializer
on my list:
the manager is displaying as <rest_framework.relations.PKOnlyObject object at 0x9f7040xbc208>
How can I display it as normal data like its username?
You want to use a 'SlugRelatedField'.
There are a few ways you can go, but if you just want to show a username, all you need is this
from rest_framework import serializers
class RestaurantSerializer(serializers.ModelSerializer):
manager = serializers.CharField(source="manager.username")
class Meta:
model = Restaurant
if you inherit from ModelSerializer and skip the manager field, it will use user PK as the value of the manager field by default.
a slightly more involved way would be to define a separate serializer for User and then embed it in RestaurantSerializer.
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
class RestaurantSerializer(serializers.ModelSerializer):
manager = UserSerializer()
class Meta:
model = Restaurant
And if you really want to use hyperlinked serializer, you need to do quite a bit of work. You need to read this part carefully http://www.django-rest-framework.org/api-guide/serializers/#how-hyperlinked-views-are-determined
I have a Django model that is hierarchical using django-mptt, which looks like:
class UOMCategory(MPTTModel, BaseModel):
"""
This represents categories of different unit of measurements.
"""
name = models.CharField(max_length=50, unique=True)
description = models.CharField(max_length=50, unique=True)
parent = TreeForeignKey('self', null=True, blank=True, related_name='%(app_label)s_%(class)s_sub_uom_categories')
The problem now is I created a REST API using Django REST Framework; how do I make sure that parent field returns serialized data?
Here is the Model Serializer:
class UOMCategorySerializer(BaseModelSerializer):
"""
REST API Serializer for UOMCategory model
"""
class Meta:
model = UOMCategory
In DRF you can use a serializer as a field in another serializer. However, recursion is not possible.
Tom Christie posted a solution on another question (Django rest framework nested self-referential objects). His solution will also work with your problem.
In your UOMCategorySerializer.Meta class you specify the fields you want to use, also list the parent and/or children field(s) there. Then you use Tom Christies solution.
In your case this would give:
class UOMCategorySerializer(ModelSerializer):
class Meta:
model = UOMCategory
fields = ('name', 'description', 'parent', 'children')
Tom Christies solution: By specifying what field to use for parent and/or children, you avoid using too much (and possibily endless) recursion:
UOMCategorySerializer.base_fields['parent'] = UOMCategorySerializer()
UOMCategorySerializer.base_fields['children'] = UOMCategorySerializer(many=True)
The above works for me in a similar situation.
Simple DRF API view using MPTT cache and DRF serializer.
from rest_framework import serializers
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from events.models import Category
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = (
"name",
"slug",
)
class CategoryTreeView(GenericAPIView):
serializer_class = CategorySerializer
def get(self, request, *args, **kwargs):
root_nodes = Category.objects.all().get_cached_trees()
data = []
for n in root_nodes:
data.append(self.recursive_node_to_dict(n))
return Response(data)
def recursive_node_to_dict(self, node):
result = self.get_serializer(instance=node).data
children = [self.recursive_node_to_dict(c) for c in node.get_children()]
if children:
result["children"] = children
return result