Problem when I try to save data in my database Django - python

I tried to make a pure api Django and save some data in my database for practicing.
But I have some problems when I try to save data.
For example
I tried this code for the post method
def post(self, request, *args, **kwargs):
form = UpdateModelForm(self.request.POST)
if form.is_valid():
obj = form.save(commit=True)
obj_data = obj.serialize()
return self.render_to_response(obj_data, status=201)
if form.errors:
data = json.dumps(form.errors)
return self.render_to_response(data, status=400)
data = {"message": "Not Allowed"}
return self.render_to_response(data, status=400)
And here is my Form validation
from django import forms
from .models import Update as UpdateModel
class UpdateModelForm(forms.ModelForm):
class Meta:
model = UpdateModel
fields = [
'user',
'content',
'image'
]
And the model
class UpdateQuerySet(models.QuerySet):
#def serialize(self):
# qs = self
# return serialize('json', qs, fields=('user', 'content', 'image'))
def serialize(self):
list_values = list(self.values("user", "content", "image", "id"))
return json.dumps(list_values)
class UpdateManager(models.Manager):
def get_queryset(self):
return UpdateQuerySet(self.model, using=self._db)
# Create your models here.
class Update(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
content = models.TextField(blank=True, null=True)
image = models.ImageField(upload_to=upload_update_image, blank=True, null=True)
timestamp = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now_add=True)
objects = UpdateManager()
def __str__self(self):
return self.content or ""
def serialize(self):
try:
image = self.image.url
except:
image = ""
data = {
"id": self.id,
"user": self.user,
"content": self.content,
"image": image
}
data_json = json.dumps(data)
return data_json
My problem arises when I try to use the method, I get this dubious error.
What do you think it is?
{"user": ["Select a valid choice. That choice is not one of the available choices."]}

Try with :
data = {
"id": self.id,
"user": self.user.id,
"content": self.content,
"image": image
}
NB : Pass the user ID instead of the user object.

Related

Django : TypeError: int() argument must be a string, a bytes-like object or a number, not 'Driver'

