Django Rest Framework: Order by Serializer Method Field - python

I am currently using Django REST framework 3.11.0 with Django 3.0.6. I am still very new to DRF and I am not sure how to approach this problem. I am trying to apply sort to SerializerMethodField and an enum. I was able to find some documentation that states that I could make my own OrderFilter but I dont know how. Are there any examples I could use? Here is my code.
View
from rest_framework import generics
from V1.assets.models.asset_main import Asset
from V1.assets.serializers.asset_serializer import AssetSerializer
from rest_framework import filters
from django_filters.rest_framework import DjangoFilterBackend
class AssetsView(generics.ListCreateAPIView):
queryset = Asset.objects.all()
serializer_class = AssetSerializer
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filter_fields = ['asset_type']
search_fields = ['asset_type', 'asset_properties', 'current_employee_name']
Model
class AssetType(models.TextChoices):
LAPTOP = 'LPT', _('Laptop')
MOUSE = 'MSE', _('Mouse')
MONITOR = 'MTR', _('Monitor')
AC_ADAPTER = 'ADR', _('AC Adapter')
TELEPHONE = 'TLP', _('Telephone')
LOCK = 'LCK', _('Lock')
HEADSET = 'HDS', _('Headset')
class Asset(CreatedModified):
asset_type = models.CharField(
max_length=3,
choices=AssetType.choices,
default=None
)
asset_properties = JSONField(default=dict)
current_employee = models.ForeignKey(
Employee,
related_name='assets_current_employees',
related_query_name='assets_current_employee',
default=None,
on_delete=models.CASCADE,
null=True,
blank=True
)
class Meta:
app_label = 'assets'
default_related_name = 'assets'
def __str__(self):
return self.asset_type
Serializer
from V1.general.enums import AssetStatus, AssetType
from V1.accounts.models.employee_main import Employee
class AssetSerializer(serializers.ModelSerializer):
current_employee_name = serializers.SerializerMethodField('get_full_name_from_employee')
asset_type = serializers.CharField(source='get_asset_type_display')
class Meta:
model = Asset
fields = (
'id',
'asset_type',
'asset_properties',
'current_employee',
'current_employee_name'
)
extra_kwargs = {
'asset_type': {
'required': True,
'allow_blank': False
}
}
def get_full_name_from_employee(self, asset):
current_employee_name = asset.current_employee.first_name + ' ' + asset.current_employee.last_name
return current_employee_name
I can't seem to order by current_employee_name or asset_type. What should I do to allow sorting for these two fields?

As far as I can understand from your question you want to order the Assets by the current employee name. Let's take few names for example:
Jean d'Artagnan (first_name: Jean, last_name: d'Artagnan)
Joe Plumber (first_name: Joe, last_name: Plumber)
Jonas Meyer (first_name: Jonas, last_name: Meyer)
The same ordering can be achieved if we sort first by first_name and then by last_name. In case there are multiple entries with the same first_name the ordering will be done be the last_name.
To achieve this you can simply specify the ordering_fields in your view:
class AssetsView(generics.ListCreateAPIView):
filters = [filters.OrderingFilter]
ordering_fields = ['current_employee.first_name', 'current_employee.last_name']

Related

Django MoneyField: Invalid value for MoneyField

