I am trying to use nested writable serializer in django-rest-framework. When I send a POST request with following data:
{
"acc_name": "Salary Card",
"acc_type_id": {
"id": 2,
"acc_type_name": "Debit Card"
},
"credit_amt": null,
"bill_dt": null,
"due_dt": null,
"balance": "0.00",
"comments": null
}
I got an error:
Cannot assign "OrderedDict([('acc_type_name', 'Debit Card')])": "Accounts.acc_type_id" must be a "AccountTypes" instance.
I actually passed id for AccountTypes, but why restframework remove it automatically? How can I resolve this problem? How can I create a new account with existing account type?
Views:
class AccountTypesViewSet(ListAPIView):
name = __name__
pagination_class = None
queryset = AccountTypes.objects.filter(active='Y')
serializer_class = AccountTypesSerializer
class AccountsViewSet(viewsets.ModelViewSet):
pagination_class = None
queryset = Accounts.objects.all()
serializer_class = AccountsSerializer
Models:
class AccountTypes(models.Model):
id = models.AutoField(primary_key=True, editable=False)
acc_type_name = models.CharField(max_length=50)
active = models.CharField(max_length=1, default='Y')
class Meta:
db_table = f'"{schema}"."taccount_types"'
managed = False
class Accounts(models.Model):
id = models.AutoField(primary_key=True, editable=False)
acc_name = models.CharField(max_length=1024)
acc_type_id = models.ForeignKey(
to=AccountTypes,
db_column='acc_type_id',
related_name='accounts',
on_delete=models.DO_NOTHING
)
credit_amt = models.DecimalField(max_digits=11, decimal_places=2, null=True)
bill_dt = models.DateField(null=True)
due_dt = models.DateField(null=True)
balance = models.DecimalField(max_digits=11, decimal_places=2, default=0)
comments = models.CharField(max_length=1024, null=True)
class Meta:
db_table = f'"{schema}"."taccounts"'
managed = False
Serializers:
class AccountTypesSerializer(serializers.ModelSerializer):
class Meta:
model = AccountTypes
fields = ('id', 'acc_type_name')
class AccountsSerializer(serializers.ModelSerializer):
# acc_type = serializers.SlugRelatedField(slug_field='acc_type_name', source='acc_type_id', read_only=True)
acc_type_id = AccountTypesSerializer()
class Meta:
model = Accounts
fields = ('id', 'acc_name', 'acc_type_id', 'credit_amt',
'bill_dt', 'due_dt', 'balance', 'comments')
def create(self, validated_data):
accounts_instance = Accounts.objects.create(**validated_data)
return accounts_instance
You don't need to set id field because Django will do it by itself, so remove that lines:
id = models.AutoField(primary_key=True, editable=False)
Next, you need to send an id of AccountTypes model to create new account and you give Django a dict fields = ('id', 'acc_type_name'). It doesn't know what to do with that. So just send id as number. And you don't need AccountTypesSerializer and that line acc_type_id = AccountTypesSerializer() also.
Next, you should create account in view, not in serializer, and you have to save it after creating.
Related
I have my DRF app. In my case, one wallet can have many entries such as income or expense. When I call my endpoint (viewset) I get data in this format:
[
{
"id": "d458196e-49f1-42db-8bc2-ee1dba438953",
"owner": 1,
"viewable": [],
"entry": []
}
]
How can I get the content of "entry" variable?.
class Category(models.Model):
name = models.CharField(max_length=20, unique=True)
def __str__(self):
return self.name
class BudgetEntry(models.Model):
STATE= [
('income','income'),
('expenses','expenses'),
]
amount = models.IntegerField()
entry_type = models.CharField(max_length=15, choices=STATE, null=True)
entry_category = models.ForeignKey(Category, null=True, blank=True, on_delete=models.SET_NULL)
class WalletInstance(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique=True)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='owner', on_delete=models.CASCADE)
viewable = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='can_view', blank=True)
entry = models.ManyToManyField(BudgetEntry, related_name='BudgetEntry', blank=True)
Serializers.py:
class BudgetEntrySerializer(serializers.ModelSerializer):
class Meta:
model = BudgetEntry
fields = '__all__'
class WalletInstanceSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.id')
class Meta:
model = WalletInstance
fields = '__all__'
Views.py:
class WalletViewset(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated]
serializer_class = WalletInstanceSerializer
def get_queryset(self):
user_id = self.request.user.id
available = WalletInstance.objects.filter(
Q(owner=user_id)
)
return available
Change your serializer like this:
class WalletInstanceSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.id')
entry = BudgetEntrySerializer(many=True, read_only=True)
class Meta:
model = WalletInstance
fields = '__all__'
I have seen many tutorials about nested serializer, but unfortunately I can`t solve this task. Please, give me some tips.
I need to create this JSON
{
"external_id": "11",
"details": [
{
"amount": 7,
"price": "12.00",
"product": {
"name": "Car"
}
}
]
}
My models consist the next relative:
from django.db import models
class Order(models.Model):
NEW = 'new'
ACCEPTED = 'accepted'
FAILED = 'failed'
order_status = [
(NEW, 'new'),
(ACCEPTED, 'accepted'),
(FAILED, 'failed'),
]
status = models.CharField(max_length=12, choices=order_status, default='new', blank=False)
created_at = models.DateTimeField(auto_now_add=True)
external_id = models.CharField(max_length=128)
def __str__(self):
return f'Order № {self.external_id}'
class Product(models.Model):
name = models.CharField(max_length=64)
def __str__(self):
return self.name
class OrderDetail(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE,
related_name='details',
null=True, blank=True)
amount = models.IntegerField(null=True, blank=True)
product = models.ForeignKey(Product, on_delete=models.CASCADE,
related_name='product',
null=True)
price = models.DecimalField(decimal_places=2, max_digits=6, null=True, blank=True)
def __str__(self):
return f'Detail for {self.order}, detail for product {self.product}'
My view
class ProductViewSet(ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
class OrderViewSet(ModelViewSet):
queryset = Order.objects.all()
serializer_class = OrderSerializer
pagination_class = ContentRangeHeaderPagination
class OrderDetailViewSet(ModelViewSet):
queryset = OrderDetail.objects.all()
serializer_class = OrderDetailSerializer
My serializer
class OrderDetailSerializer(serializers.ModelSerializer):
class Meta:
model = OrderDetail
fields = ['id', 'amount', 'price']
depth = 1
class ProductSerializer(serializers.ModelSerializer):
product = OrderDetailSerializer(many=True)
class Meta:
model = Product
fields = ['id', 'name', 'product']
class OrderSerializer(serializers.ModelSerializer):
details = OrderDetailSerializer(many=True)
class Meta:
model = Order
fields = ['id', 'status', 'created_at', 'external_id', 'details']
depth = 2
def create(self, validated_data): # works for first nesting
print(validated_data)
details_data = validated_data.pop('details')
request = Order.objects.create(**validated_data)
for detail_data in details_data: #
products_data = detail_data.pop('product')
request_detail = OrderDetail.objects.create(order=request, **detail_data)
for product_data in products_data:
Product.objects.create(product=request_detail, **product_data)
return request
I have errors when I try to send POST request. => KeyError 'products'
I wanted to get product fields using a loop. But I can't get this field, because I didn't identified it.
My question is: how to receive this field in OrderSerializer.
Thanks for your answers.
I cannot save multiple values for the Foreignkey field when adding instances to the database. I don't understand exactly what the problem is: in my code or in the format of the JSON object being passed.
models.py
class VendorContacts(models.Model):
contact_id = models.AutoField(primary_key=True)
vendor = models.OneToOneField('Vendors', on_delete=models.CASCADE)
contact_name = models.CharField(max_length=45, blank=True)
phone = models.CharField(max_length=45, blank=True)
email = models.CharField(max_length=80, blank=True, unique=True)
class Meta:
db_table = 'vendor_contacts'
class VendorModuleNames(models.Model):
vendor = models.OneToOneField('Vendors', on_delete=models.CASCADE, primary_key=True)
module = models.ForeignKey(Modules, models.DO_NOTHING)
timestamp = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'vendor_module_names'
unique_together = (('vendor', 'module'),)
class Vendors(models.Model):
COUNTRY_CHOICES = tuple(COUNTRIES)
vendorid = models.AutoField(primary_key=True)
vendor_name = models.CharField(max_length=45, unique=True)
country = models.CharField(max_length=45, choices=COUNTRY_CHOICES)
nda = models.DateField(blank=True, null=True)
user_id = models.ForeignKey('c_users.CustomUser', on_delete=models.PROTECT)
timestamp = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'vendors'
unique_together = (('vendorid', 'timestamp'),)
serializers.py
class VendorsSerializer(serializers.ModelSerializer):
class Meta:
model = Vendors
fields = ('vendor_name',
'country',
'nda',
'parent_vendor',)
class VendorContactSerializer(serializers.ModelSerializer):
class Meta:
model = VendorContacts
fields = (
'contact_name',
'phone',
'email',)
class VendorModulSerializer(serializers.ModelSerializer):
class Meta:
model = VendorModuleNames
fields = ('module',)
views.py
class VendorsCreateView(APIView):
"""Create new vendor instances from form"""
serializer_class = (VendorsSerializer)
def post(self, request, *args, **kwargs):
vendor_serializer = VendorsSerializer(data=request.data)
vendor_contact_serializer = VendorContactSerializer(data=request.data)
vendor_modules_serializer = VendorModulSerializer(data=request.data)
try:
vendor_serializer.is_valid(raise_exception=True) \
and vendor_contact_serializer.is_valid(raise_exception=True) \
and vendor_modules_serializer.is_valid(raise_exception=True) \
vendor = vendor_serializer.save(user_id=request.user)
vendor_contact_serializer.save(vendor=vendor)
vendor_modules_serializer.save(module= maybe something here?????, vendor=vendor)
except ValidationError:
return Response({"errors": (vendor_serializer.errors,
vendor_contact_serializer.errors,
vendor_modules_serializer.errors
)},
status=status.HTTP_400_BAD_REQUEST)
else:
return Response(request.data, status=status.HTTP_200_OK)
JSON body
{
"vendor_name": "Awrazofgsxsdsxjwszsslsasdjegdzasas",
"country": "Canada",
"module": [
1,
2
],
"NDA date": "",
"contact_name": "Tim",
"email": "teaszt#tesstd.gmail",
"phone": "+3464784594940",
"parent_vendor": "23"
}
When I send JSON, I get the response
{
"module": [
"Incorrect type. Expected pk value, received list."
]
}
Looks like I'm finally confused about multiple saving
Your ForeignKey should be set on the related class Modules.
I am building a Django/React App to allow users to submit orders that need to go from A to B. The user initially saves the addresses in the database and then he/she selects it in the order form. When they submit I attempt to create a relationship in the database, I'm using Django Rest Framework serializers to create the Order object in the database.
Unfortunately, I'm unable to successfully save the items as I'm not properly linking the addresses to the order. Im getting the following error:
destinationAddress: ["Invalid value."]
originAddress: ["Invalid value."]
Models
class Order(models.Model):
originAddress = models.ForeignKey(Address, related_name="originAddress", null=True, on_delete=models.CASCADE)
destinationAddress = models.ForeignKey(Address, related_name="destinationAddress", null=True, on_delete=models.CASCADE)
packages = models.CharField("Packages", max_length=1024)
class Address(models.Model):
address_code = models.CharField(max_length=250)
contact = models.CharField("Contact", max_length=1024)
phone = models.CharField("Phone", max_length=20)
company = models.CharField("Company", max_length=250)
addressLine1 = models.CharField("Address line 1", max_length=1024)
addressLine2 = models.CharField("Address line 2", max_length=1024, blank=True)
postalCode = models.CharField("Postal Code", max_length=12)
city = models.CharField("City", max_length=1024)
state = models.CharField("State", max_length=250)
Serializers
class AddressSerializer(serializers.ModelSerializer):
class Meta:
model = Address
fields = '__all__'
class OrderSerializer(serializers.ModelSerializer):
originAddress = serializers.SlugRelatedField(
queryset=Address.objects.all(),
slug_field='pk'
)
destinationAddress = serializers.SlugRelatedField(
queryset=Address.objects.all(),
slug_field='pk'
)
class Meta:
model = Order
fields = ('id', 'packages', 'destinationAddress', 'originAddress')
ViewSets
class OrderViewSet(viewsets.ModelViewSet):
queryset = Order.objects.all()
permission_classes = [
permissions.AllowAny
]
serializer_class = OrderSerializer
class AddressViewSet(viewsets.ModelViewSet):
queryset = Address.objects.all()
permission_classes = [
permissions.AllowAny
]
serializer_class = AddressSerializer
Any ideas? Thanks
Solved it by replacing SlugRelatedField to PrimaryKeyRelatedField
class OrderSerializer(serializers.ModelSerializer):
originAddress = serializers.PrimaryKeyRelatedField(
queryset=Address.objects.all(), allow_null=True, required=False
)
destinationAddress = serializers.PrimaryKeyRelatedField(
queryset=Address.objects.all(), allow_null=True, required=False
)
class Meta:
model = Order
fields = ('id', 'packages', 'destinationAddress', 'originAddress')
from django.db import models
class Customer(models.Model):
cust_firstname=models.TextField(max_length=50)
cust_lastname=models.TextField(max_length=50)
cust_company=models.TextField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
cust_contact_number = models.IntegerField()
cust_email = models.TextField(max_length=100)
cust_address=models.TextField(max_length=200,default=None)
class Employee(models.Model):
employee_firstname = models.TextField(max_length=50)
employee_lastname = models.TextField(max_length=50)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
employee_contact_number = models.IntegerField()
employee_email = models.TextField(max_length=100)
employee_designation = models.TextField(max_length=50)
employee_address=models.TextField(max_length=200, default=None)
class ProjectDetails(models.Model):
customer = models.ForeignKey(Customer)
employee=models.ForeignKey(Employee)
project_name = models.TextField(max_length=50)
project_startdate = models.DateTimeField(auto_now=False, default=None)
project_status = models.TextField(default="Initial")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
The above code is my model declaration
and my Serializer class is
from ProjectTemplate.models import Customer, Employee, ProjectDetails
from rest_framework import serializers
class CustomerSerializers(serializers.ModelSerializer):
class Meta:
model=Customer
fields = ('id','cust_firstname','cust_lastname','cust_company','created_at','updated_at','cust_contact','cust_email','cust_address')
read_only_fields = ('created_at', 'updated_at')
class EmployeeSerializer(serializers.ModelSerializer):
class Meta:
model=Employee
fields = ('id','employee_firstname','employee_lastname','created_at','updated_at','employee_contact','employee_email','employee_designation','employee_address')
read_only_fields = ('created_at', 'updated_at')
class ProjectDetailsSerializer(serializers.ModelSerializer):
customer = CustomerSerializers(many=True, read_only=True)
employee = EmployeeSerializer(many=True, read_only=True)
class Meta:
model = ProjectDetails
fields = ('id','project_name','project_startdate','created_at','updated_at','project_status','customer','employee')
read_only_fields = ('created_at', 'updated_at')
In my view i have the below code
def get(self, request, format=None):
queryset = ProjectDetails.objects.all()
projectid = self.request.query_params.get('pk', None)
if projectid is not None:
queryset = queryset.get(id=projectid)
serializer = ProjectDetailsSerializer(queryset, many=False)
return Response(serializer.data)
else:
serializer = ProjectDetailsSerializer(queryset, many=True)
return Response(serializer.data)
And my URL for the above view is
url(r'^api/v1/projects/$', ProjectListView.as_view()),
And when i try to access the URL on my Browser i get TypeError. Im trying to get all the Customers and Employees who belong to a project. But it fails can any one help me to fix this.
I'm trying to get all the Customers and Employees who belong to a project.
I am not sure what do you want to achieve here because looking at your model, an instance of ProjectDetail will only have one customer and one employee:
class ProjectDetails(models.Model):
customer = models.ForeignKey(Customer)
employee=models.ForeignKey(Employee)
Considering this, using many=True doesn't make any sense in the serializer. So this is what causing the error:
class ProjectDetailsSerializer(serializers.ModelSerializer):
customer = CustomerSerializers(many=True, read_only=True)
employee = EmployeeSerializer(many=True, read_only=True)
UPDATE: To show a specific field from the related object:
Based on the comments in the answer the OP want to show the name of customer or employee instead of id.
It can be achieved using a SlugRelatedField:
class ProjectDetailsSerializer(serializers.ModelSerializer):
customer = serializers.SlugRelatedField(slug_field='cust_firstname', read_only=True)
employee = serializers.SlugRelatedField(slug_field='employee_firstname ', read_only=True)
# other fields
Do note that using SlugRelatedField you can get only one field as in the example above you would get firstname for both.