When creating vehicle, in driver_id, I must pass an instance of an object Drive (so when I pass a number, I find an object with this ID in the serializer and pass it, because otherwise it would give an error that an instance needs to be passed).
But when I want to display information on some object vehicle/1/, it will give an error if this object has a driver_id field of an object instance, But when it is null, then everything is fine
Vehicle Model:
class Vehicle(models.Model):
driver_id = models.ForeignKey(Driver,
related_name='vehicles',
on_delete=models.PROTECT,
null=True,
blank=True)
make = models.CharField(max_length=255)
model = models.CharField(max_length=255, verbose_name='Model')
plate_number = models.CharField(max_length=255, verbose_name='Plate Number')
created_at = models.DateTimeField(auto_now_add=True, verbose_name='Created')
updated_at = models.DateTimeField(auto_now=True, verbose_name='Updated')
def __str__(self):
return self.model
URLs:
urlpatterns = [
path('vehicle/', VehicleListView.as_view()),
path('vehicle/<int:vehicle_id>/', VehicleView.as_view())
]
class VehicleListView(APIView):
def get(self, request):
vehicles = Vehicle.objects.all()
serializer = VehicleSerializer(vehicles, many=True)
return Response({"vehicles": serializer.data})
def post(self, request):
vehicles = request.data.get('vehicle')
serializer = VehicleSerializer(data=vehicles)
if serializer.is_valid(raise_exception=True):
vehicle_saved = serializer.save()
context = {
'success': f'Vehicle {vehicle_saved.plate_number} created successfully'
}
return Response(context)
class VehicleView(APIView):
def get(self, request, vehicle_id):
vehicle = Vehicle.objects.filter(id=vehicle_id)
serializer = VehicleSerializer(vehicle, many=True)
return Response({"vehicle": serializer.data})
def put(self, request, vehicle_id):
vehicle_saved = get_object_or_404(Vehicle.objects.all(), pk=vehicle_id)
data = request.data.get('vehicle')
serializer = VehicleSerializer(instance=vehicle_saved, data=data, partial=True)
if serializer.is_valid(raise_exception=True):
vehicle_saved = serializer.save()
return Response({
"success": f"Vehicle '{vehicle_saved.plate_number}' updated successfully"
})
def delete(self, request, vehicle_id):
vehicle = get_object_or_404(Vehicle.objects.all(), pk=vehicle_id)
vehicle.delete()
return Response({
"message": f"Vehicle with id `{vehicle_id}` has been deleted."
}, status=204)
class VehicleSerializer(serializers.Serializer):
driver_id = serializers.IntegerField(required=False)
make = serializers.CharField(max_length=255)
model = serializers.CharField(max_length=255)
plate_number = serializers.CharField(max_length=255)
def create(self, validated_data):
validated_data['driver_id'] = Driver.objects.get(id=validated_data['driver_id'])
return Vehicle.objects.create(**validated_data)
def update(self, instance, validated_data):
if "driver_id" in validated_data:
instance.driver_id = Driver.objects.get(id=validated_data['driver_id'])
instance.make = validated_data.get('make', instance.make)
instance.model = validated_data.get('model', instance.model)
instance.plate_number = validated_data.get('plate_number', instance.plate_number)
instance.save()
return instance
Driver Model:
class Driver(models.Model):
first_name = models.CharField(max_length=255, verbose_name='First Name')
last_name = models.CharField(max_length=255, verbose_name='Last Name')
created_at = models.DateTimeField(auto_now_add=True, verbose_name='Created')
updated_at = models.DateTimeField(auto_now=True, verbose_name='Updated')
def __str__(self):
return self.first_name
Driver Views:
class DriverView(APIView):
def get(self, request, driver_id):
driver = Driver.objects.filter(id=driver_id)
serializer = DriverSerializer(driver, many=True)
return Response({"driver": serializer.data})
def put(self, request, driver_id):
saved_driver = get_object_or_404(Driver.objects.all(), pk=driver_id)
data = request.data.get('driver')
serializer = DriverSerializer(instance=saved_driver, data=data, partial=True)
if serializer.is_valid(raise_exception=True):
saved_driver = serializer.save()
return Response({
"success": f"Driver '{saved_driver.first_name}' updated successfully"
})
def delete(self, request, driver_id):
driver = get_object_or_404(Driver.objects.all(), pk=driver_id)
driver.delete()
return Response({
"message": f"Driver with id `{driver_id}` has been deleted."
}, status=204)
class DriverListView(APIView):
def get(self, request):
qp = request.query_params
if not qp:
drivers = Driver.objects.all()
else:
created_gte, created_lte = 'created_at__gte', 'created_at__lte'
timezone = created_gte if created_gte in qp else created_lte
time = qp.get(timezone)
time = map(int, time.split('-')[::-1])
if timezone == created_gte:
drivers = Driver.objects.filter(created_at__gte=datetime(*time))
else:
drivers = Driver.objects.filter(created_at__lte=datetime(*time))
serializer = DriverSerializer(drivers, many=True)
return Response({"driver": serializer.data})
def post(self, request):
drivers = request.data.get('driver')
serializer = DriverSerializer(data=drivers)
if serializer.is_valid(raise_exception=True):
driver_saved = serializer.save()
return Response({
'success': f'Driver {driver_saved.first_name} created successfully'
})
Driver Serializer:
class DriverSerializer(serializers.Serializer):
first_name = serializers.CharField(max_length=255)
last_name = serializers.CharField(max_length=255)
def create(self, validated_data):
return Driver.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.first_name = validated_data.get('first_name', instance.first_name)
instance.last_name = validated_data.get('last_name', instance.last_name)
instance.save()
return instance
Request:
"vehicle":
{
"driver_id": 1,
"make": "5000г",
"model": "Merc",
"plate_number": "AE 1111 AB"
}
}
Error Message:
TypeError: int() argument must be a string, a bytes-like object or a number, not 'Driver'
You need to pass the id of your object so replace all occurences of the following :
instance.driver_id = Driver.objects.get(id=validated_data['driver_id'])
by
instance.driver_id = Driver.objects.get(id=validated_data['driver_id']).id
Mate, you have got quite a few things confused )
For starters, this line in delete:
vehicle = get_object_or_404(Vehicle.objects.all(), pk=vehicle_id)
should be
vehicle = get_object_or_404(Vehicle, pk=vehicle_id)
objects.all() gets you a whole quesryset, which you don't need here.
Next, this line in create:
validated_data['driver_id'] = Driver.objects.get(id=validated_data['driver_id'])
You already have driver_id in request's data. That what the client of your API wants to delete. There's absolutely no reason to reassign values in that validated_data dict. What's worse, you're doing this:
load Driver model instance from db by using driver_id from the API call. OK, makes sense. What doesn't make sense is why you are substituting driver_id and id. If you need to mirror id into another field in the model, then at least don't mutate it. But my guess is, you don't need this driver_id at all.
Then you are taking the object looked up by id (in db) = drived_id (in the request), and you assigned that object to the key 'driver_id' inside the dict - and then attempt to create a new instance which you have just looked up! Now this makes no sense.

