Contacts matching query does not exist in django rest framework - python

I am trying to update Contact model fields while creating the new fields of UpdateInfo model and add them to the existing model.
But I am getting this error
contacts.models.Contacts.DoesNotExist: Contacts matching query does not exist.
I know the contact object with the id 19 exists because I can see it when I try the get contacts API.
I am sending data like this.
My models:
class Contacts(models.Model):
full_name = models.CharField(max_length=100, blank=True)
.........
def __str__(self):
return self.full_name
class Meta:
verbose_name_plural = 'Potential Clients'
class UpdateInfo(models.Model):
contacts = models.ForeignKey(Contacts,on_delete=models.CASCADE, related_name='update_info')
updated_at = models.DateTimeField(auto_now=True)
modified_by = models.CharField(max_length=100, blank=True)
def __str__(self):
return f"Last modified by {self.modified_by} at {self.updated_at}"
My views:
class EditContactView(RetrieveUpdateDestroyAPIView):
permission_classes = [IsAuthenticated]
queryset = Contacts.objects.all()
serializer_class = ContactsUpdateSeializer
My serializers:
class UpdateInfoSerializer(serializers.ModelSerializer):
contacts= serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = UpdateInfo
fields = ['id','contacts','updated_at','modified_by']
class ContactsUpdateSeializer(serializers.ModelSerializer):
update_info = UpdateInfoSerializer(many=True)
id = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Contacts
fields = ['id', 'full_name', 'lead_source', 'email', 'phone', 'contact_owner',
'contact_status', 'company_name', 'job_position', 'tasks',
'notes', 'email_list', 'created_by', 'created_at', 'update_info']
def update(self, instance, validated_data):
update_data = validated_data.pop('update_info')
id = validated_data.get('id')
contacts = Contacts.objects.get(id=id)
#contacts.save()
#contact_only_update_logic
instance.full_name = validated_data.get('full_name')
instance.lead_source = validated_data.get('lead_source')
instance.email = validated_data.get('email')
instance.phone = validated_data.get('phone')
instance.contact_owner = validated_data.get('contact_owner')
instance.contact_status = validated_data.get('contact_status')
instance.company_name = validated_data.get('company_name')
instance.job_position = validated_data.get('job_position')
instance.tasks = validated_data.get('tasks')
instance.notes = validated_data.get('notes')
instance.email_list = validated_data.get('email_list')
instance.save()
#add_update_info_logic
for update_data in update_data:
abc = UpdateInfo.objects.create(contacts=contacts,**update_data)
instance.update_info.add(abc)
instance.save()
return instance

You have to change your serializer
class ContactsUpdateSeializer(serializers.ModelSerializer):
update_info = UpdateInfoSerializer(many=True)
class Meta:
model = Contacts
fields = ['id', 'full_name', 'lead_source', 'email', 'phone', 'contact_owner',
'contact_status', 'company_name', 'job_position', 'tasks',
'notes', 'email_list', 'created_by', 'created_at', 'update_info']
def update(self, instance, validated_data):
update_data = validated_data.pop('update_info')
instance = super(ContactsUpdateSeializer, self).update(instance, validated_data)
for update_datum in update_data:
abc = UpdateInfo.objects.create(contacts=instance,**update_datum)
return instance
PrimaryKeyRelatedField is used for foreign key purpose and there we have to define queryset also.
Don't need to add the updateinfo in contact, it is already done throgh django.
Moreover, it would be better if you use bulk_create instead of running save each time if you there is no signals exists for that.
You can do it as:-
UpdateInfo.objects.bulk_create([UpdateInfo(contacts=instance,**update_datum) for update_datum in update_data])

Related

django custom serializer field not working

