I've been trying to create a serializer for a Model in which one of the fields is a ManytoManyField through a Model that adds more fields. The problem is that the intermediate serializer is not recognizing that has added fields. Why could I be doing wrong?
Here is my code:
models.py:
class Product(models.Model):
name = models.CharField(max_length=30, unique=True)
class Movement(models.Model):
date = models.DateTimeField(auto_now_add=True)
products = models.ManyToManyField(Product, through='Movement_Product')
class Movement_Product(models.Model):
movement = models.ForeignKey(Movement)
product = models.ForeignKey(Product)
amount = models.IntegerField()
price = models.DecimalField(max_digits=9, decimal_places=2)
class Input(Movement):
invoice_number = models.CharField(max_length=30, null=True)
serializers.py:
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
class MovementProductSerializer(serializers.ModelSerializer):
product = ProductSerializer()
price = serializers.DecimalField(max_digits=9, decimal_places=2)
amount = serializers.IntegerField()
class Meta:
model = Movement_Product
class InputSerializer(serializers.ModelSerializer):
date = serializers.DateTimeField()
products = MovementProductSerializer(many=True)
class Meta:
model = Input
views.py:
class InputViewSet(viewsets.ModelViewSet):
queryset = Input.objects.order_by('-date')
serializer_class = InputSerializer
urls.py:
router = routers.DefaultRouter()
router.register(r'input', views.InputViewSet)
urlpatterns = [
url(r'^api/', include(router.urls)),
url(r'^admin/', admin.site.urls),
]
The error I got when I try to render the InputSerializer on URL path in my browser http://127.0.0.1:8000/api/input/:
Attribute Error at /api/input/
Got AttributeError when attempting to get a value for field product on serializer MovementProductSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the Product instance.
Original exception text was: 'Product' object has no attribute 'product'.
try this:
class Movement(models.Model):
date = models.DateTimeField(auto_now_add=True)
products = models.ManyToManyField(Product, through='Movement_Product')
#property
def movement_product(self):
return Movement_Product.objects.filter(movement=self)
class InputSerializer(serializers.ModelSerializer):
date = serializers.DateTimeField()
products = serializers.ListField(child=MovementProductSerializer(), source='movement_product')
class Meta:
model = Input
Related
I have two models, which look like this:
class Item(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=60)
sku = models.CharField(max_length=60)
description = models.TextField()
price = models.DecimalField(max_digits=6, decimal_places=2)
location = models.CharField(max_length=60)
serial_number = models.CharField(max_length=60)
def __str__(self):
return self.name
class Warehouse(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=60)
def __str__(self):
return self.name
and they have two serializers which look like this:
class ItemSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Item
fields = ('id', 'name', 'sku', 'description', 'price', 'location', 'serial_number')
#we need a validator that checks if location is in the list of warehouses
#we need a validator that checks if sku is in the list of products
class WarehouseSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Warehouse
fields = ('id', 'name')
I need a way to ensure that the location field for newly created items matches an existing name field from a warehouse. I also need the deletion of a warehouse to trigger the deletion of all items in that warehouse, or, failing that; if the warehouse has items, it cannot be deleted.
I'm brand new to python and django, so any help would be massively appreciated!
for reference, my views class looks like
class ItemViewSet(viewsets.ModelViewSet):
queryset = Item.objects.all().order_by('name')
serializer_class = ItemSerializer
class WarehouseViewSet(viewsets.ModelViewSet):
queryset = Warehouse.objects.all().order_by('name')
serializer_class = WarehouseSerializer
if that helps, but from what I can see I don't expect it to.
Thanks in advance!
I think the problem here is your data models. It's clear that a warehouse and an item have a one to many relationship. With that, you would have something like this in your models.
from django.db import models
class Warehouse(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=60)
def __str__(self):
return self.name
class Item(models.Model):
id = models.AutoField(primary_key=True)
warehouse = models.ForeignKey(Warehouse, related_name="items", on_delete=models.CASCADE)
name = models.CharField(max_length=60)
sku = models.CharField(max_length=60)
description = models.TextField()
price = models.DecimalField(max_digits=6, decimal_places=2)
location = models.CharField(max_length=60)
serial_number = models.CharField(max_length=60)
def __str__(self):
return self.name
The on_delete=models.CASCADE will ensure that all items related to a warehouse are deleted when a warehouse is deleted. The foreign key relationship will ensure that warehouse id you provide when creating the item, exists before the item is created.
The other files will look as follows.
serializers.py
from rest_framework import serializers
from .models import Warehouse, Item
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = ('id', 'name', 'sku', 'description', 'price', 'location', 'serial_number', "warehouse")
class WarehouseSerializer(serializers.HyperlinkedModelSerializer):
items = serializers.StringRelatedField(many=True, required=False)
class Meta:
model = Warehouse
fields = ('id', 'name', 'items')
views.py
from .models import Item, Warehouse
from .serializers import ItemSerializer, WarehouseSerializer
from rest_framework import viewsets
class ItemViewSet(viewsets.ModelViewSet):
queryset = Item.objects.all().order_by('name')
serializer_class = ItemSerializer
class WarehouseViewSet(viewsets.ModelViewSet):
queryset = Warehouse.objects.all().order_by('name')
serializer_class = WarehouseSerializer
Please check me this error with serializers.
I have two model Customer and Factor:
models.py:
class Customer(models.Model):
customer_name = models.CharField(max_length=120 ,verbose_name='بنام')
final_price = models.DecimalField(max_digits=20, decimal_places=0, default=0, verbose_name='مبلغ کل فاکتور')
def __str__(self):
return f'{self.customer_name}'
class Factor(models.Model):
title = models.CharField(max_length=120 ,verbose_name='صورتحساب')
name = models.ForeignKey(Customer,on_delete=models.CASCADE,verbose_name='بنام' ,related_name='factor_set')
description = models.CharField(max_length=200 ,verbose_name='شرح کالا')
price = models.DecimalField(max_digits=20, decimal_places=0, default=0.0,verbose_name='قیمت واحد')
count =models.DecimalField(max_digits=20,decimal_places=0, default=1,verbose_name='تعداد')
date = models.DateTimeField(default=datetime.datetime.now,null=True,verbose_name='تاریخ')
def __str__(self):
return f'{self.name}'
serializer.py:
class FactorModelSerializer(serializers.ModelSerializer):
name = serializers.StringRelatedField(many=True)
class Meta:
model = Factor
fields = '__all__'
class CustomerModelSerializer(serializers.ModelSerializer):
factor_set=FactorModelSerializer(many=True)
class Meta:
model = Customer
fields = '__all__'
views.py:
class GetAllData__(APIView):
def get(self,request):
query = Customer.objects.all()
serializer=CustomerModelSerializer(query)
return Response(serializer.data ,status=status.HTTP_200_OK)
urls.py :
from factor.views import GetAllData,GetAllData__
urlpatterns = [
path('get-all-data--', GetAllData__.as_view()),
]
error :
AttributeError at /get-all-data--
Got AttributeError when attempting to get a value for field customer_name on serializer CustomerModelSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the QuerySet instance.
Original exception text was: 'QuerySet' object has no attribute 'customer_name'.
you need to provide source as 'factor_set' since Your serializer is searching from where to to put customer_name
class FactorModelSerializer(serializers.ModelSerializer):
name = serializers.StringRelatedField(many=True)
class Meta:
model = Factor
fields = '__all__'
class CustomerModelSerializer(serializers.ModelSerializer):
factor_set=FactorModelSerializer(many=True,source='factor_set')
class Meta:
model = Customer
fields = '__all__'
I'm using Django rest framework. In serializer I used HyperlinkedModelSerializer, but there are some foreign key fields with detail-view so it can be rendered as url, but some of them don't, it gives me the following error:
Could not resolve URL for hyperlinked relationship using view name "unit-detail". You may have failed to include the related model in your API, or incorrectly configured the `lookup_field` attribute on this field
So how to fix it?
serializers.py
class MaterialSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Material
fields = ('id', 'url', 'name', 'location', 'category', 'unit', 'type', 'price',)
views.py
class MaterialViewSet(viewsets.ModelViewSet):
queryset = Material.objects.all()
serializer_class = MaterialSerializer
models.py
class Material(models.Model):
name = models.CharField(max_length=200)
location = models.ForeignKey(Location, null=True, on_delete=models.CASCADE)
category = models.ForeignKey(Category, null=True, on_delete=models.CASCADE)
unit = models.ForeignKey(Unit, null=True, on_delete=models.CASCADE)
type = models.CharField(choices=TYPE_CHOICES, default='material', max_length=200)
price = models.FloatField(default=0, blank=True, null=True)
urls.py
router = DefaultRouter()
router.register('users', user_views.UserViewSet, 'users')
router.register('profiles', profile_views.ProfileViewSet, 'profile')
router.register('location', LocationViewSet, 'location')
router.register('category', CategoryViewSet)
router.register('materials', MaterialViewSet)
router.register('supplier', SupplierViewSet)
router.register('transaction', TransactionViewSet)
urlpatterns = [
path('v1/', include(router.urls)),
]
So in my API, I want to be my foreign key fields 'location' and 'category' with URL and 'unit' field only id
The error Could not resolve URL for hyperlinked relationship using view name "unit-detail". you are seeing because in your MaterialSerializers.py
you are using unit as a field and there is no url for unit .
Solution :
add a new view UnitViewset
serializers.py
class UnitSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Unit
fields = "__all__"
views.py
class UnitViewSet(viewsets.ModelViewSet):
queryset = Unit.objects.all()
serializer_class = UnitSerializer
urls.py
router.register('units',UnitViewSet)
edit in your models.py
if you expecting blank values in Foreignkey fields for Material model add blank=True
class Material(models.Model):
name = models.CharField(max_length=200)
location = models.ForeignKey(Location, null=True,blank=True, on_delete=models.CASCADE)
category = models.ForeignKey(Category, null=True,blank=True, on_delete=models.CASCADE)
unit = models.ForeignKey(Unit, null=True,blank=True, on_delete=models.CASCADE)
type = models.CharField(default='material',blank=True, max_length=200)
price = models.FloatField(default=0,blank=True, null=True)
serializers.py
After adding this two variables (category,type_units) for dropdown post method not working. it through 500 server error And also added my view.py models.py
class ProductSerializer(serializers.HyperlinkedModelSerializer):
category = serializers.CharField(source='category.category', read_only=True)
type_units = serializers.CharField(source='type_units.type', read_only=True)
class Meta:
model = Product
fields = ('id','image','pro_name','description','category','sales','cost','taxable','type_units','hsn')
Models.py
class Units(models.Model):
type = models.CharField(max_length=10)
class Category(models.Model):
category = models.CharField(max_length=10)
class Product(models.Model):
image = models.ImageField(upload_to = "images/",blank=True,null=True)
pro_name = models.CharField(max_length=25)
description = models.CharField(max_length=150)
category = models.ForeignKey(Category,on_delete=models.CASCADE)
sales = models.CharField(max_length=25)
cost = models.CharField(max_length=25)
taxable = models.BooleanField(default=False, blank=True)
type_units = models.ForeignKey(Units, on_delete=models.CASCADE)
hsn = models.CharField(max_length=10)
views.py
class ProductViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows groups to be viewed or edited.
"""
queryset = Product.objects.all()
serializer_class = ProductSerializer
class UnitsViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows groups to be viewed or edited.
"""
queryset = Units.objects.all()
serializer_class = UnitsSerializer
class CategoryViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows groups to be viewed or edited.
"""
queryset = Category.objects.all()
serializer_class = CategorySerializer
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.