Django/DRF backend not receiving complete data from POST request

I'm trying to send data to my Django/DRF backend via Axios POST requests. Some of them have been working, but some of them haven't been. This snippet, for example, works fine:
axiosInstance.post(
`/api/notes/`,
{
title,
content: serialize(editorContent),
}
)
This one, however, has been causing problems:
axiosInstance.post(
`/api/notebooks/`,
{
name: newNotebookName,
user,
}
)
In the second snippet, the name attribute gets through to the backend, but for some reason the user attribute does not. I tried using console.log to see if user is undefined at the moment it's sent off to the backend and it isn't.
I tried to change the way I define the keys in the object I send to the backend so that they're not dynamic like in this answer and the result hasn't changed.
The user variable is a string containing an email address, like 'foobar#gmail.com'. I thought maybe Django blocked it by default for security reasons, but I split the string at the # and tried sending the first part only and it didn't work either.
I also tried defining a new variable so that the type of user is string rather than string | undefined and it still doesn't work. The value of title is also string and it gets through to the backend (it's defined in the validated_data object my serializer sees in its create() method). So I'm at a loss as to what this could be. Here are some of my files:
axiosAPI.ts
import axios from 'axios'
const baseURL = 'http://localhost:8000/'
export const axiosInstance = axios.create({
baseURL: baseURL,
timeout: 5000,
headers: {
'xsrfHeaderName': 'X-CSRFTOKEN',
'xrsfCookieName': 'csrftoken',
'Authorization': localStorage.getItem('access_token') ? 'JWT ' + localStorage.getItem('access_token') : null,
'Content-Type': 'application/json',
'accept': 'application/json',
}
})
axiosInstance.interceptors.response.use(
response => response,
error => {
const originalRequest = error.config
// Prevent infinite loops if login credentials invalid
if (error.response.status === 406 && originalRequest.url === baseURL + 'auth/token/obtain') {
window.location.href = '/login/'
return Promise.reject(error);
}
// Respond to expired refresh tokens
if (error.response.data.code === 'token_not_valid' && error.response.status === 401 && error.response.statusText === 'Unauthorized') {
const refreshToken = localStorage.getItem('refresh_token')
if (refreshToken) {
const tokenObj = JSON.parse(atob(refreshToken.split('.')[1]))
const currentTime = Math.ceil(Date.now() / 1000)
if (tokenObj.exp > currentTime) {
return axiosInstance
.post('/auth/token/refresh/', { refresh: refreshToken })
.then(response => {
localStorage.setItem('access_token', response.data.access)
localStorage.setItem('refresh_token', response.data.refresh)
axiosInstance.defaults.headers['Authorization'] = 'JWT ' + response.data.access
originalRequest.headers['Authorization'] = 'JWT ' + response.data.access
return axiosInstance(originalRequest);
})
.catch(err => {
console.log(err)
throw err;
})
} else {
console.log('Refresh token is expired.')
window.location.href = '/login/'
}
} else {
console.log('Refresh token not available.')
window.location.href = '/login/'
}
}
// Respond to invalid access tokens
if (error.response.status === 401 && error.response.statusText === 'Unauthorized') {
const refreshToken = localStorage.getItem('refresh_token')
return axiosInstance
.post('/auth/token/refresh/', { refresh: refreshToken })
.then(response => {
localStorage.setItem('access_token', response.data.access)
localStorage.setItem('refresh_token', response.data.refresh)
axiosInstance.defaults.headers['Authorization'] = 'JWT ' + response.data.access
originalRequest.headers['Authorization'] = 'JWT ' + response.data.access
return axiosInstance(originalRequest)
}).catch(err => {
console.log(err)
throw err;
})
}
}
)
notebooks/serializers.py
import bleach
import json
from rest_framework import serializers
from authentication.models import ErsatzNoteUser
from .models import Note, Notebook
class NoteSerializer(serializers.ModelSerializer):
note_id = serializers.SlugField(source='id', read_only=True, required=False)
title = serializers.JSONField(required=False)
content = serializers.CharField(required=False)
notebook = serializers.PrimaryKeyRelatedField(read_only=True, required=False)
date_modified = serializers.DateField(read_only=True, required=False)
date_created = serializers.DateField(read_only=True, required=False)
def create(self, validated_data):
title = json.dumps(validated_data['title'])
# Workaround to fix a currently unpatched bug in Slate
# that occurs when an editor's contents begin with a list
content = validated_data['content']
if content.startswith('<ul') or content.startswith('<ol'):
content = '<p></p>' + content
response_data = {
'title': title,
'content': content,
}
return Note.objects.create(**response_data)
def update(self, instance, validated_data):
instance.title = json.dumps(validated_data['title'])
# See the above comment in the 'create' method
content = validated_data['content']
if content.startswith('<ul') or content.startswith('<ol'):
content = '<p></p>' + content
instance.content = content
instance.save()
return instance
class Meta:
model = Note
fields = [ 'note_id', 'title', 'content', 'notebook', 'date_modified', 'date_created' ]
class NotebookSerializer(serializers.ModelSerializer):
notebook_id = serializers.SlugField(source='id', read_only=True, required=False)
name = serializers.CharField(max_length=64, default='')
notes = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
def create(self, validated_data):
# notebook_data = {
# 'name': validated_data['name'],
# 'user': ErsatzNoteUser.objects.get(email=validated_data['user']),
# }
print(validated_data)
return Notebook.objects.create(name=validated_data['name'], user=ErsatzNoteUser.objects.get(email=validated_data['user']))
class Meta:
model = Notebook
fields = [ 'notebook_id', 'name', 'notes', 'date_modified', 'date_created' ]
notebooks/views.py
import json
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from .serializers import NoteSerializer, NotebookSerializer
from .models import Note, Notebook
class NoteViewSet(viewsets.ModelViewSet):
serializer_class = NoteSerializer
def get_queryset(self):
return self.request.user.notes.all()
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = NoteSerializer(instance)
title = json.loads(serializer.data['title'])
response_data = {
'note_id': serializer.data['note_id'],
'title': title,
'content': serializer.data['content'],
'notebook': serializer.data['notebook'],
'date_modified': serializer.data['date_modified'],
'date_created': serializer.data['date_created'],
}
return Response(response_data)
class NotebookViewSet(viewsets.ModelViewSet):
serializer_class = NotebookSerializer
def get_queryset(self):
return self.request.user.notebooks.all()
notebooks/models.py
from django.db import models
from django.contrib.auth.models import User
from jsonfield import JSONField
from django.conf import settings
from .helpers import generate_slug
class Note(models.Model):
""" Represents an individual note. """
id = models.SlugField(max_length=settings.MAX_SLUG_LENGTH, primary_key=True)
title = JSONField(null=True)
content = models.TextField(null=True)
notebook = models.ForeignKey('Notebook', related_name='notes', on_delete=models.CASCADE, null=True, blank=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='notes', on_delete=models.CASCADE, null=True, blank=True)
date_created = models.DateField(auto_now_add=True)
date_modified = models.DateField(auto_now=True)
def save(self, *args, **kwargs):
if not self.id:
self.id = generate_slug(self, settings.MAX_SLUG_LENGTH)
# Temporary expedients for the sake of development
if not self.notebook:
self.notebook = Notebook.objects.get(id='YyOzNhMFMPtN8HM')
super(Note, self).save(*args, **kwargs)
def __str__(self):
return self.id
class Meta:
ordering = [ '-date_modified', '-date_created', ]
class Notebook(models.Model):
""" A collection of individual notes. """
id = models.SlugField(max_length=settings.MAX_SLUG_LENGTH, primary_key=True)
name = models.CharField(max_length=64, default='')
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='notebooks', on_delete=models.CASCADE)
date_created = models.DateField(auto_now_add=True)
date_modified = models.DateField(auto_now=True)
def save(self, *args, **kwargs):
if not self.id:
self.id = generate_slug(self, settings.MAX_SLUG_LENGTH)
super(Notebook, self).save(*args, **kwargs)
def __str__(self):
return self.name
class Meta:
ordering = [ '-date_modified', '-date_created', 'name' ]
Please don't hesitate to ask if you'd like further info.
EDIT: Changed NotebookViewSet so that it has a retrieve() method, the result hasn't changed:
class NotebookViewSet(viewsets.ModelViewSet):
serializer_class = NotebookSerializer
def get_queryset(self):
return self.request.user.notebooks.all()
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = NotebookSerializer(instance)
response_data = {
'name': serializer.data['name'],
'user': serializer.data['user'],
}
return Response(response_data)
In your example your NotebookViewSet class does not have a retrieve method.
It turns out that under the hood, DRF considers ForeignKey fields to be read_only. Meaning it's solely to be used for representing the object, and shouldn't be part of any create or update process. According to the documentation:
Read-only fields are included in the API output, but should not be included in the input during create or update operations. Any 'read_only' fields that are incorrectly included in the serializer input will be ignored.
Source.
But this didn't matter to my database, which requires me to identify the user that corresponds to each new Notebook at the time of its creation, or else a NOT NULL constraint is violated. So I just overwrote the default ModelViewSet create() method to pass on the user string manually to my NotebookSerializer as part of context, allowing me to use it to get the corresponding user object in the database and assign it to the newly created Notebook. I went about it like this:
class NotebookViewSet(viewsets.ModelViewSet):
serializer_class = NotebookSerializer
def get_queryset(self):
return self.request.user.notebooks.all()
def perform_create(self, serializer):
serializer.save()
def create(self, request, *args, **kwargs):
serializer = NotebookSerializer(data=request.data, context={ 'request': request })
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
class NotebookSerializer(serializers.ModelSerializer):
notebook_id = serializers.SlugField(source='id', read_only=True, required=False)
name = serializers.CharField(max_length=64, default='')
notes = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
def create(self, validated_data):
return Notebook.objects.create(
user=ErsatzNoteUser.objects.get(email=self.context['request'].user),
**validated_data
)
class Meta:
model = Notebook
fields = [ 'notebook_id', 'name', 'notes', 'date_modified', 'date_created' ]
That's all it took.