I'm having slight of a problem while using MoneyField in Django.
I have 2 models: Work and Price. And Price gets Work id as a Foreign Key.
In Postman I'm trying to post a Work with a Price, but I keep getting an error.
Here is my work_model:
from django.db import models
from django.contrib.auth import get_user_model
User = get_user_model()
class Work(models.Model):
user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
name = models.CharField(max_length=200)
length = models.IntegerField(null=True)
width = models.IntegerField(null=True)
def __str__(self):
return "{}".format(self.name)
And here my price_model
from django.db import models
from .model_work import *
from djmoney.models.fields import MoneyField
class Price(models.Model):
work = models.OneToOneField(Work, on_delete=models.CASCADE, related_name='price')
price = MoneyField(max_digits=19, decimal_places=4, default_currency='USD', null=True)
shipping = models.IntegerField(null=True)
total = models.IntegerField(null=True)
def __str__(self):
return "{}".format(self.price)
When I'm posting a Work in Postman:
{
"user":"2",
"name":"work 20",
"price":
[
{
"price":20,
"price_currency":"USD",
"shipping":12,
"total":32
}
],
"length":"50",
"width":"60"
}
I keep getting this error:
ValidationError at /works/
["Invalid value for MoneyField: [OrderedDict([('price', <Money: 20.0000 USD>), ('shipping', 12), ('total', 32)])]."]
I've looked everywhere but can't manage to understand my error, does anyone have a clue?
Thanks for your responses!
UPDATE 1:
Forgot to put the serializers.
Here is my work_serializer:
from rest_framework import serializers
from ..models.model_work import Work
from .serializers_user import *
from .serializers_price import *
class WorkIndexSerializer(serializers.ModelSerializer):
"""
Serializer listing all Works models from DB
"""
user = UserIndexSerializer()
price_set = PriceDetailsSerializer(many=True)
class Meta:
model = Work
fields = [
'id',
'name',
'price_set',
'length',
'width',
]
class WorkCreateSerializer(serializers.ModelSerializer):
"""
Serializer to create a new Works model in DB
"""
price = PriceCreateSerializer(many=True)
class Meta:
model = Work
fields = [
'user',
'name',
'price',
'length',
'width',
]
def create(self, validated_data):
price = Price.objects.create(**validated_data)
work = Work.objects.create(**validated_data)
return work
class WorkDetailsSerializer(serializers.ModelSerializer):
"""
Serializer showing details of a Work model from DB
"""
user = UserIndexSerializer()
class Meta:
model = Work
fields = fields = [
'user',
'name',
'price_set',
'length',
'width',
]
And my price_serializer:
from rest_framework import serializers
from ..models.model_price import Price
class PriceIndexSerializer(serializers.ModelSerializer):
"""
Serializer showing Price information when called by Work GET serializers.
Not showing 'work' field to avoid loop.
"""
class Meta:
model = Price
fields = [
'price',
'shipping',
'total'
]
depth = 1
class PriceDetailsSerializer(serializers.ModelSerializer):
"""
Serializer showing Price information when called by Work GET serializers.
Not showing 'work' field to avoid loop.
"""
class Meta:
model = Price
fields = [
'price',
'shipping',
'total'
]
depth = 1
class PriceCreateSerializer(serializers.ModelSerializer):
"""
Serializer to create a new Price when new Work model is created in DB
"""
class Meta:
model = Price
fields = [
'price',
'shipping',
'total'
]
def create(self, validated_data):
work = Work.objects.create(**validated_data)
return work

Django REST: Serializer lookup by UUID

