I am working with django viewsets and i want an extra field from the request for validation.
What i want is the extra field role_id which should be coming from the request. The role id if passed, i should convert the employee to user and assign the role_id to the user.
Even though i am sending the role_id from request but i am unable to get it in the create or update method. Can someone please help me to know how can i fetch extra field from response
serializer.py
class EmployeeModelSerializer(serializers.ModelSerializer):
"""Employee model serializer"""
address = EmployeeAddressModelSerializer(required=False)
detail = EmployeeDetailModelSerializer(required=False)
demographic = EmployeeDemographicModelSerializer(required=False)
contact = EmployeeContactModelSerializer(required=False)
nok = EmployeeNokModelSerializer(required=False)
visa = EmployeeVisaModelSerializer(required=False)
dbs = EmployeeDBSModelSerializer(required=False)
job_detail = EmployeeJobDetailModelSerializer(required=False)
preference = EmployeePreferenceModelSerializer(required=False)
skills = EmployeeSkillsModelSerializer(many=True, read_only=True)
user = UserModelSerializer(required=False)
serializersMap = {
"address": EmployeeAddressModelSerializer,
"detail": EmployeeDetailModelSerializer,
"demographic": EmployeeDemographicModelSerializer,
"contact": EmployeeContactModelSerializer,
"nok": EmployeeNokModelSerializer,
"visa": EmployeeVisaModelSerializer,
"dbs": EmployeeDBSModelSerializer,
"job_detail": EmployeeJobDetailModelSerializer,
"preference": EmployeePreferenceModelSerializer,
"user": UserModelSerializer,
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.context["gender"] = None
self.context["title"] = None
self.context["marital_status"] = None
self.context["employee_status"] = None
def internal_validate(self, data, field):
self.context[field] = data
if data == None:
return data
elif data.id:
return data.id
return data
def validate_gender(self, data):
return self.internal_validate(data, "gender")
def validate_title(self, data):
return self.internal_validate(data, "title")
def validate_marital_status(self, data):
return self.internal_validate(data, "marital_status")
def validate_employee_status(self, data):
return self.internal_validate(data, "employee_status")
user = serializers.SlugRelatedField(
read_only=True,
slug_field='username'
)
class Meta:
"""Meta class."""
model = Employee
fields = '__all__'
def get_fullname(self, obj):
return obj.first_name + " " + obj.last_name
def create(self, data):
data["address"] = self.saveForeign(data, "address")
data["detail"] = self.saveForeign(data, "detail")
data["demographic"] = self.saveForeign(data, "demographic")
data["contact"] = self.saveForeign(data, "contact")
data["nok"] = self.saveForeign(data, "nok")
data["visa"] = self.saveForeign(data, "visa")
data["dbs"] = self.saveForeign(data, "dbs")
data["job_detail"] = self.saveForeign(data, "job_detail")
data["preference"] = self.saveForeign(data, "preference")
data.pop("skills", None)
self.setContext(data)
employee = Employee.objects.create(**data)
return employee
def update(self, instance, data):
instance.address = self.updateForeign(instance.address, data, "address")
instance.detail = self.updateForeign(instance.detail, data, "detail")
instance.demographic = self.updateForeign(instance.demographic, data, "demographic")
instance.contact = self.updateForeign(instance.contact, data, "contact")
instance.nok = self.updateForeign(instance.nok, data, "nok")
instance.visa = self.updateForeign(instance.visa, data, "visa")
instance.dbs = self.updateForeign(instance.dbs, data, "dbs")
instance.job_detail = self.updateForeign(instance.job_detail, data, "job_detail")
instance.preference = self.updateForeign(instance.preference, data, "preference")
self.setContext(data)
return super(EmployeeModelSerializer, self).update(instance, data)
def saveForeign(self, data, name):
field = data.get(name)
data.pop(name, None)
if field is None:
return None
serializer = self.serializersMap.get(name)(data=field)
if serializer.is_valid(raise_exception=True):
return serializer.save()
def updateForeign(self, instance, data, name):
field = data.get(name)
data.pop(name, None)
if field is None:
return None
if not instance:
serializer = self.serializersMap.get(name)(data=field, context={})
if serializer.is_valid(raise_exception=True):
return serializer.save()
else:
serializer = self.serializersMap.get(name)(instance, data=field, context={})
if serializer.is_valid(raise_exception=True):
return serializer.update(instance, field)
def setContext(self, data):
if self.context["gender"] != None:
data["gender"] = self.context['gender']
if self.context["title"] != None:
data["title"] = self.context['title']
if self.context["marital_status"] != None:
data["marital_status"] = self.context['marital_status']
if self.context["employee_status"] != None:
data["employee_status"] = self.context['employee_status']
views.py
"""Employee view set"""
# authentication_classes = (TokenAuthentication,)
permission_classes = (IsAuthenticated,)
queryset = Employee.objects.filter(is_active=True).order_by('-modified')
serializer_class = EmployeeModelSerializer
filter_backends = (SearchFilter, OrderingFilter, DjangoFilterBackend)
filter_fields = [
"job_detail__employment_type_id",
"job_detail__employee_category_id",
"job_detail__job_title_id",
"skills__name__id"
]
ordering_fields = [
'modified',
'job_detail__branch__name',
'full_name',
'reference',
'employee_status__name',
'address__name'
]
search_fields = [
'full_name',
'last_name',
'address__zip_code',
'contact__mobile_number'
]
def destroy(self, request, pk=None):
employee = self.get_object()
employee.is_active = False
employee.save()
return Response({'message': 'deleted!'}, status=status.HTTP_200_OK)
models.py
class Employee(Person):
user = models.ForeignKey(
'users.User',
null=True,
on_delete=models.SET_NULL
)
employee_status = models.ForeignKey(
'core.Employee_Status',
null=True,
blank=True,
on_delete=models.SET_NULL
)
title = models.ForeignKey(
'core.Title',
null=True,
blank=True,
on_delete=models.SET_NULL
)
payroll_ref = models.CharField(
'Payroll -Reference',
max_length=25,
blank=True,
default=''
)
address = models.OneToOneField(
'employees.Employee_Address',
null=True,
on_delete=models.SET_NULL
)
contact = models.OneToOneField(
'employees.Employee_Contact',
null=True,
on_delete=models.SET_NULL
)
dbs = models.OneToOneField(
'employees.Employee_dbs',
null=True,
on_delete=models.SET_NULL
)
demographic = models.OneToOneField(
'employees.Employee_Demographic',
null=True,
on_delete=models.SET_NULL
)
detail = models.OneToOneField(
'employees.Employee_Detail',
null=True,
on_delete=models.SET_NULL
)
job_detail = models.OneToOneField(
'employees.Employee_Job_Detail',
null=True,
on_delete=models.SET_NULL
)
nok = models.OneToOneField(
'employees.Employee_NOK',
null=True,
on_delete=models.SET_NULL
)
preference = models.OneToOneField(
'employees.Employee_Preference',
null=True,
on_delete=models.SET_NULL
)
visa = models.OneToOneField(
'employees.Employee_Visa',
null=True,
on_delete=models.SET_NULL
)
skills = models.ManyToManyField(
'employees.Employee_Skill',
related_name='Employee',
)
class Meta:
verbose_name = 'Employee - Basic Info'
verbose_name_plural = 'Employees - Basic Info'
request parameter
{
"first_name": "first name",
"last_name": "last name",
"date_of_birth": "2017-02-12",
"is_active": true,
"role_id": "role_id_goes_here",
"address": {
"name": "address name",
"line_1": "line 1",
"city": "city",
"state": "state",
"country": "country",
"zip_code": "zip code"
},
"nok": {
"no": "123sad",
"full_name": "full name nok",
"mobile": 123456789,
"email": "jesus#iconos.mx",
"title": null
},
"title": null
}
Related
I am trying to work with a post request were I am first saving a Tag object, which then becomes the tag field of a Tagging object. However, no matter what combinations I have tried, despite the Tag Post method working, when I pass a json object like this:
{
"user_id": 1,
"gameround_id": 2015594866,
"resource_id": 2975,
"tag": {
"name": "TESTTAGGGG2222",
"language": "en"
},
"score": 0,
"origin": ""
}
I keep getting this message:
{
"name": [
"This field is required."
],
"language": [
"This field is required."
]
}
However, when I pass a Tag, it works.
This is the post method:
def post(self, request, *args, **kwargs):
if not isinstance(request.user, CustomUser):
current_user_id = 1
else:
current_user_id = request.user.pk
gameround = request.data.get('gameround', '')
random_resource = request.data.get('resource', '')
created = datetime.now()
score = 0
origin = ''
name = request.data.get('name', '')
language = request.data.get('language', '')
tag_serializer = TagSerializer(data=request.data)
tagging_serializer = TaggingSerializer(data=request.data)
if tag_serializer.is_valid(raise_exception=True):
tag_serializer.save(tag=request.data)
if tagging_serializer.is_valid(raise_exception=True):
tagging_serializer.save(tagging=request.data, tag=tag_serializer.data)
return Response({"status": "success", "data": tagging_serializer.data},
status=status.HTTP_201_CREATED)
# else:
# return Response({"status": "success", "data": tag_serializer.data},status=status.HTTP_201_CREATED)
else:
return Response({"status": "error", "data": tag_serializer.errors},
status=status.HTTP_400_BAD_REQUEST)
How do I correctly pass the nested object in the post method so that I don't get this error anymore?
models.py
class Tag(models.Model):
name = models.CharField(max_length=256)
language = models.CharField(max_length=256)
objects = models.Manager()
def __str__(self):
return self.name or ''
#property
def tags(self):
tags = self.tagging.values('tag')
return tags.values('tag_id', 'tag__name', 'tag__language')
class Tagging(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, null=True)
gameround = models.ForeignKey(Gameround, on_delete=models.CASCADE, related_name='taggings')
resource = models.ForeignKey(Resource, on_delete=models.CASCADE, related_name='taggings')
tag = models.ForeignKey(Tag, on_delete=models.CASCADE, related_name='tagging')
created = models.DateTimeField(editable=False, null=True)
score = models.PositiveIntegerField(default=0)
# media_type = models.ForeignKey(Gamemode, on_delete=models.CASCADE)
origin = models.URLField(max_length=256, blank=True, default='')
objects = models.Manager()
def __str__(self):
return str(self.tag) or ''
serializers.py
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ('name', 'language')
def create(self, validated_data):
tag_data = validated_data.pop('tag')
Tag.objects.create(**tag_data)
return tag_data
def to_representation(self, data):
data = super().to_representation(data)
return data
class TaggingSerializer(serializers.ModelSerializer):
tag = TagSerializer(required=False, write_only=False)
resource_id = serializers.PrimaryKeyRelatedField(queryset=Resource.objects.all(),
required=True,
source='resource',
write_only=False)
gameround_id = serializers.PrimaryKeyRelatedField(queryset=Gameround.objects.all(),
required=False,
source='gameround',
write_only=False)
user_id = serializers.PrimaryKeyRelatedField(queryset=CustomUser.objects.all(),
required=False,
source='user',
write_only=False)
class Meta:
model = Tagging
fields = ('id', 'user_id', 'gameround_id', 'resource_id', 'tag', 'created', 'score', 'origin')
depth = 1
def create(self, validated_data):
"""Create and return a new tagging"""
tag_data = validated_data.pop('tag', None)
if tag_data:
tag = Tag.objects.get_or_create(**tag_data)[0]
validated_data['tag'] = tag
tagging = Tagging(
user=validated_data.get("user"),
gameround=validated_data.get("gameround"),
resource=validated_data.get("resource"),
tag=validated_data.get("tag"),
created=datetime.now(),
score=validated_data.get("score"),
origin=validated_data.get("origin")
)
tagging.save()
return tagging
def to_representation(self, instance):
rep = super().to_representation(instance)
rep['tag'] = TagSerializer(instance.tag).data
return rep
In your post method, you made
tag_serializer = TagSerializer(data=request.data)
if tag_serializer.is_valid(raise_exception=True):
# the rest of the code
this means that your data will pass to TagSerializer so DRF will check if the fields for this serializer are in the request body or not, and it's not provided, cause this data does not belong to this serializer, it belongs to TaggingSerializer so this will give you an error This field is required
So, you need to send your request data only to TaggingSerializer, try that and let's see what will happen, and I will suggest using serializers.Serializer instead of using serializers.ModelSerializer for better performance
I'm having an issue in DRF tests when creating instance of a model, the status code in response is 'HTTP_201_CREATED' but the instance it self does not exist in the testing db.
here is my model :
class Item(SafeDeleteModel):
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name=_("Owner"))
name = models.CharField(_("Name"), max_length=150)
category = TreeForeignKey('merssis.ItemCategory', on_delete=models.SET_NULL, null=True, verbose_name=_("Category"))
fixed_price = models.FloatField(_("Fixed price"), default=0)
main_pic = ProcessedImageField(verbose_name=_("Main picture"), upload_to='item_pics', processors=[ItemWatermarker()],format='JPEG')
main_pic_thumbnail = ImageSpecField(source='main_pic',
processors=[ResizeToFill(384, 256)],
format='JPEG',
options={'quality': 100})
geo_location = models.PointField(srid=4326, null=True, blank=True, verbose_name=_("Geolocation"))
_safedelete_policy = SOFT_DELETE_CASCADE
def __str__(self):
return self.name
Serializer :
class ItemCreateSerializer(GeoFeatureModelSerializer):
PRICE_TYPE_CHOICES = (
('fixed', _('Fixed') ),
('open', _('Open') ),
)
owner = serializers.HiddenField(default=serializers.CurrentUserDefault())
price_type = serializers.ChoiceField(choices=PRICE_TYPE_CHOICES)
category = serializers.PrimaryKeyRelatedField(queryset=ItemCategory.objects.all(), many=False)#ItemCategorySerializer(many=False)
main_pic = serializers.ImageField(use_url='item_pics')
def validate(self, data):
user = self.context['request'].user
geo_data = data.get('geo_location')
#Validate fixed price value
if data['price_type'] == 'fixed':
if data.get('fixed_price') == None or int(data.get('fixed_price')) <= 0:
raise serializers.ValidationError({"fixed_price" :INVALIDE_PRICE_ERROR})
#Price type is open should explicitly set fixed price to 0
if data['price_type'] == 'open':
data['fixed_price'] = 0
#Validate geo_location
#geo_location post data form ====> {"type":"Point", "coordinates":[37.0625,-95.677068]}
if geo_data:
if not validate_in_country_location(user, geo_data):
raise serializers.ValidationError({"geo_location":OUTSIDE_COUNTRY_MSG})
return data
def create(self, validated_data):
#Remove price_type value since it is not a field in the model
#We used to determine th price type on the serializer only
validated_data.pop('price_type')
return Item(**validated_data)
class Meta:
model = Item
geo_field = 'geo_location'
fields = ( 'owner',
'name',
'price_type',
'category',
'fixed_price',
'main_pic',
'geo_location',
)
The view :
class ItemCreateAPIView(CreateAPIView):
queryset = Item.objects.all()
serializer_class = ItemCreateSerializer
permission_classes = [permissions.IsAuthenticated]
def perform_create(self, serializer, *args, **kwargs):
self.check_object_permissions(self.request, self.request.user)
serializer.save()
The test case :
class ItemTestCase(APITestCase):
def test_create_new_item(self):
"""
Testing Add new item functionality
"""
self.client_1 = APIClient()
self.user_1 = create_new_user(email='tester1#gmail.com', username='tester_1', password='qsdf654654', gender='male')
self.client_1.login(username='tester_1',password='qsdf654654')
image_file = create_test_image()
category = ItemCategory.objects.create(name='SomeCat')
new_item_data = {
'name': 'New Item',
'price_type' : 'open',
'category': str(category.pk),
'main_pic': image_file,
}
response = self.client_1.post(url, new_item_data, format='multipart')
items = Item.objects.filter(name='New Item')
print(response.status_code)
self.assertEqual( response.status_code, status.HTTP_201_CREATED)
self.assertEqual( items.count(), 1)
and when i run the test i get '201' printed in console AND AssertionError: 0 != 1
i'm fvkin confused
In the serializer create() the object was never saved so change:
return Item(**validated_data)
to:
return Item.objects.create(**validated_data) # create the object
I'm trying to create an API to upload multiple images per session, I'm using the Django REST Framework. This is the error that I get in Postman:
{
"images": {
"0": {
"non_field_errors": [
"Invalid data. Expected a dictionary, but got InMemoryUploadedFile."
]
}
}
}
models.py with 2 tables
class PostSession(models.Model):
class Meta:
verbose_name = 'session'
verbose_name_plural = 'sessions'
name = models.CharField(
max_length=255,
)
class PostImage(models.Model):
class Meta:
verbose_name = 'photo'
verbose_name_plural = 'photos'
name = models.ForeignKey(
to=PostSession,
on_delete=models.CASCADE,
null=True,
blank=True,
)
file = models.ImageField(
upload_to='photos',
)
serializers.py
class PostImageRetrieveSerializer(serializers.ModelSerializer):
class Meta:
model = PostImage
fields = '__all__'
class PostImageUpdateSerializer(serializers.Serializer):
"""
This class for validate and save child items purpose
"""
name = serializers.CharField(required=False, allow_null=True, allow_blank=True, )
file = serializers.ImageField(required=True, allow_null=False, allow_empty_file=False, )
def create(self, validated_data):
session_name = validated_data.get('name')
image_file = validated_data.get('file')
if session_name and isinstance(session_name, str):
session, _ = PostSession.objects.get_or_create(name=session_name, )
else:
session = None
instance = PostImage.objects.create(
name=session,
file=image_file,
)
return self.update(
instance=instance,
validated_data=validated_data,
)
def update(self, instance, validated_data):
instance.save()
return instance
class PostUploadSerializer(serializers.Serializer):
images = serializers.ListField(
child=PostImageUpdateSerializer(required=True, allow_null=False, many=False, ),
required=True,
allow_null=False,
allow_empty=False,
)
def validate(self, attrs):
images_list = attrs.get('images')
if not isinstance(images_list, list):
raise exceptions.ValidationError(detail={
'images': ['`images` field must be a list of dict object!', ],
})
return attrs
def save_many(self):
images_list = self.validated_data.get('images')
post_image_instances = []
for image_obj in images_list:
try:
post_image_serializer = PostImageSerializer(
context=self.context,
data=image_obj,
many=False,
)
post_image_serializer.is_valid(raise_exception=True, )
post_image = post_image_serializer.save()
post_image_instances.append(post_image)
except:
# TODO: Remove previous saved instances if needed (inside `post_image_instances`)?
pass
return post_image_instances
def create(self, validated_data):
pass
def update(self, instance, validated_data):
pass
viewsets.py
class PostViewSet(viewsets.GenericViewSet):
parser_classes = [parsers.MultiPartParser, parsers.JSONParser, ]
serializer_class = PostImageRetrieveSerializer
queryset = PostImage.objects.all()
#action(methods=['POST', ], detail=False, serializer_class=PostUploadSerializer, )
def upload_images(self, request, *args, **kwargs):
upload_serializer = PostUploadSerializer(
context={'request': request, },
data=request.data,
many=False,
)
upload_serializer.is_valid(raise_exception=True, )
post_image_instances = upload_serializer.save_many()
serializer = self.get_serializer(
post_image_instances,
many=True,
)
return response.Response(
data=serializer.data,
status=status.HTTP_200_OK,
)
The Idea is that the API can create a session with multiple images.
https://gist.github.com/cokelopez/a98ee5569991b6555ecd216764c193ec
I am trying to Update A Employee. I am able to update also. But the problem is coming a while updating when i am not sending data in body. It is giving error of Required Field.
This error is one of kind - where i was not sending excempt in json body
serializer2 {'excempt': [ErrorDetail(string='This field is required.', code='required')]}
Even i tried to put required=False and it is working but i am not understanding if we are passing instance of that EmployeeProfileSerializer(userprofile, data=request.data). Example userprofile but why it is still it is giving error. And how should i tackle it. I don't think this is the solution required=False?.
When i am not sending data in body then the previous data should be used but this is not happening instead it is giving error of required field
APIView
class EmployeeUpdateApiV2(APIView):
def post(self, request, *args, **kwrgs):
try:
accesstoken=AccessToken.objects.get(
token=self.request.META.get('HTTP_AUTHORIZATION').replace('Bearer ', '')
)
except ObjectDoesNotExist:
return Response (
{
"status" : False,
"error" : "Wrong Access Token",
"error_message":"You have provided wrong access token.",
}
)
user_id = request.data['user_id']
user = get_object_or_404(User, id=user_id)
print(user)
userprofile = get_object_or_404(UserProfile, user=user_id)
print(userprofile)
serializer1 = EmployeeRegisterSerializer(user, data=request.data)
serializer2 = EmployeeProfileSerializer(userprofile, data=request.data)
if serializer1.is_valid() and serializer2.is_valid():
serializer1.save()
serializer2.save()
print('Inside Valid')
return Response (
{
"status" : True,
"message":"Employee Updated Successfully.",
"api_name" : "EmployeeUpdateApiV2",
"result": serializer1.data,
"result1": serializer2.data,
}
)
print('Out Valid')
print('serializer1 ', serializer1.errors)
print('serializer2', serializer2.errors)
return Response(status=status.HTTP_404_NOT_FOUND)
Serializers.py
class EmployeeProfileSerializer(serializers.ModelSerializer):
employee_id = serializers.CharField(source='user_employee_id')
payroll_id = serializers.CharField(source='user_payroll_id')
hire_date = serializers.CharField(source='user_hire_date')
pay_rate = serializers.CharField(source='user_pay_rate')
salaried = serializers.CharField(source='user_salaried')
excempt = serializers.CharField(source='user_excempt')
groups = serializers.CharField(source='user_groups_id', required=False)
state = serializers.CharField(source='user_state')
city = serializers.CharField(source='user_city')
zipcode = serializers.CharField(source='user_zipcode')
status = serializers.CharField(source='user_status')
phone = serializers.CharField(source='user_phone')
class Meta:
model = UserProfile
fields = [
'employee_id',
'phone',
'payroll_id',
'hire_date',
'pay_rate',
'salaried',
'excempt',
'groups',
'state',
'city',
'zipcode',
'status',
]
class EmployeeRegisterSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['first_name','last_name', 'email',]
Userprofile Model
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
user_company = models.ForeignKey(Company, on_delete=models.CASCADE)
user_role = models.ForeignKey(ACLRoles, on_delete=models.CASCADE)
user_dob = models.DateField(null=True, blank=True)
user_phone = models.CharField(max_length=30, blank=True, null=True)
user_image = models.ImageField(upload_to='user_profiles/', default='default_user.png',blank=True, null=True)
user_created = models.DateTimeField(auto_now_add=True)
user_is_deleted = models.BooleanField(default=False)
user_deleted_at = models.DateTimeField(blank=True, null=True)
user_groups = models.ManyToManyField(Groups,related_name='user_groups')
MALE = 'Male'
FEMALE = 'Female'
GENDER_CHOICES = (
(MALE, 'Male'),
(FEMALE, 'Female'),
)
user_gender = models.CharField(
max_length=8,
choices=GENDER_CHOICES,
)
ACTIVE = 'Active'
INACTIVE = 'Inactive'
PENDING = 'Pending'
USER_ACTIVE_CHOICES = (
(ACTIVE, 'Active'),
(INACTIVE, 'Inactive'),
(PENDING, 'Pending'),
)
user_status = models.CharField(
max_length=8,
choices=USER_ACTIVE_CHOICES,
default=ACTIVE,
)
please add user in your Model Serializer field section
class EmployeeProfileSerializer(serializers.ModelSerializer):
employee_id = serializers.CharField(source='user_employee_id')
payroll_id = serializers.CharField(source='user_payroll_id')
hire_date = serializers.CharField(source='user_hire_date')
pay_rate = serializers.CharField(source='user_pay_rate')
salaried = serializers.CharField(source='user_salaried')
excempt = serializers.CharField(source='user_excempt')
groups = serializers.CharField(source='user_groups_id', required=False)
state = serializers.CharField(source='user_state')
city = serializers.CharField(source='user_city')
zipcode = serializers.CharField(source='user_zipcode')
status = serializers.CharField(source='user_status')
phone = serializers.CharField(source='user_phone')
class Meta:
model = UserProfile
fields = [
'employee_id',
'phone',
'payroll_id',
'hire_date',
'pay_rate',
'salaried',
'excempt',
'groups',
'state',
'city',
'zipcode',
'status',
'user',
]
DRF doc's say's i need to override serializers create and update methods to update and/or create nested data. But when i am trying to edit existing descriptions my code creates a new one. Is there a simple way to detect which descriptions was edit and update only it?
class TaskSerializer(ModelSerializer):
url = HyperlinkedIdentityField(
view_name='tasks:tasks-detail',
lookup_field='pk',
)
author = SlugField(source='author.username')
executor = SlugField(source='executor.username')
descriptions = DescriptionSerializer(many=True)
class Meta:
model = Task
fields = (
'pk',
'url',
'title',
'project',
'status',
'author',
'executor',
'descriptions'
)
def create(self, validated_data):
descriptions_data = validated_data.pop('descriptions', None)
author = validated_data.pop('author', None)
executor = validated_data.pop('executor', None)
try:
task_author = User.objects.get(username=author['username'])
task_executor = User.objects.get(username=executor['username'])
except User.DoesNotExist:
raise ValidationError(
_("Такого пользователя не сущетсвует!"))
task = Task.objects.create(author=task_author, executor=task_executor, **validated_data)
if descriptions_data:
for description in descriptions_data:
description, created = Description.objects.get_or_create(
text=description['text'],
task=task
)
task.descriptions.add(description)
return task
def update(self, instance, validated_data):
descriptions_data = validated_data.pop('descriptions', None)
instance.title = validated_data.get('title', instance.title)
instance.project = validated_data.get('project', instance.project)
instance.status = validated_data.get('status', instance.status)
author = validated_data.pop('author', None)
executor = validated_data.pop('executor', None)
try:
if author:
task_author = User.objects.get(username=author['username'])
instance.author = task_author
if executor:
task_executor = User.objects.get(username=executor['username'])
instance.executor = task_executor
except User.DoesNotExist:
raise ValidationError(
_("Такого пользователя не сущетсвует!"))
descriptions_list = []
if descriptions_data:
for description in descriptions_data:
description, created = Description.objects.get_or_create(
text=description["text"],
task=instance
)
descriptions_list.append(description)
instance.descriptions.set(descriptions_list)
instance.save()
return instance
class DescriptionSerializer(ModelSerializer):
class Meta:
model = Description
fields = (
'pk',
'text',
)
class Task(models.Model):
STATUS_CHOICES = (
(1, _("В процессе разработки")),
(2, _("В процессе тестирования")),
(3, _("На ревью")),
(4, _("Открыта")),
(5, _("Закрыта"))
)
title = models.CharField(_('Название'), max_length=60)
project = models.CharField(_('Название проекта'), max_length=60)
status = models.IntegerField(_('Статус'), choices=STATUS_CHOICES,
default=4)
author = models.ForeignKey(settings.AUTH_USER_MODEL,
max_length=60, on_delete=models.CASCADE,
related_name='author',
verbose_name=_('Автор'),
validators=[validate_user])
executor = models.ForeignKey(settings.AUTH_USER_MODEL,
max_length=60, on_delete=models.CASCADE,
related_name='executor',
verbose_name=_('Исполнитель'),
validators=[validate_user])
def __str__(self):
return self.title
def __unicode__(self):
return u'{}'.format(self.title)
class Meta:
verbose_name = _('Задача')
verbose_name_plural = _('Задачи')
class Description(models.Model):
task = models.ForeignKey(Task, on_delete=models.CASCADE,
related_name='descriptions')
text = models.TextField(_('Описание'))
def __str__(self):
return '#{}'.format(self.pk)
def __unicode__(self):
return u'#{}'.format(self.pk)
class Meta:
verbose_name = _('Описание')
verbose_name_plural = _('Описания')