Django Python 'Customer' object is not iterable - python

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.

Related

How to filter/hide fields in django rest framework HTML form using authenticated user

I created two models:
parcel (package) model,
'shelf' model to which parcels can be assigned.
class Parcel(models.Model):
owner = models.ForeignKey(get_user_model(), on_delete=models.PROTECT)
name = models.CharField(max_length=100, blank=False)
contents = models.TextField(blank=False, validators=[MaxLengthValidator(1500)])
size = models.CharField(max_length=50, blank=True, validators=[package_size_validator])
weight = models.PositiveIntegerField(blank=True, null=True)
contact = models.CharField(max_length=50, blank=True)
creation_date = models.DateTimeField(auto_now_add=True)
last_modification = models.DateTimeField(auto_now=True)
code = models.CharField(max_length=30, unique=True, blank=False)
def __str__(self):
return f'Parcel: {self.code}, {self.owner}, {self.name}'
class ParcelShelf(models.Model):
owner = models.ForeignKey(get_user_model(), on_delete=models.PROTECT)
name = models.CharField(max_length=100, blank=False)
creation_date = models.DateTimeField(auto_now_add=True)
last_modification = models.DateTimeField(auto_now=True)
parcels = models.ManyToManyField('Parcel', blank=True, related_name='shelf_parcel')
def __str__(self):
return f'ParcelShelf: {self.owner}, {self.name}'
I came to a solution where the logged-in user can see only his packages and shelves. The problem I have is with the many-to-many relationship where parcels can be added to shelves. I want to come to a solution where the logged in user can add to the shelf only those parcels which he is the owner, creator. It will look better in pictures.
All packages created by user t2#t2.com (user id = 17):
parcels list
Now the view when the user t2#t2.com wants to create a shelf:
shelf list
All packages are visible, while only those created by the user t2#t2.com should be available.
Code to serializer:
class ParcelShelfSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.email')
parcels = serializers.HyperlinkedRelatedField(many=True, read_only=False, view_name='parcels_detail_view',
# queryset=Parcel.objects.filter(owner=17)
queryset=Parcel.objects.all()
)
class Meta:
model = ParcelShelf
fields = ('id', 'owner', 'name', 'creation_date', 'last_modification', 'parcels')
Below is a picture where only packages for a given, logged-in user are available:
shelf list
Code to serializer:
class ParcelShelfSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.email')
parcels = serializers.HyperlinkedRelatedField(many=True, read_only=False, view_name='parcels_detail_view',
queryset=Parcel.objects.filter(owner=17)
# queryset=Parcel.objects.all()
)
class Meta:
model = ParcelShelf
fields = ('id', 'owner', 'name', 'creation_date', 'last_modification', 'parcels')
I got to the point where the 'solution' is in the 'queryset' argument.
All users: queryset=Parcel.objects.all()
Logged in user: queryset=Parcel.objects.filter(owner=17)
The problem is, this is hardcoded, and it should be something like: (owner=request.user). Unfortunately, I don't know how to achieve this in the serializer. I looked through other similar topics, but I didn't find a solution how to use the request method in the serializer field.
In addition, code in views:
class ParcelsShelfList(generics.ListCreateAPIView):
# queryset = ParcelShelf.objects.all()
serializer_class = ParcelShelfSerializer
def get_queryset(self):
user = self.request.user
if bool(user and user.is_staff and user.is_admin):
return ParcelShelf.objects.all()
else:
return ParcelShelf.objects.filter(owner=user)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
UPDATE
Thanks to the help of #Amrez, who gave me a link to a similar topic, i was able to do it.
mentioned link: How can I filter DRF serializer HyperlinkedRelationField queryset based on request data?
I add this to my code in serializers.py:
def hyperlinked_related_field_by_owner(model, view_name, owner):
return serializers.HyperlinkedRelatedField(
many=True,
view_name=view_name,
queryset=model.objects.filter(owner=owner)
)
class ParcelShelfSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.email')
parcels = serializers.HyperlinkedRelatedField(many=True,
read_only=False,
view_name='parcels_detail_view',
# queryset=Parcel.objects.filter(owner=17)
queryset=Parcel.objects.all()
)
def get_fields(self):
fields = super().get_fields()
owner = self.context['request'].user
fields['parcels'] = hyperlinked_related_field_by_owner(Parcel, 'parcels_detail_view', owner)
return fields
class Meta:
model = ParcelShelf
fields = ('id', 'owner', 'name', 'creation_date', 'last_modification', 'parcels')

