i am trying to insert data into my OrderItem() model:
class OrderItem(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
ordered = models.BooleanField(default=False)
item = models.ForeignKey(Item, on_delete=models.CASCADE)
quantity = models.IntegerField(default=1)
with an api with json data as follows:
{ "username": "admin",
"item": "Kavya",
"quantity": "1"
}
and i my view i am recieving this data and updating like:
class addtoCart(APIView):
# permission_classes=[IsAuthenticated,]
# authentication_classes=[TokenAuthentication,]
def post(self,request):
username=request.data["username"]
itemname=request.data["item"]
quant=request.data["quantity"]
#inserting data to cart
article = OrderItem()
article.User = username
article.Item = itemname
article.quantity=quant
article.save()
query=OrderItem.objects.filter(user=username)
serializers=OrderItemSerializer(query,many=True)
return Response(serializers.data)
but whenever i try to do so i get an error:
IntegrityError at /apis/addtoCart
NOT NULL constraint failed: core_orderitem.item_id
i dont know what causing this error can someone please help <3.
You need to pass an id or other unique constraint in your request body, so that you can identify which Item object to set to the article instance, e.g.:
{
"username": "admin",
"item_id": <id>,
"quantity": "1"
}
and in your post method:
from django.shortcuts import get_object_or_404
def post(self,request):
username = request.data["username"]
item_id = request.data["item_id"]
quant = request.data["quantity"]
# since `user` is also a foreign key reference i would suggest you to query it, too
user = get_object_or_404(User, username=username)
article = OrderItem()
# set the attributes as lowercase
article.user = user
# Django creates an extra field `<foreign_key>_id` for related objects, so that you can set it without querying
article.item_id = item_id
article.quantity = quant
article.save()
# ...
But I would suggest you using DRF Serializers for dealing with request.data.
Related
I am creating first rest api in django using django rest framework
I am unable to get object in json format. Serializer always returns empty object {}
models.py
class Shop(models.Model):
shop_id = models.PositiveIntegerField(unique=True)
name = models.CharField(max_length=1000)
address = models.CharField(max_length=4000)
serializers.py
class ShopSerializer(serializers.ModelSerializer):
class Meta:
model = Shop
fields = '__all__'
views.py
#api_view(['GET'])
def auth(request):
username = request.data['username']
password = request.data['password']
statusCode = status.HTTP_200_OK
try:
user = authenticate(username=username, password=password)
if user:
if user.is_active:
context_data = request.data
shop = model_to_dict(Shop.objects.get(retailer_id = username))
shop_serializer = ShopSerializer(data=shop)
if shop:
try:
if shop_serializer.is_valid():
print('is valid')
print(shop_serializer.data)
context_data = shop_serializer.data
else:
print('is invalid')
print(shop_serializer.errors)
except Exception as e:
print(e)
else:
print('false')
else:
pass
else:
context_data = {
"Error": {
"status": 401,
"message": "Invalid credentials",
}
}
statusCode = status.HTTP_401_UNAUTHORIZED
except Exception as e:
pass
return Response(context_data, status=statusCode)
When i try to print print(shop_data) it always returns empty object
Any help, why object is empty rather than returning Shop object in json format?
Edited:
I have updated the code with below suggestions mentioned. But now, when shop_serializer.is_valid() is executed i get below error
{'shop_id': [ErrorDetail(string='shop with this shop shop_id already exists.', code='unique')]}
With the error it seems it is trying to update the record but it should only get the record and serialize it into json.
You're using a standard Serializer class in this code fragment:
class ShopSerializer(serializers.Serializer):
class Meta:
model = Shop
fields = '__all__'
This class won't read the contend of the Meta subclass and won't populate itself with fields matching the model class. You probably meant to use ModelSerializer instead.
If you really want to use the Serializer class here, you need to populate it with correct fields on your own.
.data - Only available after calling is_valid(), Try to check if serializer is valid than take it's data
When i use my super acc, this error does not shows up, but when I tried to use other acc. this error shows up. where did I do wrong?
The error : DoesNotExist at /voting/
Userdata matching query does not exist.
My Model :
class Userdata(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
faculty = models.ForeignKey(Fakultas, on_delete=models.CASCADE, default=1)
is_voted = models.BooleanField(default=False)
def __str__(self): return self.user.username
My views :
#login_required
def voted(response):
user = Userdata.objects.get(id=response.user.id) # get the username
if user.is_voted:
return render(response, 'Main/voting.html', {'calon': Voting.objects.order_by('id'), 'hasil': 'You Have Voted'})
if response.method == 'POST':
id = response.POST['idcalon']
calon2 = Voting.objects.get(id=id) # get user selection in html
user.is_voted = True
calon2.voters += 1
user.save()
calon2.save()
return render(response, 'Main/voting.html', {'calon': Voting.objects.order_by('id')}) # balik ke sendiri
User and UserData don't have the same primary key. So Userdata.objects.get(id=response.user.id) will not work because there is no such Userdata with the given user's id. But you do not need that, because User and Userdata have OneToOne relation. So simply use this:
user = response.user.userdata
first i update my model instance, after that i tried to insert a new data but showing
"IntegrityError('duplicate key value violates unique constraint "RFIDActivation_ActivationId_key"\nDETAIL: Key ("ActivationId")=(6de9ed9a) already exists.\n',)"
Models.py
class RFIDActivation(models.Model):
RFIDActivationId = models.AutoField(primary_key=True, db_column='RFIDActivationId')
Device = models.ForeignKey(Device, on_delete=models.CASCADE, db_column='DeviceId')
Employee = models.ForeignKey(Employee, on_delete=models.CASCADE, db_column='EmployeeId')
ActivationId = models.CharField(max_length=10, unique=True, default=uuid4().hex[:8])
ActivationStatus = models.CharField(max_length=1)default=None)
class Meta:
db_table = "RFIDActivation"
my serializer.py
class RFIDActivationSerializer(serializers.ModelSerializer):
class Meta:
model = RFIDActivation
fields = '__all__'
view.py
#api_view(["POST"])
#permission_classes([IsAuthenticated])
def rfid_activation_initial(request):
RFIDActivation.objects.filter(Employee=request.POST.get("Employee")).update(
ActivationStatus='2',
ActivationMessage='Abort'
)
rfid_activation = {
'Employee': request.POST.get("Employee"),
'Device': request.POST.get("Device"),
'ActivationStatus': "0",
'ActivationMessage': "RFID Activation Initiated"
}
rfid_serializer = RFIDActivationSerializer(data=rfid_activation)
if rfid_serializer.is_valid():
rfid_serializer.save()
but rfid_serializer.save() shows exceptionexception
Previous ActivationId is using to saving the new data. ActivationId is unique and auto generated. How can i solve this. Error is showing trying to insert after update query
view.py for working fine code only insertion.
#api_view(["POST"])
#permission_classes([IsAuthenticated])
def rfid_data_add(request):
rfid_activation = {
'Employee': request.POST.get("Employee"),
'Device': request.POST.get("Device"),
'ActivationStatus': "0",
'ActivationMessage': "RFID Activation Initiated"
}
rfid_serializer = RFIDActivationSerializer(data=rfid_activation)
if rfid_serializer.is_valid():
rfid_serializer.save()
In restframework, serializer.save() has different behavior based on creation and update. If you pass instance to serializer, it will update the object, otherwise, it will create a new one
#api_view(['POST', 'PUT'])
def rfid_data(request, pk):
"""
Update or delete a rfid data.
"""
rfid_activation = {
...
}
if request.method == 'POST':
rfid_serializer = RFIDActivationSerializer(data=rfid_activation)
if rfid_serializer.is_valid():
rfid_serializer.save()
return Response(rfid_serializer.data,status=status.HTTP_201_CREATED)
return return Response(rfid_serializer.data, status=status.HTTP_400_BAD_REQUEST)
# PUT is used for object update
elif request.method == 'PUT':
rfid = RFIDActivation.objects.get(pk=pk)
# pass instance to your serializer
# pass partial=True to allow partial updates
rfid_serializer = RFIDActivationSerializer(instance=rfid, data=rfid_activation, partial=True)
if rfid_serializer.is_valid():
rfid_serializer.save()
return Response(rfid_serializer.data)
return Response(rfid_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
UPDATE
default is calculated at import time, in your case, your default value for activation_id is calculated once and never change for subsequent instances.
You have to define a function, thus default callable will be updated each time on object creation, read more here
def activation_id_generator(self):
return uuid4().hex[:8]
# then in your class
class RFIDActivation(models.Model):
...
ActivationId = models.CharField(max_length=10, unique=True, default=activation_id_generator)
i want to create a follower option in django using DRF so here my models.py
class Connect(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, unique = True, related_name = 'rel_from_set',on_delete=models.CASCADE)
following = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name = 'follwed_by')
def __str__(self):
return str(self.following.all().count())
urls.py
url(r'^conn/(?P<id>\d+)', ConnectApi),
serializer.py
class ConnectSerializer(serializers.ModelSerializer):
class Meta:
model=Connect
fields=('user','following')
views.py
#api_view(['GET','POST'])
def ConnectApi(request,id):
user_id=request.POST.get('id')
print(user_id)
# user_id=request.GET['id']
# action=request.POST.get('action')
if user_id :
# if user_id and action:
try:
user1=User.objects.get(id=user_id)
if user1 :
Connect.objects.get_or_create(user=request.user,
following=user1)
else:
Connect.objects.filter(user=request.user,
following=user).delete()
return JsonResponse({'status':'ok'})
except:
return JsonResponse({'status':'ki'})
return JsonResponse({'status':'ko'})
but whenever i fired up the url
http://127.0.0.1:8000/connect/conn/2
this is the response
{"status": "ko"}
and in the terminal i could see "NONE "
for print(user_id) command
i don't understand where i have done the mistake and i dont think if this is the best practice to make followers function if you know one please let me know
You are passing the id through url and it's directly available inside the view function. So, you don't want to query it from somewhere else
So, use this,
#api_view(['GET', 'POST'])
def ConnectApi(request, id):
user_id = id
print(user_id)
# your code
I have two models like this:
class Sector(models.Model):
name = models.CharField(max_length=100, db_index=True, unique=True) # HERE IF I REMOVE unique=True, it works correctly
class Address(models.Model):
...
sector = models.ForeignKey(Sector, null=True, blank=True)
And a serializer for the Address model:
In the view, I have this:
address_serialized = AddressSerializer(data=request.data)
if address_serialized.is_valid():
address_serialized.save(client=client)
It never gets to the create function. I have a serialized with a create function that looks like this:
class AddressSerializer(serializers.ModelSerializer):
city_gps = CitySerializer(required=False)
sector = SectorSerializer(required=False)
class Meta:
model = Address
fields = (..., "sector")
def create(self, validated_data):
...
sector_dict = validated_data.get("sector", None)
sector = None
if sector_dict and "name" in sector_dict and city_gps:
if Sector.objects.filter(name=sector_dict["name"], city=city_gps).exists():
sector = Sector.objects.get(name=sector_dict["name"], city=city_gps)
# pdb.set_trace()
if "sector" in validated_data:
validated_data.pop("sector")
if "city_gps" in validated_data:
validated_data.pop("city_gps")
address = Address.objects.create(sector=sector, city_gps=city_gps, **validated_data)
return address
The code never touches this function, is_valid() returns False. And the message is
{"sector":{"name":["sector with this name already exists."]}}
I need to be able to create a new address with FK to the already existing sector. How can I achieve that? Any advice will help.
EDIT
The view looks like this:
class ClientProfileAddressCreateView(APIView):
# throttle_scope = '1persecond'
renderer_classes = (JSONRenderer,)
permission_classes = (IsAuthenticated,)
def post(self, request):
try:
client = Client.objects.get(user=request.user)
except ObjectDoesNotExist:
return Response({"error": "A client profile for the logged user does not exit"},
status=status.HTTP_404_NOT_FOUND)
address_serialized = AddressSerializer(data=request.data)
print("address_serialized.is_valid: %s" % address_serialized.is_valid()) # Returns False when unique=True in models
if address_serialized.is_valid():
# print("address_serialized: %s" % address_serialized.data)
address_serialized.save(client=client)
else:
return Response(data=address_serialized.errors, status=status.HTTP_400_BAD_REQUEST)
return Response(data=address_serialized.data, status=status.HTTP_201_CREATED)
This is a known issue with nested serializers and unique constraints.
Really awesome thing to always do is actually print the Serializer - that can give you a lot of extra info.
When you have a json like this:
{
"Sector": {
"name": "Sector XYZ"
},
"address_line_one": “Some Random Address”
}
Django REST framework does not know whether you're creating or getting the Sector object, thus it forces validation on every request.
What you need to do is the following:
class SectorSerializer(serializers.ModelSerializer):
# Your fields.
class Meta:
model = Address
fields = ("Your Fields",)
extra_kwargs = {
'name': {
'validators': [],
}
}
Then to handle validation you would need to redo the create/update part to fit the uniqueness constraint and raise exception/validation error.
I hope this helps.
Helpful links: This SO Answer and Dealing with unique constraints in nested serializers
EDIT :
As per cezar's request: I will add how it might look like to override the create method of the serializer. I have not tried this code, but the logic goes like this.
class SectorSerializer(serializers.ModelSerializer):
# Your fields.
class Meta:
model = Address
fields = ("Your Fields",)
extra_kwargs = {
'name': {
'validators': [],
}
}
def create(self, validated_data):
raise_errors_on_nested_writes('create', self, validated_data)
ModelClass = self.Meta.model
info = model_meta.get_field_info(ModelClass)
many_to_many = {}
for field_name, relation_info in info.relations.items():
if relation_info.to_many and (field_name in validated_data):
many_to_many[field_name] = validated_data.pop(field_name)
# FIELD CHECK
your_field = validated_data.get("your_field","") # or validated_data["your_field"]
try:
YourModel.objects.filter(your_check=your_field).get()
raise ValidationError("Your error")
except YourModel.DoesNotExist:
# if it doesn't exist it means that no model containing that field exists so pass it. You can use YourQuerySet.exists() but then the logic changes
pass
try:
instance = ModelClass.objects.create(**validated_data)
except TypeError:
tb = traceback.format_exc()
msg = (
'Got a `TypeError` when calling `%s.objects.create()`. '
'This may be because you have a writable field on the '
'serializer class that is not a valid argument to '
'`%s.objects.create()`. You may need to make the field '
'read-only, or override the %s.create() method to handle '
'this correctly.\nOriginal exception was:\n %s' %
(
ModelClass.__name__,
ModelClass.__name__,
self.__class__.__name__,
tb
)
)
raise TypeError(msg)
# Save many-to-many relationships after the instance is created.
if many_to_many:
for field_name, value in many_to_many.items():
field = getattr(instance, field_name)
field.set(value)
return instance