I'm creating this simple shopping API in Django REST.
Internally I'm using IDs for foreign key constraints, while guuids are brought to the outside world.
For the checkout procedure, the user provides a list of article IDs he is willing to purchase. The object in the POST data thus looks as follows:
{
assets: [
{
'product': 'd9d5044d-2284-4d15-aa76-2eee3675035b',
'amount': 4
},
....
]
}
I'm using the following ticket/asset models:
# Ticket
class Ticket(models.Model):
uuid = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='tickets', on_delete=models.CASCADE)
# Assets
class Asset(models.Model):
ticket = models.ForeignKey(Ticket, related_name='assets', on_delete=models.CASCADE)
stock_item = models.ForeignKey(Stock, related_name='stock_item', on_delete=models.SET_NULL, null=True)
amount = models.IntegerField(validators=[MinValueValidator(0)])
And the serializers look as follows:
# Asset serializer
class AssetSerializer(serializers.ModelSerializer):
class Meta:
model = Asset
fields = ('stock_item', 'amount')
# Ticket serializer
class TicketSerializer(WritableNestedModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
assets = AssetSerializer(many=True)
class Meta:
model = Ticket
fields = ('uuid', 'owner', 'assets', )
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
When posting an object of the type specified above, the following error is presented:
{"assets":[{"stock_item": ["Invalid type. Expected PK, received string"]}]}
Which I can't seem to solve, how do I instruct the serializer to use the uuid as the lookup value? I solved a similar problem on view-level earlier by using the lookup_field member, but that doesn't seem to solve it. Any suggestions?
Enter code here
If I have understood you correctly, a SlugRelatedField should be able to find the correct related object.
class AssetSerializer(serializers.ModelSerializer):
ticket = serializers.SlugRelatedField(
read_only=True,
slug_field='uuid',
queryset=Ticket.objects.all() # Might be redundant with read_only=True
)
class Meta:
model = Asset
fields = ('ticket', 'stock_item', 'amount')
Elaborating on #BjornW's comment:
class UUIDRelatedField(serializers.SlugRelatedField):
slug_field = 'uuid'
def __init__(self, **kwargs):
super().__init__(slug_field=self.slug_field, **kwargs)
def to_representation(self, obj):
return getattr(obj, self.slug_field).hex

How can I write multiple filter in Django

I am confusing how can I write the filter code in my serializers.py.
I have the following models.
User (AbstractBaseUser)
userid = models.CharField(max_length=64, primary_key=True)
username = models.CharField(max_length=128)
Clinic (models.Model)
clinic_id = models.CharField(max_length=8, primary_key=True)
clinic_name = models.CharField(max_length=64)
Consultation (models.Model)
consultation_id = models.AutoField(primary_key=True)
clinic_id = models.ForeignKey(Clinic)
user = models.ForeignKey(User)
Chat (models.Model)
chat_id = models.AutoField(primary_key=True)
consultation_id = models.ForeignKey(Consultation)
DIDRESPONSE_CHOICE = {
('R', 'No'),
('C', 'Doing'),
('F', 'Done'),
}
did_response = models.CharField(max_length=1, choices=DIDRESPONSE_CHOICE, default='N')
ChatDetail (models.Model)
chat_no = models.AutoField(primary_key=True)
chat_id = models.ForeignKey(Chat)
CHATFROM_CHOICE = {
('D', 'Doctor'),
('U', 'User'),
}
chat_from = models.CharField(max_length=1, blank=True, null=True)
chat = models.CharField(max_length=512, blank=True)
I want to serialize above data and response to client in JSON format.
{
'username': 'Tom Smith',
'clinic_name': 'Adam's clinic',
'consultation_id': 12345678,
'chat_id': 09876543,
'chat_detail' : [
{'chat_no': 11122233, 'chat_from': 'D', 'chat': 'How are you doing today?'},
{'chat_no': 11112234, 'chat_from': 'U', 'chat': 'I have a headache'},
{'chat_no': 11122235, 'chat_from': 'D', 'chat': 'Oh, I'm sorry'},
{'chat_no': 11122236, 'chat_from': 'U', 'chat': 'Can you help me?'},
]
}
I made filters in my views.py by using override get_queryset method. But it wasn't not work. So, I tried to write filters in my serializers.py. But it is also didn't work(I don't understand how can I write above requirement filter in my serializers.py).
What should I do to achieve my requirement? Please help me please.
views.py
class WebViewChatDataOutputView(APIView):
def get(self, request, format=None):
chat_detail = ChatDetail
serializer = WebViewChatDataOutputSerializer(chat_detail, many=True)
filter_fields = ('chat_id')
return Response(serializer.data)
def get_queryset(self):
consultation_id = self.request.consultation_id
chat = Chat.objects.filter(consultation_id=consultation_id)
return ChatDetail.objects.filter(chat_id=chat.chat_id)
serializers.py
class WebViewChatDataOutputSerializer(serializers.ModelSerializer):
# class Meta:
# model = Consultation
# fields = ('consultation_id')
#
# class Meta:
# model = Chat
# fields = ('chat_id')
#
class Meta:
chat = ChatDataForChatDetailSerializer(many=True)
model = ChatDetail
fields = ('chat_no', 'chat_from', 'chat')
I am trying to make a filter. But I got 'Meta.fields'; contains fields that are not defined on this FilterSet: ChatDetail, Chat" error.
My view is the following.
from .filtering import CategoryFilter
class WebViewChatDataOutputView(generics.RetrieveAPIView):
def get(self, request, format=None):
filter_backends = (
rest_framework.filters.OrderingFilter,
rest_framework.filters.DjangoFilterBackend
)
filter_class = CategoryFilter
serializer = WebViewChatDataInputSerializer(chat_detail, many=True)
return Response(serializer_class.data)
And my filtering.py is the following.
class CategoryFilter(rest_framework.filters.FilterSet):
name = django_filters.CharFilter(
name=ChatDetail,
lookup_expr='chat_id'
)
parent_category_name = django_filters.CharFilter(
name=Chat,
lookup_expr='chat_id'
)
class Meta:
model = ChatDetail
fields = ['ChatDetail', 'Chat']
Did I mistake any kind of grammar? Please gime an advice.
I updated my code.
views.py
class WebViewChatDataOutputView(generics.RetrieveAPIView):
def get(self, request, format=None):
lookup_field = 'chat_id'
queryset = Chat.objects.all()
serializer_class = ChatSerializer
return Response(serializer_class.data)
serializers.py
class ChatDetailSerializer(serializers.ModelSerializer):
class Meta:
model = ChatDetail
fields = '__all__'
class ChatSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='consultation_id.user.username') # You should rename the consultation_id foreign key field to consultation, by the way
clinic_name = serializers.CharField(source='consultation_id.clinic_id.clinic_name') # Same goes for clinic_id
chat_detail = ChatDetailSerializer(many=True, source='chatdetail_set')
class Meta:
model = Chat
fields = ('user_name', 'clinic_name', 'consultation_id', 'chat_id', 'chat_detail')
I put below couple solution to achieve filters for your endpoint.
You can use filter_class but to do it use ListApiView. Create filtering.py and add there something(for quick example):
import django_filters
import rest_framework.filters
from categories import models as categories_models
class CategoryFilter(rest_framework.filters.FilterSet):
name = django_filters.CharFilter(
name='name',
lookup_expr='icontains'
)
parent_category_name = django_filters.CharFilter(
name='parent_category__name',
lookup_expr='icontains'
)
class Meta:
model = categories_models.Category
fields = ['name', 'parent_category_name']
In your view add:
import rest_framework.filters
filter_backends = (
rest_framework.filters.OrderingFilter,
rest_framework.filters.DjangoFilterBackend
)
filter_class = categories_filtering.CategoryFilter
More details you find in ListApiView inspector
Its clear because you have another file only with filter logic and its seperated from your serializer.
You can use also **get_queryset but first u need add statment to check url for example:**
url contain q parametr with filter field
in method u need get q value and to something with these
write query to do it
but better idea is to use(u need change to Generic):
Filter_queryset
It seems to me that your problem is not filtering, in the DRF sense or the QuerySet sense, you simply need the right serializers and views for your models.
The output you shared refers to a given Chat. For the serializers, you'll need a ChatSerializer and a ChatDetailSerializer (for the list of ChatDetails).
Your can use the source argument in your serializer fields to retrieve data from your related models, so ChatSerializer could look something like:
class ChatSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='consultation_id.user.username') # You should rename the consultation_id foreign key field to consultation, by the way
clinic_name = serializers.CharField(source='consultation_id.clinic_id.clinic_name') # Same goes for clinic_id
chat_detail = ChatDetailSerializer(many=True, source='chatdetail_set')
class Meta:
model = Chat
fields = ('user_name', 'clinic_name', 'consultation_id', 'chat_id', 'chat_detail')
The ChatDetailSerializer can be a simple ModelSerializer
class ChatDetailSerializer(serializers.ModelSerializer):
class Meta:
model = ChatDetail
fields = '__all__'
In your views, DRF needs to know how to fetch the data. You can use get_queryset for that, but that does nothing if you implement in on an ApiView, use a RetrieveView instead, since you're showing one model instance (a ChatDetail). Or use some other generic view depending on your needs (see the DRF documentation on the subject, http://www.django-rest-framework.org/api-guide/generic-views/#retrieveapiview).
class WebViewChatDataOutputView(generics.RetrieveAPIView):
lookup_field = 'chat_id'
queryset = Chat.objects.all()
serializer_class = ChatSerializer
And that's it. I hope you figure it out.

Django - How to filter by date with Django Rest Framework?

I have some model with a timestamp field:
models.py
class Event(models.Model):
event_type = models.CharField(
max_length=100,
choices=EVENT_TYPE_CHOICES,
verbose_name=_("Event Type")
)
event_model = models.CharField(
max_length=100,
choices=EVENT_MODEL_CHOICES,
verbose_name=_("Event Model")
)
timestamp = models.DateTimeField(auto_now=True, verbose_name=_("Timestamp"))
I'm then using Django-rest-framework to create an API endpoint for this class, with django-filter providing a filtering functionality as follows:
from .models import Event
from .serializers import EventSerializer
from rest_framework import viewsets, filters
from rest_framework import renderers
from rest_framework_csv import renderers as csv_renderers
class EventsView(viewsets.ReadOnlyModelViewSet):
"""
A read only view that returns all audit events in JSON or CSV.
"""
queryset = Event.objects.all()
renderer_classes = (csv_renderers.CSVRenderer, renderers.JSONRenderer)
serializer_class = EventSerializer
filter_backends = (filters.DjangoFilterBackend,)
filter_fields = ('event_type', 'event_model', 'timestamp')
with the following settings:
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend',),
}
I'm able to filter by event_type and event_model, but am having trouble filtering by the timestamp field. Essentially, I want to make an API call that equates to the following:
AuditEvent.objects.filter(timestamp__gte='2016-01-02 00:00+0000')
which I would expect I could do as follows:
response = self.client.get("/api/v1/events/?timestamp=2016-01-02 00:00+0000", **{'HTTP_ACCEPT': 'application/json'})
though that is incorect. How do I make an API call that returns all objects with a timestamp greater than or equal to a certain value?
To expand on Flaiming's answer, if you're only ever going to be filtering via ISO datetime formats, it helps to overwrite the defaults to always use the IsoDateTimeFilter. This can be done per filterset with e.g.
import django_filters
from django.db import models as django_models
from django_filters import rest_framework as filters
from rest_framework import viewsets
class EventFilter(filters.FilterSet):
class Meta:
model = Event
fields = {
'timestamp': ('lte', 'gte')
}
filter_overrides = {
django_models.DateTimeField: {
'filter_class': django_filters.IsoDateTimeFilter
},
}
class EventsView(viewsets.ReadOnlyModelViewSet):
...
filter_class = EventFilter
You then won't have to worry about setting a different filter for each lookup expression and each field.
You can create specific FilterSet as follows:
import django_filters
from rest_framework import filters
from rest_framework import viewsets
class EventFilter(filters.FilterSet):
timestamp_gte = django_filters.DateTimeFilter(field_name="timestamp", lookup_expr='gte')
class Meta:
model = Event
fields = ['event_type', 'event_model', 'timestamp', 'timestamp_gte']
class EventsView(viewsets.ReadOnlyModelViewSet):
...
filter_class = EventFilter
Than you can filter by "/api/v1/events/?timestamp_gte=2016-01-02"
EDIT: Just to clarify, this example uses django-filter library.
IsoDateTimeFilter is very picky about the input format; instead of:
2016-01-02 00:00+0000
use:
2016-01-02T00:00:00Z
A better way is to filter datetime in get_queryset function
def get_queryset(self):
queryset = Event.objects.all()
start_date = self.request.query_params.get('start_date', None)
end_date = self.request.query_params.get('end_date', None)
if start_date and end_date:
queryset = queryset.filter(timstamp__range=[start_date, end_date])
None of the answers worked for me but this did:
class EventFilter(filters.FilterSet):
start = filters.IsoDateTimeFilter(field_name="start", lookup_expr='gte')
end = filters.IsoDateTimeFilter(field_name="end", lookup_expr='lte')
class Meta:
model = Event
fields = 'start', 'end',
I don't know what is the case you are looking for. Basically, you can access the params from the views by
date_params = self.request.query_params.get('params_name').
Then you can do
Event.objects.filter(date__lte=date_params, date__gte=date_params)
Give parameter in date format instead of timestamp
Since you want to filter by only date not timestamp, you can consider writing customized django_filters.FilterSet class.
You can do so by replacing django_filters.DateTimeFilter to django_filters.DateFilter, adding __date as suffix to your timestamp field_name and add lookup_expr as exact value.
Have a look at code below for example.
Model Class
class RegistrationLink(models.Model):
"""marketing campaign links"""
campaign_name = models.CharField("campaign name", max_length=128, unique=True)
url = models.URLField("URL", max_length=200, null=True, unique=True)
created_date = models.DateTimeField("created date and time", auto_now_add=True)
created_by = models.ForeignKey(User, verbose_name="user ID", on_delete=models.PROTECT)
visit_count = models.PositiveIntegerField("visit count", null=False, default=0)
FilterSet Class
class RegistrationLinkFilter(django_filters.FilterSet):
created_date = django_filters.DateFilter(field_name='created_date__date', lookup_expr="exact")
campaign_name = django_filters.CharFilter(lookup_expr='icontains')
class Meta:
model = RegistrationLink
fields = ['campaign_name', 'created_date']
class based view
import django_filters
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import OrderingFilter
# ...
class RegistrationLinkViewV2(ListAPIView):
"""Registration links for marketing campaigns"""
permission_classes = (IsAuthenticated, ManagePermission)
method = ''
queryset = RegistrationLink.objects.all()
serializer_class = LinkSerializer
pagination_class = PageNumberPagination
filter_backends = (DjangoFilterBackend, OrderingFilter,)
filterset_class = RegistrationLinkFilter
filterset_fields = ('campaign_name', 'created_date')
ordering_fields = ('campaign_name', 'created_date',
'url', 'created_by__username')
ordering = "-created_date"
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)