How do you add validation to django rest framework api

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

Django admin export issue

admin.site.register(RegistrationOTP)
class RegistrationOTPResource(resources.ModelResource):
class Meta:
model = RegistrationOTP
export_order = ('country','phone_number','otp','created_date','is_verified')
class RegistrationOTPAdmin(ImportExportModelAdmin):
resource_class = RegistrationOTPResource
here is models.py
class RegistrationOTP(models.Model):
country = models.ForeignKey(Country,on_delete=models.CASCADE, related_name='otp_country', default=1)
phone_number = models.CharField(max_length=13)#, unique=True, validators=[phone_regex],
otp = models.PositiveIntegerField()
created_date = models.DateTimeField(auto_now=True)
is_verified = models.CharField(choices=YES_OR_NO, default='no', max_length=10)
def __unicode__(self):
return '%s : %s'% (self.phone_number, self.otp)
Here i am doing as per documentation but it is not showing export csv option in admin
class RegistrationOTPResource(resources.ModelResource):
class Meta:
model = RegistrationOTP
fields = '__all__'
export_order = ('id','country','phone_number','otp','created_date','is_verified')
try this, i guess due to missing fields section, it is not working

Object of type 'ListSerializer' is not JSON serializable

I want to get all customer data and responses and also remarks.
This is model.py
class Customer(models.Model):
name = models.CharField(max_length=200)
email_address = models.CharField(max_length=200)
phone_number = models.CharField(max_length=20)
age = models.SmallIntegerField(default=14)
remarks = models.ManyToManyField(Remark,null=True,blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.id)
class Response(models.Model):
question = models.ForeignKey(Question)
customer = models.ForeignKey(Customer)
response_text = models.CharField(max_length=100, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
uuid = models.UUIDField()
def __str__(self):
return str(self.id)
This is serializers.py
class ResponseSerializer(ModelSerializer):
class Meta:
model = Response
fields = '__all__'
class RemarksSerializer(ModelSerializer):
class Meta:
model = Remark
fields = '__all__'
class CustomerInformationSerializer(ModelSerializer):
remarks = RemarksSerializer(many=True)
responses = serializers.SerializerMethodField()
def get_responses(self, obj):
responses = Response.objects.filter(customer=obj)
return ResponseSerializer(responses, many=True)
class Meta:
model = Customer
fields = ('name', 'email_address', 'phone_number', 'age', 'remarks', 'responses')
This is services.py
def customer_information(company_id=1):
cus = Customer.objects.filter(remarks__company_id=company_id)
return CustomerInformationSerializer(cus, many=True).data
This is views.py
class CustomerInformationView(APIView):
def get(self, request):
company_id = request.GET.get('company_id', 1)
resp = {'data': customer_information(company_id)}
return Response(data=resp, status=status.HTTP_200_OK)
This is url.py
url(r'^customer/$', CustomerInformationView.as_view()),
I'm having this problem. How can I solve this. Kindly guide me.
get function in your view should return responses.data, insted of responsed.
SIDE NOTE
First, let me point you to a resource that I think is GREAT for anything dealing with Django REST Framework:
Classy Django REST Framework. It is a fantastic resource because you can easily dig right into the source code to see how you may or may not need to override default operations.
MY ANSWER
What I suggest is that instead of using the APIView, you use ListAPIView.
It would look something like this:
from rest_framework.generics import ListAPIView
class Customer(models.Model):
name = models.CharField(max_length=200)
email_address = models.CharField(max_length=200)
phone_number = models.CharField(max_length=20)
age = models.SmallIntegerField(default=14)
remarks = models.ManyToManyField(Remark,null=True,blank=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.id)
class Response(models.Model):
question = models.ForeignKey(Question)
customer = models.ForeignKey(Customer, related_name='responses')
response_text = models.CharField(max_length=100, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
uuid = models.UUIDField()
def __str__(self):
return str(self.id)
class ResponseSerializer(ModelSerializer):
class Meta:
model = Response
fields = '__all__'
class RemarksSerializer(ModelSerializer):
class Meta:
model = Remark
fields = '__all__'
class CustomerInformationSerializer(ModelSerializer):
remarks = RemarksSerializer(many=True)
responses = ResponseSerializer(many=True)
class Meta:
model = Customer
fields = ('name', 'email_address', 'phone_number', 'age', 'remarks', 'responses')
class CustomerInformationView(ListAPIView):
queryset = Customer.objects.all()
serializer_class = CustomerInformationSerializer
lookup_field = 'remarks__company'
Note the change that I made by adding related_name to the customer field on your Response model. See Django documentation for more information on related_name. In short, it adds responses as a field name on your Customer model so that you can travel backwards through that relationship.
This is not tested, but this should be a better strategy to do what you want without having to have a get_responses method, or a services.py.
Some there might be error because of missing "/" at the end of path like "event-api"=incorrect and "event-api/" correct. That worked for me. Hope you also have same problem.
Incorrect: path('event-api',views.event_view,name="event-view")
Correct: path('event-api/',views.event_view,name="event-view")

How to display a foreign key value instead of the id?

I have the following models :
class FlightSchedule(models.Model):
tail_number = models.ForeignKey(TailNumber, null=False)
flight_number = models.CharField(max_length=30, null=False)
flight_group_code = models.ForeignKey(FlightGroup, null=False)
origin_port_code = models.ForeignKey(Port, null=False, related_name="Origin")
destination_port_code = models.ForeignKey(Port, null=False, related_name="Destination")
flight_departure_time = models.TimeField()
start_date = models.DateField()
end_date = models.DateField()
def __unicode__(self):
return u'%s' % self.flight_number
class Meta:
verbose_name_plural = "Flight Schedule"
class FlightScheduleDetail(models.Model):
flight_date = models.CharField(max_length=30, null=False)
flight_number = models.ForeignKey(FlightSchedule, null=False, related_name="flight_number_schedule")
route_id = models.CharField(max_length=30, null=False, unique=True)
flight_status = models.ForeignKey(Status, null=True, default=1)
def __unicode__(self):
return u'%s' % self.route_id
class Meta:
verbose_name_plural = "Flight Schedule Details"
and the serializer is as below :
class FlightScheduleDetailSerializer(serializers.ModelSerializer):
class Meta:
model = FlightScheduleDetail
fields = '__all__'
class FlightScheduleSerializer(serializers.ModelSerializer):
flight_number_schedule = FlightScheduleDetailSerializer(many=True)
class Meta:
model = FlightSchedule
fields = ['tail_number', 'flight_number', 'origin_port_code', 'destination_port_code', 'flight_departure_time',
'flight_number_schedule']
Here tail_number , flight_number is a foreign key. When I create an API, I get the response as the id of the fields. How can I display the name in the json?
My views.py is as below :
#api_view(['GET'])
def flight_schedule(request):
schedule = FlightSchedule.objects.all()
serializer = FlightScheduleSerializer(schedule, many=True)
return Response(serializer.data)
You can define the source with field_name in your serializer as follows.
I have used source='TailNumber.number'. Please use the right field_name in place of number
class UserProfileSerializer(serializers.ModelSerializer):
tail_number = serializers.CharField(source='TailNumber.number', read_only=True)
flight_number = ....(change as above)
class Meta:
model = FlightSchedule
fields = ['tail_number', 'flight_number', 'origin_port_code', 'destination_port_code', 'flight_departure_time',
'flight_number_schedule']
You could simply add them as if they were attributes.
flight_number_str = serializers.ReadOnlyField(source='flight_number.flight_number')
First flight_number is the attribute of FlightScheduleDetail, then the one of FlightSchedule
and then add it to the list of fields fields = [..., 'flight_number_str']
Otherwise you may have a look at nested relationships in DRF which can offer more possibilities also.
Another alternative is to use the depth option in a serializer. It is to specify nested serialization - doc
class AccountSerializer(serializers.ModelSerializer):
class Meta:
model = Account
fields = ('id', 'account_name', 'users', 'created')
depth = 1
If users is a foreign key or manytomany key the serializer will display the users as an object and not as a key.
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.

Categories