In my django app, i'm having difficulties whenever i want to add a new object that uses the table paymentInvoice.
The error i'm getting from my api looks like this
IntegrityError at /api/clients/invoice/
null value in column "invoiceOwner_id" violates not-null constraint
DETAIL: Failing row contains (10, INV-0006, Lix, 2020-08-04, 1, Pending, 3000, null).
NB: I haven't created the field invoiceOwner_id, postgres automatically added it or rather is using it as a representation for my invoiceOwner field
class Purchaser(models.Model):
name = models.CharField(max_length=50)
phone = models.CharField(max_length=20, unique=True)
email = models.EmailField(max_length=255, unique=True, blank=True)
image = models.ImageField(default='default.png', upload_to='customer_photos/%Y/%m/%d/')
data_added = models.DateField(default=datetime.date.today)
def __str__(self):
return self.name
class paymentInvoice(models.Model):
invoiceNo = models.CharField(max_length=50, unique=True, default=increment_invoice_number)
invoiceOwner = models.ForeignKey(Purchaser, on_delete=models.CASCADE, related_name="invoice_detail")
product = models.CharField(max_length=50, blank=True)
date = models.DateField(default=datetime.date.today)
quantity = models.PositiveSmallIntegerField(blank=True, default=1)
payment_made = models.IntegerField(default=0)
def __str__(self):
return self.invoiceOwner.name
serilizers file
class paymentInvoiceSerializer(serializers.ModelSerializer):
invoiceOwner = serializers.SerializerMethodField()
class Meta:
model = paymentInvoice
fields = '__all__'
def get_invoiceOwner(self, instance):
return instance.invoiceOwner.name
views file
class paymentInvoiceListCreateView(ListCreateAPIView):
serializer_class = paymentInvoiceSerializer
queryset = paymentInvoice.objects.all().order_by('-date')
GET result from api call.
{
"id": 1,
"invoiceOwner": "Martin",
"invoiceNo": "INV-0001",
"product": "",
"date": "2020-08-04",
"quantity": 1,
"payment_made": 0
}
Tried passing below as POST but got the main error
{
"invoiceOwner": "Becky",
"product": "Lix",
"quantity": 1,
"payment_made": 3000
}
"invoiceOwner" in your serializers.py is a SerializerMethodField which is readonly
that's why you get an error, you have to define the create method yourself
As I said in comment: You need to explicitly override the create method in your serializer since your model has foreign key invoiceOwner, just to create that instance first as a Purchaser instance.
You can try the code below:
class paymentInvoiceSerializer(serializers.ModelSerializer):
invoiceOwner = serializers.SerializerMethodField()
class Meta:
model = paymentInvoice
fields = '__all__'
def get_invoiceOwner(self, instance):
return instance.invoiceOwner.name
def create(self, validated_data):
purchaser_name = validated_data.get("invoiceOwner")
purchaser = Purchaser(name=purchaser_name,
# you need to have phone, email, since these fields are unique,
# they can't remain null
)
purchaser.save()
return paymentInvoice.objects.create(invoiceOwner = purchaser, **validated_data)
Related
I have two models Purchaser and paymentInvoice, I want to make sure i don't create a duplicate Purchaser object when i'm creating a new paymentInvoice for the same Purchaser individual/instance.
Basically i have a Purchaser by the name Becky, so when i want to create an invoice for Becky 1st i want to make sure if the name Becky exists in Purchaser if it does, create paymentInvoice object with Becky taking the field invoiceOwner. If Becky doesn't exist in Purchaser, create an instance of that purchaser in Purchaser then use that instance name to create paymentInvoice object.
Models file
class Purchaser(models.Model):
name = models.CharField(max_length=50)
phone = models.CharField(max_length=20)
email = models.EmailField(max_length=255, blank=True, null=True)
image = models.ImageField(default='default.png', upload_to='customer_photos/%Y/%m/%d/')
data_added = models.DateField(default=datetime.date.today)
def __str__(self):
return self.name
class paymentInvoice(models.Model):
invoiceNo = models.CharField(max_length=50, unique=True, default=increment_invoice_number)
invoiceOwner = models.ForeignKey(Purchaser, on_delete=models.CASCADE, related_name="invoice_detail")
product = models.CharField(max_length=50, blank=True)
date = models.DateField(default=datetime.date.today)
quantity = models.PositiveSmallIntegerField(blank=True, default=1)
payment_made = models.IntegerField(default=0)
def __str__(self):
return self.invoiceOwner.name
Serializers file
class paymentInvoiceSerializer(serializers.ModelSerializer):
invoiceOwner = purchaserSerializer(many=False)
invoiceOwner = serializers.CharField(source='invoiceOwner.name')
class Meta:
model = paymentInvoice
fields = '__all__'
def create(self, validated_data):
purchaser_data = validated_data.pop("invoiceOwner")
purchaser, _ = Purchaser.objects.get_or_create(**purchaser_data).first()
validated_data.update({"invoiceOwner": purchaser})
return paymentInvoice.objects.create(**validated_data)
Views file
class PurchaserListCreateView(ListCreateAPIView):
serializer_class = purchaserSerializer
queryset = Purchaser.objects.all()
class paymentInvoiceListCreateView(ListCreateAPIView):
serializer_class = paymentInvoiceSerializer
queryset = paymentInvoice.objects.all().order_by('-date')
POST request in Postman for the model paymentInvoice
{
"invoiceOwner":"Becky",
"product": "Lix",
"quantity": 1,
"payment_made": 3000
}
My way is not working, i get the error from PostMan
MultipleObjectsReturned at /api/clients/invoice/
get() returned more than one Purchaser -- it returned 2!
You can first check for the record if not found then create one like below
def create(self, validated_data):
purchaser_data = validated_data.pop("invoiceOwner")
purchaser = Purchaser.objects.filter(**purchaser_data).first()
if purchaser is None:
purchaser = Purchaser.objects.create(**purchaser_data)
validated_data.update({"invoiceOwner": purchaser})
return paymentInvoice.objects.create(**validated_data)
Probably get_or_create should work also, just i think you should redifine get_or_create not create method .
def get_or_create(self):
purchaser_data = validated_data.pop("invoiceOwner")
purchase,created = Purchase.objects.get_or_create(**purchaser_data)
validated_data.update({"invoiceOwner": purchaser})
payment_invoice = paymentInvoice.objects.create(**validated_data)
return payment_invoice,created
in view :
model_serializer = paymentInvoiceSerializer(data=request.data)
if model_serializer.is_valid():
payment_invoice,_ = model_serializer.get_or_create()
I'm trying to add new record to my invoice model table but i keep getting the error message
IntegrityError
null value in column "shipping_address_owner_id" violates not-null constraint
My guess is the column shipping_address_owner which is using ForeignKey that links it to a second table called ShippingAddress.
I'm trying to add the following entry to my paymentInvoice model table but i'm getting the above stated error
{
"shipping_address_owner": "Becky",
"product": [
"Sepit"
],
"quantity": 1,
"payment_made": "2600.00"
}
Below is my paymentInvoice model table
class paymentInvoice(models.Model):
shipping_address_owner = models.ForeignKey(ShippingAddress, on_delete=models.CASCADE, related_name="customer_invoice")
product = models.ManyToManyField(Product, related_name='product_invoice')
date = models.DateField(default=datetime.now)
invoice_id = models.CharField(max_length=50, unique=True, default=increment_invoice_number)
quantity = models.PositiveSmallIntegerField()
payment_made = models.DecimalField(max_digits=20, decimal_places=2)
def __str__(self):
return self.shipping_address_owner.customer.name
My ShippingAddress model
class ShippingAddress(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name="customer_ship_address")
address = models.CharField(max_length=50, blank=True)
zip_code = models.CharField(max_length=12, blank=True)
def __str__(self):
return self.customer.name
My serializer file
class paymentInvoiceSerializer(serializers.ModelSerializer):
product = serializers.SerializerMethodField()
shipping_address_owner = serializers.SerializerMethodField()
class Meta:
model = paymentInvoice
fields = '__all__'
def get_shipping_address_owner(self, instance):
return instance.shipping_address_owner.customer.name
def get_product(self, instance):
names = []
for product in instance.product.all(): # get all products field
names.append(product.name) # append the product name
return names
I am working in a drf & react app . Where i need to show details of a foreign key field but when in POST method i need post only ID.But its not norking for nested serilizer. Giving an error from frontend named "Expected Dictonary but got int".
output:
{
"id": 1,
"Hospital": {
"id": 1,
"User_Info": "bnsb#gmail.com",
"Manager_Name": "Jalal Uddin",
"Address": "Dhaka"
},
"FullName": "s",
"Age": "s",
Models.py:
class Hospital(models.Model):
User_Info = models.ForeignKey(User , on_delete=models.CASCADE)
Manager_Name = models.CharField(max_length= 40 , blank=True, null=True)
Address = models.CharField(max_length=40 , blank=True, null=True)
class Patient(models.Model):
Hospital = models.ForeignKey(Hospital , on_delete= models.CASCADE , default = 1)
FullName = models.CharField(max_length = 50 , blank = False , null = False , default = "")
Age = models.CharField(max_length=12 , blank=True, null=True)
def __str__(self):
return str(self.User_Info.Hospital_Name)
Serilizers.py:
class HospitalDetailsForPatientSerializer(serializers.ModelSerializer):
User_Info = CustomUserSerializer(read_only= True)
class Meta:
model = Hospital
fields = "__all__"
extra_kwargs = {"Address":{'read_only': True} ,"Manager_Name":{'read_only' : True}}
I got the expected output, Now I need to POST the only ID of Hospital Model when I save the patient but it's not working.
I solved the problem by defining the to_representation method in the parent serializer. Now I can see the foreign key details in the 'GET' method but when I want to POST foreign it needs only ID.
Serializers.py :
class HospitalDetailsForPatientSerializer(serializers.ModelSerializer):
User_Info = CustomUserSerializer()
class Meta:
model = Hospital
fields = ['User_Info']
class PatientSerializer(serializers.ModelSerializer):
class Meta:
model = Patient
fields = "__all__"
def to_representation(self, instance):
response = super().to_representation(instance)
response['Hospital'] = HospitalDetailsForPatientSerializer(instance.Hospital).data
return response
I'm using Django 2.x and Django REST Framework
My models.py file contents
class ModeOfPayment(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
title = models.CharField()
class AmountGiven(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
contact = models.ForeignKey(Contact, on_delete=models.PROTECT)
amount = models.FloatField()
mode_of_payment = models.ForeignKey(
ModeOfPayment,
on_delete=models.PROTECT,
blank=True,
default=None,
null=True
)
and serializers.py
class ModeOfPaymentSerializer(serializers.ModelSerializer):
class Meta:
model = ModeOfPayment
fields = ('id', 'title')
class AmountGivenSerializer(serializers.ModelSerializer):
mode_of_payment = ModeOfPaymentSerializer()
class Meta:
model = AmountGiven
depth = 1
fields = (
'id', 'contact', 'amount', 'mode_of_payment',
)
def update(self, instance, validated_data):
mode_of_payment = validated_data.pop('mode_of_payment')
instance.mode_of_payment_id = mode_of_payment.id
return instance
and views.py
class AmountGivenViewSet(viewsets.ModelViewSet):
serializer_class = AmountGivenSerializer
permission_classes = (IsAuthenticated, AdminAuthenticationPermission,)
filter_fields = ('contact__id',)
def get_queryset(self):
queryset = AmountGiven.objects.filter(
contact__user=self.request.user
)
return queryset
But when I post data using postman with PUT method to update the existing record
It still says
{
"mode_of_payment": [
"This field is required."
]
}
Edit 2: Response after Daniel answer
{
"id": "326218dc-66ab-4c01-95dc-ce85f226012d",
"contact": {
"id": "b1b87766-86c5-4029-aa7f-887f436d6a6e",
"first_name": "Prince",
"last_name": "Raj",
"user": 3
},
"amount": 3000,
"mode_of_payment": "0cd51796-a423-4b75-a0b5-80c03f7b1e65",
}
You've told AmountSerializer to accept a nested dict representing a ModeOfPayment instance, by setting the mode_of_payment field to ModeOfPaymentSerializer. But that's not what you're sending; you're sending the ID of the ModeOfPayment.
You should remove that line in AmountGivenSerializer.
Edit
I was wrong, you need to declare the field explicitly as a PrimaryKeyRelatedField:
class AmountGivenSerializer(serializers.ModelSerializer):
mode_of_payment = serializers.PrimaryKeyRelatedField(queryset=ModeOfPayment.objects.all())
class Meta:
...
Now it will accept a UUID in the data.
I have a nested serializer and I want to activate the allow_null to true, but it doesn't work.
TOP object have a nested Down object, the related_name must be present in the TOP object but with a null value. If the down object is not null all down object fields are required.
Example request with all fields in down object (this one works fine) :
{
"title": "Titre new rgfdgfdgthtrh",
"downs": {
"type": "Type example",
"is_external": true,
},
}
Example that i tryed to do : request when down object is null (this one doesn't work)
{
"title": "Titre new ",
"downs": {},
}
I have tryed with "downs": None or Null without success.
My views :
# My Views.py
class Top(models.Model):
class Meta:
verbose_name = _('Top')
verbose_name_plural = _('Tops')
top_guid = models.UUIDField(
primary_key=True,
unique=True,
default=uuid.uuid4,
editable=False)
title = models.CharField(
help_text=_('Title'),
verbose_name=_('title'),
max_length=100,
blank=False
)
class Down(models.Model):
top = models.OneToOneField(
Top,
on_delete=models.CASCADE,
help_text=_('Top'),
verbose_name="top",
related_name="downs"
)
type = models.CharField(
help_text=_('Type'),
verbose_name=_('type'),
max_length=30,
blank=False
)
is_external = models.BooleanField(
help_text=_('external (default = false)'),
verbose_name=_('external'),
blank=False,
default=False
)
and my serializers
# My serializers.py
class DownSerializer(serializers.ModelSerializer):
class Meta:
model = Down
fields = '__all__'
class TopSerializer(serializers.ModelSerializer):
downs = DownSerializer(many=False, required=False, allow_null=True)
class Meta:
model = Top
fields = ('top_guid', 'title', 'downs',)
def create(self, validated_data):
"""
Create and return a new `Topic` instance.
"""
downs_data = validated_data.pop('downs')
top = Top.objects.create(**validated_data)
Down.objects.create(top=top, **downs_data)
return top
def update(self, instance, validated_data):
"""
Update and return an existing `Topic` instance.
"""
# get bim_snippet data and bim_snippet object
downs_data = validated_data.pop('downs')
downs = instance.downs
# update top data and save top object
instance.title = validated_data.get('title', instance.title)
instance.top_type = validated_data.get('top_type', instance.top_type)
instance.save()
# update down data and save down object
downs.snippet_type = downs_data.get('type', downs.snippet_type)
downs.is_external = downs_data.get('is_external', downs.is_external)
downs.save()
return instance
Thank's a lot.
I think that if you add arguments like allow_null=True or read_only=False in your serializer class, you need to recreate your sqlite3 database. read_only was not working, but just after recreate the db it works fine. (makemigrations and migrate seem's to be not enought)