I am trying to add an additional field in the serializer but at the moment of adding it I get the following error
error view
During handling of the above exception ('MovieSerializer' object has no attribute 'len_name')
I have seen many posts and all of them are working, what could it be?
this is my code
Model:
from django.db import models
class Movie(models.Model):
title = models.CharField(max_length=255, unique=True)
description = models.TextField()
active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(default=None, null=True)
def __str__(self):
return self.title
serializer:
from rest_framework import serializers
from watchlist_app.models import Movie
from watchlist_app.api.validations import MovieValidations
class MovieSerializer(serializers.ModelSerializer):
len_name = serializers.SerializerMethodField(method_name='len_name')
class Meta:
model = Movie
fields = "__all__"
read_only_fields = ['id', 'len_name', 'created_at']
required_fields = ['title', 'description']
#staticmethod
def create(self, validated_data):
return Movie.objects.create(**validated_data)
#staticmethod
def update(self, instance, validated_data):
instance.title = validated_data.get('title', instance.title)
instance.description = validated_data.get('description', instance.description)
instance.active = validated_data.get('active', instance.active)
instance.save()
return instance
#staticmethod
def get_len_name(self, obj):
return len(obj.title)
validators = [MovieValidations.validate_title, MovieValidations.validate_description,
MovieValidations.validate_equals]
everything works fine until I add the serializerMethodField
I think you set the function in the wrong place. The function needs to belong to the MovieSerializer.
from rest_framework import serializers
from watchlist_app.models import Movie
from watchlist_app.api.validations import MovieValidations
class MovieSerializer(serializers.ModelSerializer):
len_name = serializers.SerializerMethodField()
class Meta:
model = Movie
fields = "__all__"
...
def get_len_name(self, obj):
return len(obj.title)
use extra_fields in meta class
class MovieSerializer(serializers.ModelSerializer):
len_name = serializers.SerializerMethodField(method_name='len_name')
class Meta:
model = Movie
fields = "__all__"
extra_fields = ['len_name',]
read_only_fields = ['id', 'len_name', 'created_at']
required_fields = ['title', 'description']
*** your serializer methods in wrong indent,fix it -> Go back one indent and remode staticmethod decorator ***

Django REST Framework: NestedSerializer in OneToMany relation

I have 2 related models:
class Systems(models.Model):
name = models.CharField(max_length=200)
description = models.CharField(max_length=2000, null=True, blank=True)
class Teams(models.Model):
system = models.ForeignKey(Systems, on_delete=models.CASCADE)
name = models.CharField(max_length=200)
description = models.CharField(max_length=2000, null=True, blank=True)
And 2 serializers:
class System_serializer(serializers.HyperlinkedModelSerializer):
id = serializers.ReadOnlyField
class Meta:
model = Systems
fields = ('id', 'name', 'description')
def create(self, validated_data):
return Systems.objects.create(**validated_data)
def update(self, system, validated_data):
system.name = validated_data.get('name', system.name)
system.description = validated_data.get('description', system.description)
system.save()
return system
second:
class Team_serializer(serializers.ModelSerializer):
id = serializers.ReadOnlyField
system_id = System_serializer()
class Meta:
model = Teams
fields = ('id', 'system_id', 'name', 'description')
def create(self, validated_data):
return Teams.objects.create(**validated_data)
def update(self, team, validated_data):
team.name = validated_data.get('name', team.name)
team.description = validated_data.get('description', team.description)
team.system_id = validated_data.get('system_id', team.system_id)
team.save()
return team
In 'team' entity I just want to have only 'System' ID, not the whole 'System'
How can I use the System_serializer, to serialize 'system_id' in 'team' object?
Thanks!
ADD
I tried to use SlugRelatedField:
class Team_serializer(serializers.ModelSerializer):
id = serializers.ReadOnlyField
system_id = serializers.SlugRelatedField(queryset=Systems.objects.all(), slug_field='id')
class Meta:
model = Teams
fields = ('id', 'system_id', 'name', 'description')
But got
'int' object has no attribute 'id'
ADD2
Tried to use PrimatyKeyRelatedField:
class Team_serializer(serializers.ModelSerializer):
id = serializers.ReadOnlyField
system_id = serializers.PrimaryKeyRelatedField(queryset=Systems.objects.all())
class Meta:
model = Teams
fields = ('id', 'system_id', 'name', 'description')
def create(self, validated_data):
return Teams.objects.create(**validated_data)
but got on create
Field 'id' expected a number but got <Systems: Systems object (2)>.

Django Rest Framework update don't save changes

