Closed alysarnau closed 2 years ago
Can you share your models so we can check out how the relationship is set up ?
Material:
from django.db import models
from django.core.validators import MaxValueValidator, MinValueValidator
# Create your models here.
class Material(models.Model):
material_name = models.CharField(max_length=50)
material_description = models.CharField(max_length=500)
material_image = models.CharField(max_length=150)
sale_price = models.IntegerField(
validators=[
MaxValueValidator(2147483647),
MinValueValidator(1),
]
)
link_to_wiki = models.CharField(max_length=150)
def __str__(self):
return self.material_name
Character
from django.db import models
from django.contrib.auth import get_user_model
from django.core.validators import MaxValueValidator, MinValueValidator
# Create your models here.
class Character(models.Model):
# define fields
# https://docs.djangoproject.com/en/3.0/ref/models/fields/
owner = models.ForeignKey(
get_user_model(),
on_delete=models.CASCADE
)
# we will use these constants for the pets later on
CAT = 'Cat'
DOG = 'Dog'
# this defines the pet choices later on
PET_TYPE_CHOICES = [
(CAT, 'Cat'),
(DOG, 'Dog'),
]
# we will use these constants for the love interests! yes I will be adding in Krobus
ALEX = 'AL'
ELLIOT = 'EL'
HARVEY = 'HA'
SAM = 'SA'
SEBASTIAN = 'SE'
SHANE = 'SH'
ABIGAIL = 'AB'
EMILY = 'EM'
HALEY = 'HL'
LEAH = 'LE'
MARU = 'MA'
PENNY = 'PE'
KROBUS = 'KR'
# this defines the love interest choices later on
LOVE_INTEREST_CHOICES = [
(ALEX, 'Alex'),
(ELLIOT, 'Elliot'),
(HARVEY, 'Harvey'),
(SAM, 'Sam'),
(SEBASTIAN, 'Sebastian'),
(SHANE, 'Shane'),
(ABIGAIL, 'Abigail'),
(EMILY, 'Emily'),
(HALEY, 'Haley'),
(LEAH, 'Leah'),
(MARU, 'Maru'),
(PENNY, 'Penny'),
(KROBUS, 'Krobus'),
]
# and these are for the pet urls
CAT1 = 'C1'
CAT2 = 'C2'
CAT3 = 'C3'
DOG1 = 'D1'
DOG2 = 'D2'
DOG3 = 'D3'
# this defines the pet URL choices later on
PET_URL_CHOICES = [
(CAT1, 'Cat 1'),
(CAT2, 'Cat 2'),
(CAT3, 'Cat 2'),
(DOG1, 'Dog 1'),
(DOG2, 'Dog 2'),
(DOG3, 'Dog 3'),
]
# and now for the models
name = models.CharField(max_length=12)
Platform = models.CharField(max_length=30)
farm_name = models.CharField(max_length=12)
# this lets us define the choices given the character
pet_type = models.CharField(
max_length=3,
choices=PET_TYPE_CHOICES,
)
pet_name = models.CharField(max_length=12)
pet_image = models.CharField(
max_length=2,
choices=PET_URL_CHOICES,
)
love_interest = models.CharField(
max_length=2,
choices=LOVE_INTEREST_CHOICES,
)
horse_name = models.CharField(max_length=12)
total_g = models.IntegerField(
default=1,
validators=[
MaxValueValidator(2147483647),
MinValueValidator(0),
]
)
year = models.IntegerField(
default=1,
validators=[
MaxValueValidator(2147483647),
MinValueValidator(1),
]
)
def __str__(self):
return self.name
Inventory
from django.db import models
from JunimoDatabaseApp.models.character import Character
from JunimoDatabaseApp.models.material import Material
# you will need to bring over the user for authentication
# do we want to create an amount of 0 for all resource IDs?
# Create your models here.
class Inventory(models.Model):
character_id = models.ForeignKey(Character, on_delete=models.CASCADE)
material_id = models.ForeignKey(Material, on_delete=models.CASCADE)
amount = models.IntegerField()
def __str__(self):
return ("{}'s {}".format(self.character_id, self.material_id) )
# if update amount, all that is needed to send is character id, resource id, new amount
For further context, the show route works fine and this is how it delivers the json:
{
"inventory": {
"material": {
"id": 2,
"material_name": "Bone Fragment",
"material_description": "A small piece of bone.",
"material_image": "Bone_Fragment.png",
"sale_price": 12,
"link_to_wiki": "https://stardewvalleywiki.com/Bone_Fragment"
},
"character": {
"id": 2,
"name": "Alys",
"Platform": "Switch",
"farm_name": "Fern",
"pet_type": "Dog",
"pet_name": "Sunny",
"pet_image": "D2",
"love_interest": "PE",
"horse_name": "Trelle",
"total_g": 999999,
"year": 4,
"owner": 1
},
"amount": 7,
"id": 2
}
}
I'm running into an issue with the nested values :/
Hm, good news, getting a new error! Bad news, getting a new error. Here's what I get for the patch route:
Here is my updated serializer:
# for serializer SPECIFICALLY for PATCH/POST routes
class UpdateInventorySerializer(serializers.ModelSerializer):
class Meta:
model = Inventory
fields = ('__all__')
And my updated views:
# this will affect ONE inventory entry for ONE character
class ShowInventoryView(generics.RetrieveUpdateDestroyAPIView):
permission_classes=(IsAuthenticated,)
def get(self, request, pk, fk):
"""Index request"""
# Filter the characters by owner, so you can only see your owned characters
character = get_object_or_404(Character, pk=fk)
# Only do request if they own the character whose inventory it is
if request.user != character.owner:
raise PermissionDenied('Unauthorized, you do not own this character')
# Filter the inventories by character, so you can only see your owned inventories
inventory = get_object_or_404(Inventory, pk=pk, character_id=fk)
# Run the data through the serializer
data = InventorySerializer(inventory).data
return Response({ 'inventory': data })
# THESE ARE TRICKY DUE TO NESTED DATA
# create an entry in inventory
def post(self, request, pk, fk):
# this would work for patch better
"""Create request"""
# Filter the characters by owner, so you can only see your owned characters
character = get_object_or_404(Character, pk=fk)
# Only do request if they own the character whose inventory it is
if request.user != character.owner:
raise PermissionDenied('Unauthorized, you do not own this character')
# Only do request if they own the character whose inventory it is
if request.user != character.owner:
raise PermissionDenied('Unauthorized, you do not own this character')
# If pass...
# print the request data
print(request.data)
# Serialize/create inventory
inventory = UpdateInventorySerializer(data=request.data)
# If the inventory data is valid according to our serializer...
if inventory.is_valid():
# Save the created inventory & send a response
inventory.save()
return Response({ 'inventory': inventory.data }, status=status.HTTP_201_CREATED)
# If the data is not valid, return a response with the errors
return Response(inventory.errors, status=status.HTTP_400_BAD_REQUEST)
def patch(self, request, pk, fk):
"""Update Request"""
# Locate Character for auth
character = get_object_or_404(Character, pk=fk)
# Check the character's owner against the user making this request
if request.user != character.owner:
raise PermissionDenied('Unauthorized, you do not own this character')
# If pass...
inventory = get_object_or_404(Inventory, pk=pk)
# Serialize/create inventory for validation
# Validate updates with serializer
updated_inventory = UpdateInventorySerializer(inventory, data=request.data)
if updated_inventory.is_valid():
# Save & send a 204 no content
updated_inventory.save()
return Response(status=status.HTTP_204_NO_CONTENT)
# If the data is not valid, return a response with the errors
return Response(updated_inventory.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk, fk):
"""Delete request"""
# Locate mango to delete
character = get_object_or_404(Character, pk=pk)
# Check the characters's owner against the user making this request
if request.user != character.owner:
raise PermissionDenied('Unauthorized, you do not own this character')
# Only delete if the user owns the character
# set inventory to delete
inventory = get_object_or_404(Inventory, pk=fk)
inventory.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
FIXED IT!!!!!
For the post and patch requests, I just had to unnest the body. For example:
instead of this in Postman:
{
"inventory": {
"material_id": 2,
"character_id": 2,
"amount": 500
}
}
I had to send this in Postman
{
"material_id": 2,
"character_id": 2,
"amount": 500
}
What stack are you using?
(ex: MERN(mongoose + react), DR(django + react), PEN, etc.)
DR
What's the problem you're trying to solve?
I'm trying to update 'amount' on a table with 4 lines 1) Material_id (foreign key) 2) Resource_id (foreign key) 3) Amount 4) Primary Key
But I'm not sending the request properly due to the serializer and I'm trying to figure it out!
Post any code you think might be relevant (one fenced block per file)
PATCH route:
If you see an error message, post it here. If you don't, what unexpected behavior are you seeing?
When I use postman to patch to with this body:
I get this:
What is your best guess as to the source of the problem?
I'm not referencing material or character in a way the serializer can use
What things have you already tried to solve the problem?
In InventorySerializer, I set these fields to read only to help deal with nested data issue:
Didn't work!
Paste a link to your repository here https://github.com/alysvolatile/my-junimo-api/tree/crudInventory