Learning DRF: Serialize models in a group - django

I'm new to both Django and DRF. I need to expose the following JSON via GET, but I seem to be having trouble getting each class to be a part of classes []. I belive I can use StringRelatedField for this, but the examples I have found don't seem to click with me. Thanks in advance!
expected output
{
"classes":[
{
"id":24,
"name":"Math 101",
"starts_on":"2016-08-09",
"ends_on":"2016-08-14",
"entries_url":"https://example.com/classes/24/classes.json"
},
{
"id":23,
"name":"English 101",
"starts_on":"2016-07-28",
"ends_on":"2016-07-30",
"entries_url":"https://example.com/classes/23/classes.json"
}
]
}
Here's what I've cobbled together. "nr" is just a throwaway variable as I don't actually want any data besides what comes from class.
model.py
class Class(models.Model):
id = models.CharField(max_length=10)
starts_on = models.DateTimeField(auto_now_add=False)
ends_on = models.DateTimeField(auto_now_add=False)
entries_url = models.CharField(max_length=150)
class Meta:
ordering = ('id',)
class Classes(models.Model):
nr = models.CharField(max_length=100)
serializers.py
class ClassesSerializer(serializers.ModelSerializer):
class = serializers.StringRelatedField(many=True)
class Meta:
model = Classes
fields = ('classes')

Your database needs to keep a relation between your tables. You can use a foreign key here:
class Class(models.Model):
id = models.CharField(max_length=10)
starts_on = models.DateTimeField(auto_now_add=False)
ends_on = models.DateTimeField(auto_now_add=False)
entries_url = models.CharField(max_length=150)
related_classes = models.ForeignKey('Classes', on_delete=models.CASCADE, related_name='classes')
class Meta:
ordering = ('id',)
class Classes(models.Model):
nr = models.CharField(max_length=100)
Then, your serializer becomes:
class ClassSerializer(serializers.ModelSerializer):
class Meta:
model = Class
fields = ['__all__']
class ClassesSerializer(serializers.ModelSerializer):
classes = ClassSerializer(many=True)
class Meta:
model = Classes
fields = ('classes',)
Also, I strongly discourage you to use class for your variables, since it clashes with the class keyword of Python.

Related

Django rest framework serializer for membership Model?

How do I use Serializer for something like This
class Language(BaseModel):
name = models.CharField(null=False, blank=False, unique=True, max_length=150)
class Helper(BaseModel):
languages = models.ManyToManyField('Language', blank=True, through="HelperLanguage")
class HelperLanguage(BaseModel):
helper = models.ForeignKey('Helper', on_delete=models.CASCADE)
language = models.ForeignKey('Language', on_delete=models.CASCADE)
read = models.BooleanField()
write = models.BooleanField()
speak = models.BooleanField(default=True)
class LanguageSerializer(ModelSerializer):
class Meta:
model = Language
fields = ["id", "name"]
class HelperLanguageSerializer(ModelSerializer):
language = LanguageSerializer(read_only=True)
class Meta:
model = HelperLanguage
fields = ["id", "language", "read", "write", "speak"]
class HelperPublicSerializer(ModelSerializer):
languages = HelperLanguageSerializer(read_only=True, many=True)
class Meta:
model = Helper
fields = ['id', 'languages']
while using HelperPublicSerialiser for list view I am getting error
Got AttributeError when attempting to get a value for field read on serializer HelperLanguageSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the Language instance.
I do understand the problem but couldn't find any solution probably not using membership model right way.

Making general Filter in Djando Rest Framework