I have two models related through a one to one field, and I want to create a REST services that manages them both as they where one. The post works perfectly so far, and new instances are created in both models as they where one, but the put method just does nothing. It raises no error or anything, it just leaves the data unchanged.
These are my models:
class Company(models.Model):
name = models.CharField(max_length=40)
legal_name = models.CharField(max_length=100)
url = models.URLField()
address = models.TextField(max_length=400)
def __str__(self):
return "[{}]{}".format(self.id, self.name)
class Hotel(models.Model):
company = models.OneToOneField('Company', on_delete=models.CASCADE, primary_key=True)
code = models.CharField(max_length=10)
city = models.ForeignKey('City', on_delete=models.PROTECT, related_name='city_hotels')
category = models.ForeignKey('HotelCategory', on_delete=models.PROTECT, related_name='category_hotels')
capacity = models.IntegerField()
position = models.DecimalField(max_digits=11, decimal_places=2, default=0.00)
in_pickup = models.BooleanField(default=False)
def __str__(self):
return self.company.name
This is my ViewSet:
class HotelViewSet(viewsets.ModelViewSet):
queryset = models.Hotel.objects.all()
serializer_class = serializers.HotelSerializer
These are my serializers:
class CompanySerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = models.Company
fields = ('id', 'name', 'legal_name', 'url', 'address')
class HotelSerializer(serializers.HyperlinkedModelSerializer):
company = CompanySerializer()
class Meta:
model = models.Hotel
fields = ('company', 'code', 'city', 'category', 'capacity', 'position', 'in_pickup')
def create(self, validated_data):
company_data = validated_data.pop('company')
new_company=models.Company.objects.create(**company_data)
hotel = models.Hotel.objects.create(company=new_company, **validated_data)
return hotel
def update(self, instance, validated_data):
company = models.Company(
id=instance.company.id,
name=instance.company.name,
legal_name=instance.company.legal_name,
url=instance.company.url,
address=instance.company.address
)
company.save()
instance.save()
return instance
I found that instance carries the original data, and validated_data carries the new data. I was saving the original data back.
I had to replace instance data with validated_data data and then save instance:
def update(self, instance, validated_data):
company_data = validated_data.pop('company')
company = models.Company.objects.get(id=instance.company.id)
company.name = company_data.get('name')
company.legal_name = company_data.get('legal_name')
company.tax_id = company_data.get('tax_id')
company.url = company_data.get('url')
company.address = company_data.get('address')
instance.company = company
instance.code = validated_data.get('code', instance.code)
instance.city = validated_data.get('city', instance.city)
instance.category = validated_data.get('category', instance.category)
instance.capacity = validated_data.get('capacity', instance.capacity)
instance.position = validated_data.get('position', instance.position)
instance.in_pickup = validated_data.get('in_pickup', instance.in_pickup)
instance.is_active = validated_data.get('is_active', instance.is_active)
company.save()
instance.save()
return instance
put method works handsomely now.
You are creating a new instance of company instead of updating the one that belongs to the Hotel instance.
company = Company.objects.get(id=instance.company.id)
company.name = instance.company.name
company.legal_name = instance.company.name
company.url = instance.company.url
company.address = instance.company.address
company.save()
instance.save()

DRF: Post request with existing nested objects