Why the django models clean() method doesnt work

I'm trying to overwrite the value of the DateField field if it's empty. As usual, a field with this type is not validated during serialization if the value is not an object of datetime class. I need to write null to the database if the value is empty. To do this, I'm trying to change the clean() model method.
serializer.py
class VendorsSerializer(serializers.ModelSerializer):
contacts = VendorContactSerializer(many=True)
class Meta:
model = Vendors
fields = (...
'nda',
'contacts',)
def create(self, validated_data):
contact_data = validated_data.pop('contacts')
vendor = Vendors.objects.create(**validated_data)
vendor.full_clean()
for data in contact_data:
VendorContacts.objects.create(vendor=vendor, **data)
return vendor
models.py
class Vendors(models.Model):
...
nda = models.DateField(blank=True, null=True)
def clean(self):
if self.nda == "":
self.nda = None
view.py
class VendorsCreateView(APIView):
"""Create new vendor instances from form"""
permission_classes = (permissions.AllowAny,)
serializer_class = VendorsSerializer
def post(self, request, *args, **kwargs):
serializer = VendorsSerializer(data=request.data)
try:
serializer.is_valid(raise_exception=True)
serializer.save()
except ValidationError:
return Response({"errors": (serializer.errors,)},
status=status.HTTP_400_BAD_REQUEST)
else:
return Response(request.data, status=status.HTTP_200_OK)
Why clean() doesnt run ?
json
{
"vendor_name": "The Greey swAlsbudam2",
"country": "Belgium",
"nda": "",
"contacts": [{"contact_name": "Mrk", "phone": "2373823", "email": "ryryr#gmail.com"},
{
"contact_name": "Uio",
"phone": "34567",
"email": "ryfhhryr#gmail.com"
}
]
}
Because you are creating instance before calling full_clean here
vendor = Vendors.objects.create(**validated_data)
vendor.full_clean()
The first line creates the object in the database (with an empty string). The second line performs cleaning but does not save again in the database. You have to perform cleaning before saving.
vendor = Vendors(**validated_data)
vendor.full_clean()
vendor.save()