Django REST - Create object with foreign key using serializers

I'm a little new to Django and Django-REST so please bear with me. Perhaps the answer is in the documentation, so if I missed it, apologies in advance.
Goal: I would like to create an EquipmentInfo object whose attributes include pre-existing foreign keys (EquipmentType and EquipmentManufacturer).
models.py
class EquipmentType(models.Model):
equipment_type = models.CharField(verbose_name="Equipment Type", max_length=50, unique=True)
def __unicode__(self):
return self.equipment_type
class EquipmentManufacturer(models.Model):
manufacturer_name = models.CharField(verbose_name="Manufacturer Name", max_length=50, unique=True)
def __unicode__(self):
return self.manufacturer_name
class EquipmentInfo(models.Model):
equipment_type = models.ForeignKey(EquipmentType, verbose_name="Equipment Type")
part_identifier = models.CharField(verbose_name="Machine ID (alias)", max_length=25)
manufacturer_name = models.ForeignKey(EquipmentManufacturer, verbose_name="Manufacturer Name")
serial_number = models.CharField(verbose_name="Serial Number", max_length=25)
date_of_manufacture = models.DateField(verbose_name="Date of Manufacture", default=date.today)
is_active = models.BooleanField(verbose_name="Is Active", default=True)
def __unicode__(self):
return self.part_identifier
serializers.py
class EquipmentTypeSerializer(serializers.ModelSerializer):
class Meta:
model = EquipmentType
fields = ('id', 'equipment_type',)
class EquipmentManufacturerSerializer(serializers.ModelSerializer):
class Meta:
model = EquipmentManufacturer
fields = ('id', 'manufacturer_name',)
class EquipmentInfoSerializer(serializers.ModelSerializer):
class Meta:
model = EquipmentInfo
fields = ('id', 'equipment_type', 'part_identifier', 'manufacturer_name','serial_number', 'date_of_manufacture', 'is_active')
equipment_type = EquipmentTypeSerializer(many=False)
manufacturer_name = EquipmentManufacturerSerializer(many=False)
def create(self, validated_data):
equipment_type = validated_data.pop('equipment_type')
manufacturer_name = validated_data.pop('manufacturer_name')
equipment_info = EquipmentInfo.objects.create(**validated_data)
return equipment_info
Assuming I already have relevant EquipmentType and EquipmentManufacturer objects created, I would like to add another EquipmentInfo object. What is the appropriate way to set up my EquipmentInfo serializer so that I can pass in information such as
{
"equipment_type":{
"equipment_type":"already_created",
},
"part_identifier":"something_new",
"manufacturer_name":{
"manufacturer_name":"already_created"
},
"serial_number":"WBA1",
"date_of_manufacture": "1900-01-01",
"is_active":true
}
or even better:
{
"equipment_type":"already_created",
"part_identifier":"something_new",
"manufacturer_name":"already_created",
"serial_number":"WBA1",
"date_of_manufacture": "1900-01-01",
"is_active":true
}
Any help is appreciated.
I have also faced the problem ,and have solved it ,the following is my step ,hope it will be helpful
1.company Model and contact model as follows:
class Company(models.Model):
Company_Name = models.CharField(u'Company Name',max_length=255, default="")
Modified_By = models.CharField(u'Modified By',max_length=255, default="")
class Company_Contact(models.Model):
Company = models.ForeignKey(Company)
Last_Name = models.CharField(u'Last Name',max_length=255, default="")
First_Name = models.CharField(u'First Name',max_length=255, default="")
2.I create A New Serializer Named CompanyReferenceSerializer,and company_contact
class CompanyReferenceSerializer(serializers.ModelSerializer):
class Meta:
model = Company
fields = ['id', 'Company_Name', 'Company_Name_SC']
class CompanyContactSerializer(serializers.ModelSerializer):
Company = CompanyReferenceSerializer()
class Meta:
model = Company_Contact
fields = ['Company', 'Last_Name','First_Name']
extra_kwargs = {
'Company': {'allow_null': True, 'required': False},
'Last_Name': {'allow_null': True, 'allow_blank': True, 'required': False},
'First_Name': {'allow_null': True, 'required': False, 'allow_blank': True},
}
3.Viewset as follows,in the backend,I get the object Namedcompany_instanceaccording to the 'company_id'
class CompanyContactViewSet(viewsets.ModelViewSet):
serializer_class = CompanyContactSerializer
def create(self, validated_data):
serializer = self.get_serializer(data=self.request.data)
company_id_for_contact = self.request.data.pop('Company_id')
company_instance = Company.objects.filter(id=company_id_for_contact).first()
if not serializer.is_valid():
print serializer.errors
data = serializer.validated_data
serializer.save(Company=company_instance)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
and I success insert one record in the company_contact ,Hope it can help you !
Using nested serializers makes it really hard for posts (if it even works, as it didn't used to work), and given your simple models, I would recommend just removing them.
I will recommend you add APIs for
/api/v1/type
/api/v1/manufacturer
/api/v1/info
(or whatever names you want to use). The type and manufacturer ones should be vanilla views and using your existing serializers.
For info, remove the two nested serializers:
class EquipmentInfoSerializer(serializers.ModelSerializer):
class Meta:
model = EquipmentInfo
fields = ('id', 'equipment_type', 'part_identifier', 'manufacturer_name','serial_number', 'date_of_manufacture', 'is_active')
After that, you should be able to do post using:
data = {
"equipment_type": 5, # The ID of the equipment type record
"part_identifier":"something_new",
"manufacturer_name": 10 # The ID of the manufacturer record
"serial_number":"WBA1",
"date_of_manufacture": "1900-01-01",
"is_active":true
}
In my case, I do like making it the GETs more convenient so I add read-only fields to return a name (or even the whole serialized record):
class EquipmentInfoSerializer(serializers.ModelSerializer):
type_name = serializers.SerializerMethodField(read_only=True)
class Meta:
model = EquipmentInfo
fields = ('id', 'equipment_type', 'part_identifier', 'manufacturer_name','serial_number', 'date_of_manufacture', 'is_active')
def get_type_name(self, obj):
return obj.equipment_type.equipment_type
Hope this helps.

Categories

Resources