I'm new to Django Rest Framework and nested serializers. I have a ModelSerializer named OrderSerialiser which contains two nested ModelSerializers : ProductSerializer and ClientSerializer.
I want that new instances of the model client and the model product are created (Only if there is no already existing ones) when a post request is sent to the Order CreateAPI.
The solution I have found is to override the create method of the OrderSerializer.
It works fine when there is no instances of the client and the product having the same email and sku, but it returns an error saying that there is already existing objects ( client with the same email and a product with the same sku ) in the other case and does not get those existing objects,I noted that the create method in this case is not called , I think that I have to override the serializers.is_valid() method but I didn't figure out what I should do exactly .
models.py
class Client(models.Model):
email = models.EmailField(
verbose_name=_('Email address'),
max_length=255,
unique=True,
primary_key=True
)
first_name = models.CharField(_('first name'), max_length=30)
last_name = models.CharField(_('last name'), max_length=30)
class Product(models.Model):
sku = models.CharField(
verbose_name=_('SKU'),
unique=True,
max_length=120,
primary_key=True
)
name = models.CharField(
verbose_name=_('Name'),
max_length=150
)
url = models.URLField(
verbose_name=_('URL'),
blank=True,
null=True
)
class Order(models.Model):
client = models.ForeignKey(Client)
products = models.ManyToManyField(
Product,
related_name= "orders",
null=True,
blank=True,
)
serializers.py
class ProductSerialiser(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('sku', 'name', 'url')
class ClientSerialiser(serializers.ModelSerializer):
class Meta:
model = Client
fields = ('email','first_name', 'last_name')
class OrderSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.email')
client = ClientSerialiser()
products = ProductSerialiser(many=True, required=False)
class Meta:
model = Order
fields = ('id', 'client', products')
def create(self, validated_data):
client_data = validated_data.pop('client')
try:
client_instance = Client.objects.get(email=client_data['email'])
except ObjectDoesNotExist:
client_instance = Client.objects.create(**client_data)
if 'products' in validated_data:
products_data = validated_data.pop('products')
order_instance = Order.objects.create(client=client_instance, **validated_data)
for product_data in products_data:
try :
product = Product.objects.get(sku=product_data['sku'])
except ObjectDoesNotExist:
product = Product.objects.create(**product_data)
product.orders.add(order_instance)
return order_instance
order_instance = Order.objects.create(client=client_instance, **validated_data)
return order_instance
Akamee,
one way I am used to solve this sort of problems is basically to, inside your serializers.py, do
def validate(self, data):
data = super(YourSerializer, self).validate(data)
try:
data['product'] = Product.objects.get(sku='bar')
except Product.DoesNotExist:
data['product'] = Product.object.create(sku='bar')
This isn't ideal, but I did find a solution that solved my problem (I'm waiting to accept it as the answer, hoping someone else can do better)
I have eliminated the validators for the field email in ClientSerializer and the field sku in Productserializer , and then checked the existence of objects manually.
modification on serialzers.py
class ProductSerialiser(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('sku', 'name', 'url')
extra_kwargs = {
'sku': {
'validators': []
}
}
class ClientSerialiser(serializers.ModelSerializer):
class Meta:
model = Client
fields = ('email', 'first_name', 'last_name')
extra_kwargs = {
'email': {
'validators': []
}
}
class OrderSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.email')
client = ClientSerialiser(partial=True)
products = ProductSerialiser(many=True, required=False, partial=True)
class Meta:
model = Order
fields = ('id','client', products')
def create(self, validated_data):
client_data = validated_data.pop('client')
try:
print "**************** client exists ***********************"
client_instance = Client.objects.get(email=client_data['email'])
except ObjectDoesNotExist:
print "**************** creating a client ***********************"
client_instance = Client.objects.create(**client_data)
if 'products' in validated_data:
products_data = validated_data.pop('products')
order_instance = Order.objects.create(client=client_instance, **validated_data)
for product_data in products_data:
try :
print "**************** Product exists ***********************"
product = Product.objects.get(sku=product_data['sku'])
except ObjectDoesNotExist:
print "**************** creating object product ***********************"
product = Product.objects.create(**product_data)
product.orders.add(order_instance)
return order_instance
order_instance = Order.objects.create(client=client_instance, **validated_data)
return order_instance

django rest framework: object of type 'NoneType' has no len() when calling get method of related table

I am building an django rest api for saving/managing customer data for my project. I have two models. Customer for storing basic customer details and CustomerDetails for storing a bunch of customer details. I want to write a single api to create/update data for both the models.
Now my code is saving the user data. But I now I can't get the customer data. When I call the get method, the following error occurs.
TypeError at /api/v1/customer
object of type 'NoneType' has no len()
Request Method: GET
Request URL: http://127.0.0.1:8000/api/v1/customer
Also, Do I need to do anything extra to use this code for updation (PUT)
urls.py
router.register(r'customer', views.CustomerViewSet, 'customers')
models.py
class Customer(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=20)
address = models.CharField(max_length=50)
city = models.CharField(max_length=256)
"""some more fields to go"""
# Create your models here.
class CustomerDetails(models.Model):
customer = models.OneToOneField(Customer, on_delete=models.CASCADE, primary_key=True, )
spouse_name = models.CharField(max_length=256)
interests = models.CharField(max_length=256)
"""many more fields to go"""
serializers.py
class CustomerDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = CustomerDetails
fields = ('spouse_name',)
class CustomerSerializer(serializers.ModelSerializer):
customer_details = CustomerDetailsSerializer( required=True)
class Meta:
model = Customer
fields = ('name', 'city', 'customer_details', )
def create(self, validated_data):
request = self.context.get('request')
user = request.user
# create user
customer = Customer.objects.create(
user = user,
name = validated_data['name'],
city = validated_data['city'],
# etc ...
)
print(json.dumps(validated_data, indent=4))
customer_details_data = validated_data.pop('customer_details')
# create profile
customer_details = CustomerDetails.objects.create(
customer = customer,
spouse_name = customer_details_data['spouse_name'],
)
customer.customer_details = customer_details_data;
return customer
class CustomerListSerializer(serializers.ModelSerializer):
model = Customer
customer_details = CustomerDetailsSerializer()
class Meta:
model = Customer
fields = '__all__'
views.py
class CustomerViewSet(viewsets.ModelViewSet):
customer_photo_thumb = Customer.get_thumbnail_url
customer_details = ''
permission_classes = [permissions.IsAuthenticated, TokenHasReadWriteScope]
queryset = Customer.objects.all()
serializer_class = CustomerSerializer
def get_queryset(self):
queryset = Customer.objects.all()
def get_serializer_class(self):
if self.action == 'list' or self.action == 'retrieve':
return CustomerListSerializer
return CustomerListSerializer
Your method .get_queryset return None replace like this:
def get_queryset(self):
return Customer.objects.all()
In fact your get_queryset method is not necessary since the queryset attribute does not change.

Categories