ManyToMany Fields do not get filled or throws TypeError when creating an entity

This is a problem which I've been fighting with for a while and now I gave up. I am writing a User/Profile model in Django 2.0.2 (python 3.6 + postgres 10 on Linux) which is as follows :
class Config_Table(models.Model):
entity_name = models.TextField(primary_key=True)
category = models.TextField()
description = models.TextField(blank=True,default='')
Above table keeps some static information which gets me into trouble.
class UserProfile(AbstractUser):
"The Profile of a user with details are stored in this model."
username = models.TextField(primary_key=True, max_length=11)
first_name = models.TextField(max_length=50,blank=True,default='')
last_name = models.TextField(max_length=100,blank=True,default='')
phone_number = models.TextField(max_length=11,blank=True,default='')
avatar = models.ImageField(blank=True, upload_to='Pictures/')
GENDER_CHOICES = (
('M','Male'),
('F','Female'),
)
gender = models.CharField(max_length=1,choices=GENDER_CHOICES, default='M')
city = models.TextField(max_length=25, blank=True, default='NY')
description = models.TextField(max_length=2000, blank=True, default='')
interests = models.ManyToManyField(Config_Table, blank=True, default='')
date_of_birth = models.DateField(blank=True)
official_docs = models.ImageField(blank=True, upload_to='Pictures/')
team_name = models.TextField(blank=True,default='')
debit_card_number = models.IntegerField(blank=True, default=0)
MUSIC_CHOICES = (
('Rock','Rock Music'),
('Trad','Traditional Music'),
('Elec','Electronic Music'),
('Clas','Classical Music')
)
favorite_music = ArrayField(models.TextField(blank=True,default=''),size=2,blank=True, default='{}')
class Meta:
permissions=(("User","User level permission"),
("Tour","Tourleader level permission"),
("Admin","Administrators"))
my views.py :
class UserList(APIView):
"""
List all users, or create a new user.
"""
def get(self, request, format=None):
users = UserProfile.objects.all()
target_users = []
for user in users.iterator():
if user.is_superuser == False:
target_users.append(user)
serializer = UserProfileSerializer(target_users, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = UserProfileSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save()
print(serializer.errors)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class UserDetail(APIView):
"""
Retrieve, update or delete a User.
"""
pk_url_kwarg = 'username'
def get_object(self, pk):
try:
return UserProfile.objects.get(pk=pk)
except UserProfile.DoesNotExist:
raise Http404
def get(self, request, pk, format=None):
user = self.get_object(pk=pk)
if user.is_superuser == False:
serializer = UserProfileSerializer(user)
return Response(serializer.data)
else:
return Response(status=status.HTTP_404_NOT_FOUND)
def put(self, request, pk, format=None):
user = self.get_object(pk)
serializer = UserProfileSerializer(user, data=request.data, partial=True)
if serializer.is_valid():
for attr, value in serializer.validated_data.items():
if attr == 'password' and attr is None:
serializer.validated_data['password'] = user.password
break
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk, format=None):
user= self.get_object(pk)
user.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
and my serializers.py:
def create(self, validated_data):
hashed_password = make_password(validated_data['password']) # get the hashed password
print(validated_data)
user = UserProfile(
username=validated_data['username'],
email = validated_data['email'],
first_name= validated_data['first_name'],
last_name= validated_data['last_name'],
phone_number=validated_data['phone_number'],
avatar=validated_data.pop('avatar'),
gender=validated_data['gender'],
city=validated_data['city'],
description=validated_data['description'],
date_of_birth=validated_data.pop('date_of_birth'),
# user_type=validated_data['user_type'],
official_docs=validated_data.pop('official_docs'),
team_name=validated_data['team_name'],
debit_card_number=validated_data['debit_card_number'],
favorite_music=validated_data['favorite_music'],
)
user.set_password(hashed_password)
interest = validated_data['interests']
user.interests.add(validated_data['interests'])
user.groups.add(validated_data['groups'])
user.save()
return user
The problem I have happens inside the serializer, when I want to submit some JSON object through http, like below (sample data for testing) :
{
"username": "12345678004",
"password": "thisisatest",
"last_login": null,
"is_superuser": false,
"email" : "sample#gmail.com",
"first_name": "AAA",
"last_name": "BBB",
"phone_number": "12045678000",
"gender": "M",
"city": "NY",
"description": "",
"date_of_birth": "2010-03-28",
"team_name": "",
"avatar": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBxAPBhAREQ8QDQ8PDxIWEBAWFRANDxAQFRUWFhURExUYHSggGBolGxUTITEhJSkrLi4uFx8zODMsNygtLisBCgoKBQUFDgUFDisZExkrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrK//AABEIAOEA4QMBIgACEQEDEQH/xAAbAAEAAgMBAQAAAAAAAAAAAAAABAUBAgMGB//EADIQAQABAwEHAQYEBwAAAAAAAAABAgMRBAUSITFBUXFhIjKRobHBM4HR8RMjQmJyguH/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/APpgCIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDIAAAAAAAAAAAAAAAAAAAAAAANrduaq8RGZkGqVY0FdXON2PXn8E/SaOKIzPGrv0jwlAhW9nURHHNU/B3p0tER7sOwDjVpaJj3YcbmzqJjhmJTAFPf0FdPGPaj5/BEejRtVo4rjPKrv38gpRtdtzTXiYxLUAAAAAAAAAAAAAAAAAACIzOO670emi3b/unnP2Q9l2M1zVPKOXlaAAAAAAAAA4azTxct+scpUlVMxVieEw9ErNq2MVRVHXn57grwAAAAAAAAAAAAAAAAbW4zciO8x9QXekt7unpj0diOQAAAAAAAAA5aq3vWJj04eXUB5yRtejF2fMtQAAAAAAAAAAAAAAG9n8an/KPq0InE57A9GMUTmiJ7wyAAAAAAAAADFdWKJntAKDUT/Oq8tGa5zXPlgAAAAAAAAAAAAAAAAFxs29vWMdafolqLR39y9E9J4T4XkTmMxxiQZAAAAAAAARNpXd2xjrV9OqVM4jM8IhSay/v3pnpHLwDgAAAAAAAAAAAAAAAAAAnaDWbvs1cuk9kEB6OJ4Cp0F65E4iJqp7f9W0cgAAAAAVevuXJ4TE00+nHPkDX6ze9mn3es90AAAAAAAAAAAAAAAAAAAddPYqrrxH5z0gHOiiaqsRGZWWm2dEca+Pp0S9Pp6aKeEces9ZdQYpiIjEcIZAAAAAAnkAIep0FNWZp9mfkq71mqirEx+kvQNLtqKqcTGQefEjV6Sbc96Z5T9kcAAAAAAAAAAAAAGaKZmqIjnIN9PZmu5iPznsu7FmKLeI/dppNPFu1jrPOXcAAAAAAAAAAAAGK6YmnExmJUut0v8OvvTPKfsu2l63FduYnlIPPje/ami5MT+8NAAAAAAAAAAAFlsvT/wBc/wCv6oFm3vXYiOsr6imKaIiOUA2AAAAAAAAAAAAAAABE2jp9+1mPep+cKd6NSa6zuX57TxgEcAAAAAAAAAFjsm1xmr8o+6ycdJb3dPTHpxdgAAAAAAAAAAAAAAAAEPadresZ60/RMa105omO8A88NrlOLkx2lqAAAAAAA6aejev0x3lzStmU51XiJkFyAAAAAAAAAAAAAAAAAAACl2jRjUz6oyw2vT7VM+ivAAAAAAATdk/jz4AFsAAAAAAAAAAAAAAAAAAACBtb8KPKrAAAAAH/2Q==",
"official_docs": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAkGBxAPBhAREQ8QDQ8PDxIWEBAWFRANDxAQFRUWFhURExUYHSggGBolGxUTITEhJSkrLi4uFx8zODMsNygtLisBCgoKBQUFDgUFDisZExkrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrK//AABEIAOEA4QMBIgACEQEDEQH/xAAbAAEAAgMBAQAAAAAAAAAAAAAABAUBAgMGB//EADIQAQABAwEHAQYEBwAAAAAAAAABAgMRBAUSITFBUXFhIjKRobHBM4HR8RMjQmJyguH/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/APpgCIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDIAAAAAAAAAAAAAAAAAAAAAAANrduaq8RGZkGqVY0FdXON2PXn8E/SaOKIzPGrv0jwlAhW9nURHHNU/B3p0tER7sOwDjVpaJj3YcbmzqJjhmJTAFPf0FdPGPaj5/BEejRtVo4rjPKrv38gpRtdtzTXiYxLUAAAAAAAAAAAAAAAAAACIzOO670emi3b/unnP2Q9l2M1zVPKOXlaAAAAAAAAA4azTxct+scpUlVMxVieEw9ErNq2MVRVHXn57grwAAAAAAAAAAAAAAAAbW4zciO8x9QXekt7unpj0diOQAAAAAAAAA5aq3vWJj04eXUB5yRtejF2fMtQAAAAAAAAAAAAAAG9n8an/KPq0InE57A9GMUTmiJ7wyAAAAAAAAADFdWKJntAKDUT/Oq8tGa5zXPlgAAAAAAAAAAAAAAAAFxs29vWMdafolqLR39y9E9J4T4XkTmMxxiQZAAAAAAAARNpXd2xjrV9OqVM4jM8IhSay/v3pnpHLwDgAAAAAAAAAAAAAAAAAAnaDWbvs1cuk9kEB6OJ4Cp0F65E4iJqp7f9W0cgAAAAAVevuXJ4TE00+nHPkDX6ze9mn3es90AAAAAAAAAAAAAAAAAAAddPYqrrxH5z0gHOiiaqsRGZWWm2dEca+Pp0S9Pp6aKeEces9ZdQYpiIjEcIZAAAAAAnkAIep0FNWZp9mfkq71mqirEx+kvQNLtqKqcTGQefEjV6Sbc96Z5T9kcAAAAAAAAAAAAAGaKZmqIjnIN9PZmu5iPznsu7FmKLeI/dppNPFu1jrPOXcAAAAAAAAAAAAGK6YmnExmJUut0v8OvvTPKfsu2l63FduYnlIPPje/ami5MT+8NAAAAAAAAAAAFlsvT/wBc/wCv6oFm3vXYiOsr6imKaIiOUA2AAAAAAAAAAAAAAABE2jp9+1mPep+cKd6NSa6zuX57TxgEcAAAAAAAAAFjsm1xmr8o+6ycdJb3dPTHpxdgAAAAAAAAAAAAAAAAEPadresZ60/RMa105omO8A88NrlOLkx2lqAAAAAAA6aejev0x3lzStmU51XiJkFyAAAAAAAAAAAAAAAAAAACl2jRjUz6oyw2vT7VM+ivAAAAAAATdk/jz4AFsAAAAAAAAAAAAAAAAAAACBtb8KPKrAAAAAH/2Q==",
"debit_card_number": 0,
"favorite_music": [],
"groups": [1],
"user_permissions": [],
"interests": ["Ski"]
}
It always returns TypeError:
user.interests.add(validated_data['interests'])
Exception Type: TypeError at /users/
Exception Value: unhashable type: 'list'
I have tried different ways of implementing "interests" and "groups" in the serializer create function. I tried parsing validated data, selecting the child from Config_table and adding it here, but none of them work.
I actually have the same problem with "groups" field as well. It seems that Django cannot unhash the validated data in serializer and there it throws error.
The interesting point is, if I don't fill in the "group" and "interests" field when calling the POST method, it works fine, and later, I can update those fields by calling PUT with no problem.
What can I do with this?
You should pass to interests.add method different interests objects. Not list of names. So first you need get objects by name and then pass unpacking list of objects to add method using * syntax:
intersts = []
for name in validated_data['interests']:
obj, created = Config_Table.objects.get_or_create(entity_name=name)
interests.append(obj)
user.interests.add(*interests)
OK, I resolved the issue like below :
I changed Config_table model and added default django id field (technically, just removed primary_key from entity_name row, so django added the id AutoField by default). Re-populated the database and changed the code in serializers.py like below..using the answer of #neverwalkaloner :
user = UserProfile(
...
)
user.set_password(hashed_password)
user.save()
interests = []
for id in validated_data['interests']:
interests.append(id)
user.interests.add(*interests)
for name in validated_data['groups']:
user.groups.add(name)
user.save()
return user
Note that I had to save the entity before I wanted to work with a ManyToManyField. As django expects to see something in the database prior to adding some entities for ManyToMany fields. Keep that note in your code.
With this workaround, my problem has been resolved. But it is still strange to me why I could not use a primary key rather than the default. If anyone has an answer to this question, I'm more than willing to hear.