I want to make a common filter to my models because I need to filter all my objects to return a gap between time_start and time_end, but apparently it doesn't work.
I'm not sure if it's even possible(But I hope so, because it won't by DRY otherwise).
models.py
class Time(models.Model):
time = models.TimeField()
class Meta:
abstract=True
class Mark(Time):
value = models.IntegerField(verbose_name="mark")
teacher = models.CharField(max_length=20)
subject = models.CharField(max_length=20)
serializers.py
class MarkSerializer(serializers.ModelSerializer):
class Meta:
model = Mark
fields = ('id', 'time','value', 'teacher', 'subject')
filers.py
class DataFilter(django_filters.FilterSet):
start_time = django_filters.TimeFilter(name="time", lookup_expr='gte')
end_time = django_filters.TimeFilter(name="time", lookup_expr='lte')
class Meta:
model = Time
fields = ['start_time', 'end_time']
views.py
class MarkViewSet(viewsets.ModelViewSet):
serializer_class = MarkSerializer
queryset = Mark.objects.all()
filter_class = DataFilter
I try to get needed marks through:
127.0.0.1:8000/api/v0/marks/?time_start=11:40:00&time_end=12:00:00
but it returns all the objects that I have not the filtered ones.
Thanks in advance.
You have passed the filter params wrong, it should be the name of the field you described in the filter class DataFilter.
Hit this endpoint in the browser,
127.0.0.1:8000/api/v0/marks/?start_time=11:40:00&end_time=12:00:00

DRF nested serializers - Filtering data on child serializers

I am trying to use nested serializer. How do I use the root serializer to filter data on the grandchild serializer?
School and Program have a many to many relationship So that any school can subscribe to any program. Each school has classes and those classes are part of a program, that's why PClass has foreign keys to both School and program.
When I call my api .../api/school/1 I want to get all the programs that school subscribes to and which classes are available in each program (in that school)
class School(TimeStampedModel, SoftDeletableModel):
name = models.CharField(max_length=40)
slug = models.SlugField(max_length=40, default='', blank=True)
class Program(TimeStampedModel, SoftDeletableModel):
name = models.CharField(max_length=50, unique=True)
slug = models.SlugField(max_length=50,default='',blank=True, unique=True)
description = models.CharField(max_length=100, blank=True)
school = models.ForeignKey(School, blank=True, null=True, related_name="programs")
class PClass(TimeStampedModel, SoftDeletableModel):
name = models.CharField(max_length=50)
slug = models.SlugField(max_length=50,default='',blank=True)
description = models.CharField(max_length=100)
program = models.ForeignKey(Program, related_name="classes")
school = models.ForeignKey(School, related_name="classes")
and the following serializers:
class SchoolSerializer( serializers.ModelSerializer):
programs = ProgramSerializer(source='get_programas',many=True,read_only=True)
class Meta:
model = School
fields = '__all__'
lookup_field = 'slug'
extra_kwargs = {
'url': {'lookup_field': 'slug'}
}
class PClassSerializer(serializers.ModelSerializer):
class Meta:
model = Class
fields = ('name','slug')
class ProgramSerializer(serializers.ModelSerializer):
school = serializers.SlugRelatedField(queryset=School.objects.all(),
slug_field='name',
required=False)
classes = PClassSerializer(many=True,read_only=True)
class Meta:
model = Program
exclude = ('id',)
lookup_field = 'slug'
extra_kwargs = {
'url': {'lookup_field': 'slug'}
}
is this possible? or is it a problem with the way I set up my models?
There's 2 ways I know how to do this. The first is you're pretty close already
EDIT: Noticed you're using related names. I've updated the answer for that
class SchoolSerializer( serializers.ModelSerializer):
programas = ProgramSerializer(source='programs',many=True,read_only=True)
For more complex filtering the best way is to use a SerializerMethodField Field. Here's an example.
You'll probably want to also do some pre-fetches in your view to get the queryset to minimize the # of queries.
class SchoolSerializer(serializers.ModelSerializer):
programas = SerializerMethodField(source='get_programas',many=True,read_only=True)
class Meta:
model = Unidade
fields = '__all__'
lookup_field = 'slug'
extra_kwargs = {
'url': {'lookup_field': 'slug'}
}
def get_programas(self, obj):
# You can do more complex filtering stuff here.
return ProgramaSerializer(obj.programs.all(), many=True, read_only=True).data
To get the PClasses you'll just need to filter your queryset with
program.classes.filter(school=program.school)
Full example for ProgramSerializer is
class ProgramSerializer(serializers.ModelSerializer):
classes = SerializerMethodField(source='get_classes', many=True, read_only=True)
class Meta:
model = Program
def get_classes(self, obj):
return PClassSerializer(obj.classes.filter(school=obj.school), many=True, read_only=True).data
EDIT 10 or so:
Since you have changed the program -> School from foreignkey to ManyToMany, this changes everything.
For the schoolserializer, you need to use a SerializerMethodField. This way you can pass in extra context to your nested serializer.
class SchoolSerializer(serializers.ModelSerializer):
classes = SerializerMethodField(source='get_programs')
class Meta:
model = School
def get_programs(self, obj):
return ProgramSerializer(obj.program_set.all(), many=True, read_only=True, context={ "school": obj }).data
class ProgramSerializer(serializers.ModelSerializer):
classes = SerializerMethodField(source='get_classes', many=True, read_only=True)
class Meta:
model = Program
def get_classes(self, obj):
return PClassSerializer(obj.classes.filter(school=self.context["school"]), many=True, read_only=True).data

Django REST displayed filtered nested resource

I'm attempting to return JSON in a simple GET that includes a row from a joined table. The models:
class School(models.Model):
id = models.CharField(max_length=18,primary_key=True)
name = models.CharField(max_length=255,blank=False)
class Meta:
db_table='school'
class FKTable(models.Model):
school = models.ForeignKey(School,blank=False, db_column='school_id', on_delete=models.CASCADE)
name = models.CharField(max_length=45, blank=False)
value = models.CharField(max_length=255, blank=True, null=True)
class Meta:
db_table='fk_table'
And the serializer:
class SchoolListSerializer(serializers.ModelSerializer):
id = serializers.CharField(source='id')
name = serializers.CharField(source='name')
fkColumn = ????
class Meta:
model = models.School
fields = ('id','name','fkColumn')
The problem is I want to also filter the nested resource that I join against on the 'name' column so I don't know what to put for 'fkColumn' in the serializer. In SQL it would look something like this:
SELECT school.*, fk_table.value from school INNER JOIN fk_table on
fk_table.school_id = school.id AND fk_table.name = 'some name'
The end result JSON I want is:
{
"schools": [
{
"id": "someId",
"name": "someName",
"fkColumn": "Value"
}]}
RelatedField does not seem to work and none of the documentation seems to give a solid answer on how to do this. Any ideas?
Revised after clarification. Define an fkeySerializer:
class fkeySerializer(serializers.ModelSerializer):
...
Then link this in your main serializer:
fkey = fkeySerializer(many=True)
class Meta:
model = models.School
fields = ('id','name','fkColumn',)
You can use a MethodField serializer here. For example:
fkColumn = serializers.SerializerMethodField()
def get_fkColumn(self, obj):
return obj.fkColumn
class Meta:
model = models.School
fields = ('id','name','fkColumn',)
As an aside, perhaps there's a good reason for it, but it might be easier to rename this table to be more easily readable

Django REST Framework -- is multiple nested serialization possible?

I would like to do something like the following:
models.py
class Container(models.Model):
size = models.CharField(max_length=20)
shape = models.CharField(max_length=20)
class Item(models.Model):
container = models.ForeignKey(Container)
name = models.CharField(max_length=20)
color = models.CharField(max_length=20)
class ItemSetting(models.Model):
item = models.ForeignKey(Item)
attribute_one = models.CharField(max_length=20)
attribute_two = models.CharField(max_length=20)
serializers.py
class ItemSettingSerializer(serializers.ModelSerializer):
class Meta:
model = ItemSetting
class ItemSerializer(serializers.ModelSerializer):
settings = ItemSettingSerializer(many=True)
class Meta:
model = Item
fields = ('name', 'color', 'settings')
class ContainerSerializer(serializers.ModelSerializer):
items = ItemSerializer(many=True)
class Meta:
model = Container
fields = ('size', 'shape', 'items')
When I do nesting of only one level (Container and Item) it works for me. But when I try to add another level of nesting with the ItemSetting, it throws an AttributeError and complains 'Item' object has no attribute 'settings'
What am I doing wrong?
Multiple nested serialization works for me. The only major difference is that I specify a related_name for the FK relationships. So try doing this:
class Item(models.Model):
container = models.ForeignKey(Container, related_name='items')
class ItemSetting(models.Model):
item = models.ForeignKey(Item, related_name='settings')
Hope this works for you.