Это общепринятая практика, но у нее есть свои преимущества и недостатки. Фактическая передовая практика зависит от ваших реальных потребностей. Здесь, как вы предложили, при создании счета вам также необходимо отправить имя клиента в запросе, что не обязательно. Чтобы преодолеть эту потребность, одна возможная практика может быть следующей:
class ClientSerializer(serializers.ModelSerializer):
class Meta:
model = Client
fields = ['id', 'name']
class InvoiceSerializer(serializers.ModelSerializer):
client = serializers.PrimaryKeyRelatedField(queryset=Client.objects.all())
class Meta:
model = Invoice
fields = ['id', 'client', 'date', 'amount']
При таком подходе вы включаете только идентификатор клиента в сериализатор. При таком подходе вам нужно будет отправить только идентификатор клиента в реквизите, и вам не нужно писать собственный метод создания в сериализаторе. Недостаток такого подхода: при перечислении счетов у вас нет имени клиента. поэтому, если вам нужно отображать имя клиента при отображении счета-фактуры, нам нужно немного улучшить это решение:
class InvoiceSerializer(serializers.ModelSerializer):
client = serializers.PrimaryKeyRelatedField(queryset=Client.objects.all())
client_details = ClientSerializer(source='client', read_only=True)
class Meta:
model = Invoice
fields = ['id', 'client', 'client_details', 'date', 'amount']
При таком подходе мы добавили поле только для чтения, client_details , который хранит данные в клиентском серилайзере. Поэтому для операций записи мы используем поле client , которое является только идентификатором, а для чтения деталей о клиенте мы используем поле client_details .
Другой подход может быть определение отдельного клиентского сериализатора, который будет использоваться как дочерний сериализатор только в InvoiceSerializer:
class ClientSerializer(serializers.ModelSerializer):
class Meta:
model = Client
fields = ['id', 'name']
class InvoiceClientSerializer(serializers.ModelSerializer):
name = serializers.CharField(read_only=True)
class Meta:
model = Client
fields = ['id', 'name']
class InvoiceSerializer(serializers.ModelSerializer):
client = InvoiceClientSerializer()
class Meta:
model = Invoice
fields = ['id', 'client', 'date', 'amount']
def create(self, data):
client = Client.objects.get(pk=data['client']['id'])
invoice = Invoice(client=client,
date=datetime.fromisoformat(data['date']),
amount=Decimal(data['amount']))
invoice.save()
В этом подходе мы определили специальный клиентский сериализатор для использования только в InvoiceSerializer, у которого есть поле имени только для чтения. Таким образом, при создании / обновлении счета-фактуры вам не нужно отправлять имя клиента, но при перечислении счетов-фактур вы получите имя клиента. Преимущество этого подхода перед подходом перед использованием, нам не нужно использовать два отдельных поля для поля клиента для записи и чтения деталей.
Что касается вашего второго вопроса, DRF не поддерживает его из коробки, но вы можете взглянуть на этот пакет, который предоставляет эту функциональность и указан в собственной документации DRF: https://github.com/alanjds/drf-nested-routers