Django Rest Framework, some fields are absent when nesting serializers

The problem: when I pass someModelSerializer.data as a nested data for anotherModelSerializer, and after that I pass anotherModelSerializer.data to Response, in the response I see only two fields of SomeModel instead of 5. But when I pass someModelSerializer.data directly to Response, I can see that all model fields are present.
Details below.
I have TranslationHistory model
class TranslationHistory(models.Model):
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
string = models.CharField(max_length=255)
count = models.IntegerField(default=1)
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='translation_history')
and it's TranslationHistorySerializer
class TranslationHistorySerializer(serializers.ModelSerializer):
class Meta:
model = TranslationHistory
user = serializers.PrimaryKeyRelatedField(read_only=True, default=None)
count = serializers.IntegerField(read_only=True)
def validate_user(self, value):
return self.context['request'].user
def update(self, instance, validated_data):
instance.count += 1
instance.save()
return instance
I also have a virtual entity Translation, which is not a model. It has it's own serializer.
class TranslationSerializer(serializers.Serializer):
translation_history = TranslationHistorySerializer() # nested serializer
translation = serializers.CharField()
In my view, if I do like this,
history = TranslationHistory().findByUserAndString(request.user, string)
historySerializer = TranslationHistorySerializer(history)
return Response(historySerializer.data, status=status.HTTP_200_OK)
I have the response like this.
{
"id": 18,
"user": 1,
"count": 72,
"created": "2015-07-15T15:35:50.751219Z",
"updated": "2015-07-24T15:37:04.247469Z",
"string": "hello"
}
But if in my view I do like this,
history = TranslationHistory().findByUserAndString(request.user, string)
historySerializer = TranslationHistorySerializer(history)
serializer = TranslationSerializer(
data={
'translation_history': historySerializer.data,
'translation': 'hello'
},
context={'request': request}
)
serializer.is_valid(raise_exception=True)
return Response(serializer.data, status=status.HTTP_200_OK)
I have the response like this.
{
"translation_history": {
"user": 1,
"string": "hello"
},
"translation": "hello"
}
Why am I getting only user and string fields in this case?

Categories