models.py:
class Car(models.Model):
# many fields
class CarOptions(models.Model):
car = models.OneToOneField(Car, primary_key=True, related_name='options')
color = models.CharField()
# many other fields
So, I want get all information from Car and its CarOptions.
serializer.py:
class CarOptionsSerializer(serializers.ModelSerializer):
color = serializers.CharField()
class Meta:
model = CarOptions
fields = ('color')
class CarSerializer(serializers.ModelSerializer):
color = CarOptionsSerializer(many=True, read_only=True, )
class Meta:
model = Car
fields = ('many fields', 'color', )
In views.py I have created class based on XMLRenderer, in _to_xml() method (link) I use:
self._to_xml(xml, item["color"])
But it does not work. I have an error:
KeyError: 'color'
I print item-dict and there is no key color in it.
How to fix that?
Thanks!
You should reference the options in the CarSerializer using the options field name rather than the color field name (as you set the related field to be options in the OneToOneField). You do not define a color field directly on the Car.
The following code works for me with the latest django and rest framework:
models.py
class CarModel(models.Model):
name = models.CharField(max_length=250)
class CarOptionsModel(models.Model):
car = models.OneToOneField(CarModel, related_name='options')
color = models.CharField(max_length=250)
serializers.py
class CarOptionsSerializer(serializers.ModelSerializer):
color = serializers.CharField()
class Meta:
model = CarOptionsModel
fields = ('color',)
class CarSerializer(serializers.ModelSerializer):
options = CarOptionsSerializer(read_only=True, )
class Meta:
model = CarModel
fields = ('options', 'name')
views.py
class CarViewSet(viewsets.ModelViewSet):
serializer_class = CarSerializer
queryset = CarModel.objects.all()
urls.py
router = routers.DefaultRouter()
router.register(r'api', CarViewSet)
urlpatterns = router.urls
I would note that since you define a one to one mapping, you cannot have many=True.
Related
I am trying to create nested relationship from more than two models in Django Rest Framework.
Thank you in advance for helping me.
I succeed with two models but when I'm trying with three models unable to create nested serialization.
from django.db import models
class Project(models.Model):
project_id = models.AutoField(primary_key=True)
project_name = models.CharField(max_length=255)
def __str__(self):
return self.name
class Site(models.Model):
site_id = models.AutoField(primary_key=True)
site_name = models.CharField(max_length=255)
project_id= models.ForeignKey(Project, related_name="projectid", on_delete=models.CASCADE)
def __str__(self):
return self.site_name
class Aggin(models.Model):
assign_id = models.AutoField(primary_key=True)
site_id = Models.ForeginKey(Site, relate_name="siteid", on_delete=models.CASCADE)
from rest_framework import serializers
from .models import Song, Artist
class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = ('__all__')
class SiteSerializer(serializers.ModelSerializer):
class Meta:
model = Site
fields = ('__all__')
class AggignSerializer(serializers.ModelSerializer)
class Meta:
model = Aggin
fields = ('__all__')
I think you don't need to primary id field if you wanna use the Django's default foreign key setting. And related_name should be defined from the view of the counterpart model.
from django.db import models
class Project(models.Model):
project_name = models.CharField(max_length=255)
def __str__(self):
return self.name
class Site(models.Model):
site_name = models.CharField(max_length=255)
project = models.ForeignKey(Project, related_name="sites", on_delete=models.CASCADE)
def __str__(self):
return self.site_name
class Aggin(models.Model):
site = Models.ForeginKey(Site, relate_name="assigns", on_delete=models.CASCADE)
And then, in serializer, you can set like the following.
from rest_framework import serializers
from .models import Song, Artist
class ProjectSerializer(serializers.ModelSerializer):
sites = SiteSerializer(read_only = True, many = True)
class Meta:
model = Project
fields = ('id', 'project_name', 'sites')
class SiteSerializer(serializers.ModelSerializer):
assigns = AggignSerializer(read_only = True, many = True)
class Meta:
model = Site
fields = ('id', 'site_name', 'assigns')
class AggignSerializer(serializers.ModelSerializer):
class Meta:
model = Aggin
fields = ('id')
I am trying to enforce a constraint for mysql where user is prohibited from inserting twice the same name and model. E.g This should not be allowed to be inserted twice: name:Name1 model:Model1
#Model
class Car(models.Model):
name = models.CharField(max_length=100)
model = models.CharField(max_length=100)
#View
class CarListCreateAPIView(generics.ListCreateAPIView):
serializer_class = CarSerializer
def get_queryset(self):
trip_code = self.kwargs.get("pk")
return Car.objects.filter(trip = trip_code) #Return cars for given trip
#Seializer
class CarSerializer(serializers.ModelSerializer):
class Meta:
model = Car
fields = ('__all__')
constraints = [
models.UniqueConstraint(fields=['name', 'model'], name='car_name_model_constraint')
]
The problem is that the constraint is never created and thus not enforced. What might be the issue with the code?
Use unique_together in class Meta in Model like that:
class Car(models.Model):
name = models.CharField(max_length=100)
model = models.CharField(max_length=100)
class Meta:
unique_together = ['name','model']
More on that here: https://docs.djangoproject.com/en/4.0/ref/models/options/#unique-together
Lets say I have two models and a form:
class Person(models.Model):
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
class Car(models.Model):
plate = models.CharField(max_length=255)
persons = models.ManyToManyField(Person)
class CarAddForm(forms.ModelForm):
plate = forms.CharField()
persons = forms.ModelMultipleChoiceField(queryset=Person.objects.all())
class Meta:
model = Car
fields = [
'plate',
'persons'
]
Is there a way to get ModelMultipleChoiceField queryset of people that are NOT associated with any car?
In case of editing Car model object, the queryset should contain people that are NOT associated with any car PLUS people that are associated with the car being edited
PS: maybe there is a better way to achieve this?
You can make use of the limit_choices_to--(Doc) argument of ManyToManyField as
class Car(models.Model):
plate = models.CharField(max_length=255)
persons = models.ManyToManyField(
Person,
limit_choices_to={"car__isnull": True}
)
Alternatively, you can also alter the queryset argument of ModelMultipleChoiceField as
class CarAddForm(forms.ModelForm):
plate = forms.CharField()
persons = forms.ModelMultipleChoiceField(
queryset=Person.objects.filter(car__isnull=True)
)
class Meta:
model = Car
fields = [
'plate',
'persons'
]
You can specify a filter for the query:
from django.db import models
class CarAddForm(forms.ModelForm):
...
persons = forms.ModelMultipleChoiceField(
queryset=Person.objects\
.annotate(car_count=models.Count('cars'))\
.filter(car_count=0))
...
Another options is to override the forms __init__() method. Maybe like this:
from django.db import models
class CarAddForm(forms.ModelForm):
...
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['person'].queryset = self.fields['person'].queryset\
.annotate(car_count=models.Count('cars'))\
.filter(car_count=0))
I have a problem where DRF isn't displaying all of my fields correctly for a model class / reference table (specifically the primary key).
My Model Class looks like this (very simple):
class UnitOfIssue(models.Model):
code = models.CharField(max_length=2, primary_key=True)
description = models.CharField(max_length=16)
class Meta:
ordering = ('code',)
def __str__(self):
return "{0} - {1}".format(self.code, self.description)
My Serializer Looks like this:
class UnitOfIssueSerializer(serializers.HyperlinkedModelSerializer):
"""
"""
url = serializers.HyperlinkedIdentityField(
read_only=True,
view_name='unitofissue-detail',
format='html',
lookup_field='code')
class Meta:
model = UnitOfIssue
fields = ('code', 'description', 'url')
# fields = '__all__'
And I'm using a generic view:
class UnitOfIssueDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = UnitOfIssue.objects.all()
serializer_class = UnitOfIssueSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
lookup_field = 'code'
In order for the UnitOfIssue primary key code to be displayed in the auto-generated UI, I have to define fields = ('code', 'description', 'url') in the serializer instead of fields = '__all__'.
I want to just be able to use the '__all__' syntax but I can't figure out what's going wrong.
Also, I'm using Django==1.11.13 and djangorestframework==3.8.2
This issue plagued me for weeks, and yet it was such a simple error. I fixed it by changing the serializer base class from:
class UnitOfIssueSerializer(serializers.HyperlinkedModelSerializer):
to:
class UnitOfIssueSerializer(serializers.ModelSerializer):
Here's are examples I have:
models.py:
class Example(models.Model):
title = models.CharField(...)
description = models.CharField(...)
class Foo(models.Model):
example = models.ManyToManyField(Example)
serializers.py:
class FooSerializer(serializers.ModelSerializer):
class Meta:
model = Foo
fields = '__all__'
depth = 1
views.py:
...
serialized_data = [FooSerializer(foo).data for foo in Foo.objects.all().get]
In output, I receive only Example's IDs, but is there any way I could get title and description fields also (details of m2mfield)? As I understand, Foo.objects.all().get simply doesn't contain this data, but maybe I could somehow get it and use it?
I could also rebuild models if needed, but currently I use m2mf because of needs to contain multiple objects as related to this model data.
update
models.py:
class Event(models.Model):
ts = models.BigIntegerField(editable=False)
class Foo(Event):
user = models.ForeignKey(User, ...)
example = *...(remains to be the same)*
foos = models.ForeignKey('self', **somemore** null=True)
serializers.py:
class EventSerializer(serializers.ModelSerializer):
class Meta:
model = Event
fields = '__all__'
def to_representation(self, instance):
result = {'ts': instance.ts}
if isinstance(instance, Foo):
result['foo'] = FooSerializer(instance).data
return result
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username')
class FooSerializer(serializers.ModelSerializer):
# user = UserSerializer(read_only=True) # with this I have an error: Got AttributeError when attempting to get a value for field 'username' on #serializer 'UserSerializer'
class Meta:
model = Foo
fields = '__all__'
depth = 1
You could use depth attribute to achieve desired output.
The default ModelSerializer uses primary keys for relationships, but
you can also easily generate nested representations using the depth
option.The depth option should be set to an integer value that
indicates the depth of relationships that should be traversed before
reverting to a flat representation.
class FooSerializer(serializers.ModelSerializer):
class Meta:
model = Foo
fields = '__all__'
depth = 1
Apart from the answer, I would like to change your views.py code, cause it seems like very bad :(. Do it on DRF Way as
serialized_data = FooSerializer(Foo.objects.all(), many=True).data<br>
Example View
from rest_framework.viewsets import ModelViewSet
class FooViewset(ModelViewSet):
serializer_class = FooSerializer
queryset = Foo.objects.all()
UPDATE-1
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
exclude = ('password',) # add fields that are need to be excluded
class FooSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = Foo
fields = '__all__'
depth = 1
depth = 1 will serializer all fields in the model, (It's same as setting the fields=='__all__' in Meta class of serializer)
UPDATE-2
class FooSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = Foo
fields = '__all__'
depth = 1
def to_representation(self, instance):
real_data = super().to_representation(instance).copy()
# DO YOUR EXTRA CHECKS
child = UserSerializer(instance.child_foo).data
if child:
real_data.update({"child_data": child})
# After your checks, add it to "real_data"
return real_data
and I assumed I have a Foo model as
class Foo(models.Model):
example = models.ManyToManyField(Example)
user = models.ForeignKey(User)
child_foo = models.ForeignKey('self', null=True, blank=True)
In your serializer add depth = 1. Example where 'users' is the related field:
FooSerializer(serializers.ModelSerializer):
class Meta:
model = Foo
fields = ('id', 'account_name', 'users', 'created')
depth = 1