For what purpose is the class Meta: used in the class inside the Django serializers.py file?
Serializer classes can also include reusable validators that are
applied to the complete set of field data. These validators are
included by declaring them on an inner Meta class. Also when
you are defining a serializer then meta tags will help the serializer
to bind that object in the specified format
Below are some of the examples :
While validating request data in specified format:
class EventSerializer(serializers.Serializer):
name = serializers.CharField()
room_number = serializers.IntegerField(choices=[101, 102, 103, 201])
date = serializers.DateField()
class Meta:
# Each room only has one event per day.
validators = UniqueTogetherValidator(
queryset=Event.objects.all(),
fields=['room_number', 'date']
)
While getting data from db
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ['id', 'account_name', 'users', 'created']
More you can read here
The Meta class can be used to define various things about the model such as the permissions, database name, singular and plural names
Related
class Schedule(models.Model):
name = models.CharField(max_length=50)
class ScheduleDefinition(models.Model):
schedule = models.ForeignKey(Schedule, on_delete=models.DO_NOTHING)
config = JSONField(default=dict, blank=True)
These are my models. I am trying to create a new ScheduleDefinition(The Schedule already exists and I know the ID I want to use for my foreign_key). I have a predefined Schedule id that I want to use, but it is not working..
Posting this body:
{
"schedule_id": 1,
"config": {
"CCC": "ccc"
}
}
Error I get:
null value in column "schedule_id" violates not-null constraint
What am I doing wrong? When I create new ScheduleDefinition models, the Schedule model will already be created previously. I am never going to be creating new Schedule's when I create new ScheduleDefinition's.
Serializer:
class ScheduleSerializer(serializers.ModelSerializer):
class Meta:
model = Schedule
fields = ['id', 'name']
class ScheduleDefinitionSerializer(serializers.ModelSerializer):
schedule = ScheduleSerializer(read_only=True, many=False)
class Meta:
model = ScheduleDefinition
fields = ['schedule', 'config']
View:
from rest_framework import generics
from .models import Schedule, ScheduleDefinition
from .serializers import ScheduleSerializer, ScheduleDefinitionSerializer
class ScheduleList(generics.ListAPIView):
queryset = Schedule.objects.all()
serializer_class = ScheduleSerializer
class ScheduleDefinitionList(generics.ListCreateAPIView):
queryset = ScheduleDefinition.objects.all()
serializer_class = ScheduleDefinitionSerializer
class ScheduleDefinitionDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = ScheduleDefinition.objects.all()
serializer_class = ScheduleDefinitionSerializer
View error:
File "/app/server/schedules/serializers.py", line 13, in ScheduleDefinitionSerializer
schedule_id = serializers.PrimaryKeyRelatedField(source="schedule")
File "/usr/local/lib/python3.7/dist-packages/rest_framework/relations.py", line 247, in __init__
super().__init__(**kwargs)
File "/usr/local/lib/python3.7/dist-packages/rest_framework/relations.py", line 108, in __init__
'Relational field must provide a `queryset` argument, '
AssertionError: Relational field must provide a `queryset` argument, override `get_queryset`, or set read_only=`True`.
You've specified Schedule as required (not null), but you aren't actually posting any information to it. Currently your serializer is expecting information in the form:
{
"schedule": {
"name": "Foo"
},
"config": {...}
}
schedule_id is being discarded when you post. Furthermore, you've specified that schedule is a read only field, meaning even if you posted a schedule, it would still be discarded.
If you'd like to post to foreign keys, you'll either need to specially handle it by manually writing the create/update logic for a writable nested serializer (which can be a bit of a hassle), or use a different (writable) foreignkey field serializer and serialize your other read-only data another way.
For example, the following setup should work (untested) with the data you're currently trying to POST:
class ScheduleDefinitionSerializer(serializers.ModelSerializer):
schedule = serializers.PrimaryKeyRelatedField(
queryset=Schedule.objects.all()
)
schedule_name = serializers.CharField(read_only=True, source="schedule.name")
class Meta:
model = ScheduleDefinition
fields = ['schedule', 'schedule_name', 'config']
With this your post should work, and you'll still have read-only access to the corresponding schedule's name via the schedule_name field in your list/detail views.
EDIT
My earlier version of the code would not have been compatible with the original desired POST data. The following should work without altering the POST
class ScheduleDefinitionSerializer(serializers.ModelSerializer):
schedule = ScheduleSerializer(many=False, read_only=True)
schedule_id = serializers.PrimaryKeyRelatedField(
source="schedule",
queryset=Schedule.objects.all()
)
class Meta:
model = ScheduleDefinition
fields = ['schedule', 'schedule_id', 'config']
In short, I want to have a global default serializer per model. My use case here is to create dynamic serializer- i.e creating ModelSerializer classes on the fly.
class Customer(models.Model):
name = models.CharField(max_length=200)
code = models.CharField(max_length=200)
# many more fields..
class CustomerTicket(models.Model):
customer = models.ForeignKey(Customer)
date = models.DateTimeField(auto_now_add=True)
# more fields..
Customer will be referenced by many other models, and hence it will be serialized as a nested object. I don't want the 'code' field to appear in the output - no matter what it should always be excluded.
Now I'd like to create a function:
def serialize_default(model, fields, queryset):
class S(serializers.ModelSerializer):
class Meta:
model = model
fields = fields
depth = 1
return S(queryset, many=True)
if I serialize CustomerTicket queryset using this function, I will get all the customer fields as a nested object. I know I can override it locally, but I want to define a CustomerSerializer that will be used by default (for the nested Customer here) unless other serializer is specified as a field. How to achieve this?
Would something like that work for you?
class DefaultCustomerSerializer(serializers.ModelSerializer):
# whatever fields you want
class DefaultCustomerSerializerModel(serializers.ModelSerializer):
customer = DefaultCustomerSerializer()
# You can inherit from this to have default customer serializer
# on serializers you want.
class CustomerTicketSerializer(DefaultCustomerSerializerModel):
# Other fields
models.py:
class Station(models.Model):
station = models.CharField()
class Flat(models.Model):
station = models.ForeignKey(Station, related_name="metro")
# another fields
Then in serializers.py:
class StationSerializer(serializers.ModelSerializer):
station = serializers.RelatedField(read_only=True)
class Meta:
model = Station
class FlatSerializer(serializers.ModelSerializer):
station_name = serializers.RelatedField(source='station', read_only=True)
class Meta:
model = Flat
fields = ('station_name',)
And I have an error:
NotImplementedError: RelatedField.to_representation() must be implemented.
If you are upgrading from REST framework version 2 you might want ReadOnlyField.
I read this, but it does not help me.
How to fix that?
Thanks!
RelatedField is the base class for all fields which work on relations. Usually you should not use it unless you are subclassing it for a custom field.
In your case, you don't even need a related field at all. You are only looking for a read-only single foreign key representation, so you can just use a CharField.
class StationSerializer(serializers.ModelSerializer):
station = serializers.CharField(read_only=True)
class Meta:
model = Station
class FlatSerializer(serializers.ModelSerializer):
station_name = serializers.CharField(source='station.name', read_only=True)
class Meta:
model = Flat
fields = ('station_name', )
You also appear to want the name of the Station object in your FlatSerializer. You should have the source point to the exact field, so I updated it to station.name for you.
I'm trying to use Django Rest to return a json representation of a model based on a ordering from a custom field that is not attached to the model, but is attached to the serializer. I know how to do this with model specific fields, but how do you use django rest to return an ordering when the field is only within the serializer class? I want to return a list of Pics ordered by 'score'. Thanks!
------Views.py
class PicList(generics.ListAPIView):
queryset = Pic.objects.all()
serializer_class = PicSerializerBasic
filter_backends = (filters.OrderingFilter,)
ordering = ('score')
------Serializer.py
class PicSerializer(serializers.ModelSerializer):
userprofile = serializers.StringRelatedField()
score = serializers.SerializerMethodField()
class Meta:
model = Pic
fields = ('title', 'description', 'image', 'userprofile', 'score')
order_by = (('title',))
def get_score(self, obj):
return Rating.objects.filter(picc=obj).aggregate(Avg('score'))['score__avg']
You could move the logic of the method get_score to the manager of the class Pic. In this answer there is an example of how to do it.
Once you have it in the manager, the score field would become "magically" available for every object of the class Pic everywhere (serializer, views...) and you'll be able to use it for ordering.
I have a model form that I use to update a model.
class Turtle(models.Model):
name = models.CharField(max_length=50, blank=False)
description = models.TextField(blank=True)
class TurtleForm(forms.ModelForm):
class Meta:
model = Turtle
Sometimes I don't need to update the entire model, but only want to update one of the fields. So when I POST the form only has information for the description. When I do that the model never saves because it thinks that the name is being blanked out while my intent is that the name not change and just be used from the model.
turtle_form = TurtleForm(request.POST, instance=object)
if turtle_form.is_valid():
turtle_form.save()
Is there any way to make this happen? Thanks!
Only use specified fields:
class FirstModelForm(forms.ModelForm):
class Meta:
model = TheModel
fields = ('title',)
def clean_title(self....
See http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#controlling-which-fields-are-used-with-fields-and-exclude
It is common to use different ModelForms for a model in different views, when you need different features. So creating another form for the model that uses the same behaviour (say clean_<fieldname> methods etc.) use:
class SecondModelForm(FirstModelForm):
class Meta:
model = TheModel
fields = ('title', 'description')
If you don't want to update a field, remove it from the form via the Meta exclude tuple:
class Meta:
exclude = ('title',)