Django - Serializer not setting ManyToManyField - python

For some reason the following code isn't setting the hash_tags attribute under Post. The way I checked was I put a breakpoint at the return Response line in view.py and I checked the newly created Post object and the hash_tags attribute just returned an empty list. Also when I read the serializer.data, hash_tags is an empty list as well. Even though the HashTag table clearly created the hash tag found in the body. What's going on?
model.py
class Post(AbstractBaseModel):
creator = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="post_creator")
join_goal = models.ForeignKey(JoinGoal, on_delete=models.CASCADE)
body = models.CharField(max_length=511, validators=[MinLengthValidator(5)])
hash_tags = models.ManyToManyField(HashTag)
type = models.CharField(
choices=PostType.choices,
max_length=50,
)
class HashTag(models.Model):
hash_tag = models.CharField(max_length=140, primary_key=True, validators=[
MinLengthValidator(1)])
Serializer.py
class HashTagSerializer(serializers.ModelSerializer):
class Meta:
model = HashTag
fields = ['hash_tag']
class PostSerializer(serializers.ModelSerializer):
hash_tags = HashTagSerializer(many=True, read_only=True)
class Meta:
model = Post
fields = ('creator', 'join_goal', 'body', 'uuid', 'created', 'type', 'updated_at', 'hash_tags')
view.py
#api_view(['POST'])
def post_create_update_post(request):
user_uuid = str(request.user.uuid)
request.data['creator'] = user_uuid
request.data['type'] = PostType.UPDATE
post_text = request.data['body']
hash_tags_list = extract_hashtags(post_text)
hash_tags = [HashTag.objects.get_or_create(hash_tag=ht)[0].hash_tag for ht in hash_tags_list]
request.data['hash_tags'] = hash_tags
try:
with transaction.atomic():
serializer = PostSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
post_obj = serializer.save()
except Exception as e:
return Response(dict(error=str(e),
user_message=error_message_generic),
status=status.HTTP_400_BAD_REQUEST)
return Response(serializer.data, status=status.HTTP_201_CREATED)
I also tried not setting read_only=True and get this error
this leads to ValidationError({'hash_tags': [ErrorDetail(string='This field is required.', code='required')]}) with request.data['hash_tags']=['Test']

You set read_only=True This is why hash_tags value not saving on the database.
hash_tags = HashTagSerializer(many=True, read_only=True)
Declare it without read_only like this:
hash_tags = HashTagSerializer(many=True)

I believe that you have to override the create and update method for the nested serializer in serializer.py:
class HashTagSerializer(serializers.ModelSerializer):
class Meta:
model = HashTag
fields = ['hash_tag']
class PostSerializer(serializers.ModelSerializer):
hash_tags = HashTagSerializer(many=True, read_only=True)
class Meta:
model = Post
fields = ('creator', 'join_goal', 'body', 'uuid', 'created', 'type', 'updated_at', 'hash_tags')
def create(self, validated_data):
hash_tags_data = validated_data.pop('hash_tags')
post = Post.objects.create(**validated_data)
for data in hash_tags_data:
hash_tag = HashTag.objects.get_or_create(**data)
post.hash_tags.add(hash_tag)
post.save()
return post

Related

serialize self-referential foreign key

Lets say I want to create an article that links via slug to one or multiple other articles (for now one).
A post request comes in giving me some article-info and the related slug.
I want to
validate the data and the related slug
create an article in the db
return the article (with the slug)
models.py
class Article(models.Model):
heading = models.CharField(max_length=2550)
slug = models.SlugField(null=True, default=None, unique=True, max_length=255)
article_previous = models.ForeignKey('self', to_field='slug', blank=True, null=True,
related_name='subcategories', on_delete=models.DO_NOTHING)
serializers.py
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = (
"heading",
"slug",
"article_previous",
def create(self, validated_data):
try:
article_previous = Article.objects.get(slug=d['value'])
article_previous_slug = article_previous.slug
except Article.DoesNotExist:
raise serializers.ValidationError(f'No Article with the slug {d["value"]}')
article=Article.objects.create(**validated_data,article_previous_id=article_previous_slug)
return validated_data
This solution gives:
article_previous = serializers.CharField()
ValueError: Cannot assign "'test_1'": "Article.article_previous" must be a "Article" instance.
This solution gives:
article_previous = serializers.CharField('article.article_previous')
TypeError: Article() got an unexpected keyword argument 'article'
If I use serializer.validated_data in views.py:
return Response(serializer.validated_data, status=status.HTTP_201_CREATED, headers=headers)
response = serializer.save()
return Response(response, status=status.HTTP_201_CREATED, headers=headers)
TypeError: Object of type Article is not JSON serializable.
You can serialize the slug by working with the article_previous_id field:
class ArticleSerializer(serializers.ModelSerializer):
article_previous = serializers.CharField(source='article_previous_id')
class Meta:
model = Article
fields = ('heading', 'slug', 'article_previous')
# …
class ArticleSerializer(serializers.ModelSerializer):
article_previous=serializers.SlugRelatedField(queryset=Article.objects.all(), slug_field="slug")
class Meta:
model = Article
fields = ('heading', 'slug', 'article_previous')
def create(self, validated_data):
article_previous=validated_data.pop("article_previous")
article=Article.objects.create(**validated_data, article_previous=article_previous)
validated_data.update( {"article_previous" : article.article_previous.slug} )
return validated_data
I think this solution has the advantage that the naming stays article_previous and is not changed to article_previous_id.

Rest Framework cant save serializer with foreign key

I have next serializers:
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = '__all__'
class PostSerializer(serializers.ModelSerializer):
category = CategorySerializer()
class Meta:
model = Post
fields = ['id', 'title', 'text', 'date', 'category']
And here is my view:
#api_view(['POST'])
def create_post(request):
serializer = PostSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
else:
return Response(serializer.errors)
return Response(serializer.data)
I want to create new Post object, but when I pass an category id at form it does not work, it is not saving my object. I tried to replace create method at my PostSerializer, to this:
def create(self, validated_data):
category_id = validated_data.pop('category')
post = Post.objects.create(**validated_data, category=category_id)
return post
but this dont work. Using postman formdata it is saying, that category field is required despite I filled it.
Here is my models:
class Category(models.Model):
name = models.CharField(max_length=512)
def __str__(self):
return self.name
class Post(models.Model):
title = models.CharField(max_length=512)
text = models.TextField()
date = models.DateTimeField(auto_now_add=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='category')
You need a category object not just an id, so try this
#api_view(['POST'])
def create_post(request):
category_id = request.data['category'] # or however you are sending the id
serializer = PostSerializer(data=request.data)
if serializer.is_valid():
category = Category.objects.get(id=category_id)
serializer.save(category=category)
else:
return Response(serializer.errors)
return Response(serializer.data)
or you can do something similar in the create method of the serializer

how can i add a new key value to my request.data in django

I would like to know how can I add a new value to my request.data
this is my request.data, structure:
[{'fname': 'john', 'lname': 'Doe'}]
how can I add the age key for example
This is my whole code :
def create(self, request):
formSerializer = self.serializer_class(data = request.data)
#### look Down
request.data['master'] = self.request.user.id
#######
if formSerializer.is_valid():
formSerializer.save()
objectSerializer = TeamSerializer(Team.objects.all(), many=True)
return Response(objectSerializer.data, status =
status.HTTP_201_CREATED)
return Response(formSerializer.errors, status.HTTP_400_BAD_REQUEST)
this is my model
from django.db import models
from users.models import CustomUser
# Create your models here.
class Team(models.Model):
master = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
name = models.CharField(null=False, blank=False, max_length=255)
comment = models.CharField(null=True, blank=True, max_length=255)
i have two input fields ( name + comment ) so i need master field that's why
Here you have to not need to add mater in request.data.
Try this:
def create(self, request):
formSerializer = self.serializer_class(data = request.data)
# request.data['master'] = self.request.user.id
if formSerializer.is_valid():
formSerializer.save(master = self.request.user)
objectSerializer = TeamSerializer(Team.objects.all(), many=True)
return Response(objectSerializer.data, status =
status.HTTP_201_CREATED)
return Response(formSerializer.errors, status.HTTP_400_BAD_REQUEST)
Remove master from serializer's fields
and add this in serializer:
class MYSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='master.username',read_only=True)
class Meta:
model = MY_MODEL
fields = (OTHER_FIELDs,'username')
Source Mean??

Related field id in POST request in Django Rest Framework

I am creating the object with just only related field id. I have searched a lot but couldn't get the answer. Here is my code
models.py:
class Resturant(models.Model):
name = models.CharField(_('name'), max_length=100)
class Menu(models.Model):
resturant_id = models.OneToOneField(Resturant, related_name='resturant', on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now=True)
serializers.py:
class MenuSerializer(serializers.ModelSerializer):
resturant_id = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Menu
fields = ['id', 'created_at', 'resturant_id']
views.py:
class CreateMenuAPIView(APIView):
def post(self, request, *args, **kwargs):
serializer = MenuSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(status=status.HTTP_400_BAD_REQUEST)
I am getting this error while sending { "resturant_id": 2 } in POST request.
DETAIL: Failing row contains (14, 2018-04-02 09:36:43.261849+00, null).
The above exception (null value in column "resturant_id" violates not-null constraint
Any help would be appreciated !
you can override method create for find Restaurant object or create if not exist. and only edit serializer.
serializer.py
class MenuSerializer(serializers.ModelSerializer):
resturant_id = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Menu
fields = ['id', 'created_at', 'resturant_id']
def create(self, validated_data):
id_param = validated_data.pop('resturant_id')
resturant = Resturant.objects.get_or_create(id=id_param)[0]
menu = Menu.objtects.create(resturant_id=resturant.id)
return menu
if not work you can delete this line:
resturant_id = serializers.PrimaryKeyRelatedField(read_only=True)
returant_id = serializers.PrimaryKeyRelatedField(read_only=True)
Could you try giving read_only=False
Could you check the spelling,
returant_id is used in serializers field,'s' is missing.
'resturant_id' is used in fields list
You are using a model serializer and have overridden 'returant_id'.
class MenuSerializer(serializers.ModelSerializer):
returant_id = serializers.PrimaryKeyRelatedField(queryset=Resturant.objects.all())
try to change the serializer to
class MenuSerializer(serializers.ModelSerializer):
resturant_id = serializers.PrimaryKeyRelatedField()
class Meta:
model = Menu
fields = ['id', 'created_at', 'resturant_id']
if read_only=True then it will not write into database.

Django REST - Create object with foreign key using serializers

I'm a little new to Django and Django-REST so please bear with me. Perhaps the answer is in the documentation, so if I missed it, apologies in advance.
Goal: I would like to create an EquipmentInfo object whose attributes include pre-existing foreign keys (EquipmentType and EquipmentManufacturer).
models.py
class EquipmentType(models.Model):
equipment_type = models.CharField(verbose_name="Equipment Type", max_length=50, unique=True)
def __unicode__(self):
return self.equipment_type
class EquipmentManufacturer(models.Model):
manufacturer_name = models.CharField(verbose_name="Manufacturer Name", max_length=50, unique=True)
def __unicode__(self):
return self.manufacturer_name
class EquipmentInfo(models.Model):
equipment_type = models.ForeignKey(EquipmentType, verbose_name="Equipment Type")
part_identifier = models.CharField(verbose_name="Machine ID (alias)", max_length=25)
manufacturer_name = models.ForeignKey(EquipmentManufacturer, verbose_name="Manufacturer Name")
serial_number = models.CharField(verbose_name="Serial Number", max_length=25)
date_of_manufacture = models.DateField(verbose_name="Date of Manufacture", default=date.today)
is_active = models.BooleanField(verbose_name="Is Active", default=True)
def __unicode__(self):
return self.part_identifier
serializers.py
class EquipmentTypeSerializer(serializers.ModelSerializer):
class Meta:
model = EquipmentType
fields = ('id', 'equipment_type',)
class EquipmentManufacturerSerializer(serializers.ModelSerializer):
class Meta:
model = EquipmentManufacturer
fields = ('id', 'manufacturer_name',)
class EquipmentInfoSerializer(serializers.ModelSerializer):
class Meta:
model = EquipmentInfo
fields = ('id', 'equipment_type', 'part_identifier', 'manufacturer_name','serial_number', 'date_of_manufacture', 'is_active')
equipment_type = EquipmentTypeSerializer(many=False)
manufacturer_name = EquipmentManufacturerSerializer(many=False)
def create(self, validated_data):
equipment_type = validated_data.pop('equipment_type')
manufacturer_name = validated_data.pop('manufacturer_name')
equipment_info = EquipmentInfo.objects.create(**validated_data)
return equipment_info
Assuming I already have relevant EquipmentType and EquipmentManufacturer objects created, I would like to add another EquipmentInfo object. What is the appropriate way to set up my EquipmentInfo serializer so that I can pass in information such as
{
"equipment_type":{
"equipment_type":"already_created",
},
"part_identifier":"something_new",
"manufacturer_name":{
"manufacturer_name":"already_created"
},
"serial_number":"WBA1",
"date_of_manufacture": "1900-01-01",
"is_active":true
}
or even better:
{
"equipment_type":"already_created",
"part_identifier":"something_new",
"manufacturer_name":"already_created",
"serial_number":"WBA1",
"date_of_manufacture": "1900-01-01",
"is_active":true
}
Any help is appreciated.
I have also faced the problem ,and have solved it ,the following is my step ,hope it will be helpful
1.company Model and contact model as follows:
class Company(models.Model):
Company_Name = models.CharField(u'Company Name',max_length=255, default="")
Modified_By = models.CharField(u'Modified By',max_length=255, default="")
class Company_Contact(models.Model):
Company = models.ForeignKey(Company)
Last_Name = models.CharField(u'Last Name',max_length=255, default="")
First_Name = models.CharField(u'First Name',max_length=255, default="")
2.I create A New Serializer Named CompanyReferenceSerializer,and company_contact
class CompanyReferenceSerializer(serializers.ModelSerializer):
class Meta:
model = Company
fields = ['id', 'Company_Name', 'Company_Name_SC']
class CompanyContactSerializer(serializers.ModelSerializer):
Company = CompanyReferenceSerializer()
class Meta:
model = Company_Contact
fields = ['Company', 'Last_Name','First_Name']
extra_kwargs = {
'Company': {'allow_null': True, 'required': False},
'Last_Name': {'allow_null': True, 'allow_blank': True, 'required': False},
'First_Name': {'allow_null': True, 'required': False, 'allow_blank': True},
}
3.Viewset as follows,in the backend,I get the object Namedcompany_instanceaccording to the 'company_id'
class CompanyContactViewSet(viewsets.ModelViewSet):
serializer_class = CompanyContactSerializer
def create(self, validated_data):
serializer = self.get_serializer(data=self.request.data)
company_id_for_contact = self.request.data.pop('Company_id')
company_instance = Company.objects.filter(id=company_id_for_contact).first()
if not serializer.is_valid():
print serializer.errors
data = serializer.validated_data
serializer.save(Company=company_instance)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
and I success insert one record in the company_contact ,Hope it can help you !
Using nested serializers makes it really hard for posts (if it even works, as it didn't used to work), and given your simple models, I would recommend just removing them.
I will recommend you add APIs for
/api/v1/type
/api/v1/manufacturer
/api/v1/info
(or whatever names you want to use). The type and manufacturer ones should be vanilla views and using your existing serializers.
For info, remove the two nested serializers:
class EquipmentInfoSerializer(serializers.ModelSerializer):
class Meta:
model = EquipmentInfo
fields = ('id', 'equipment_type', 'part_identifier', 'manufacturer_name','serial_number', 'date_of_manufacture', 'is_active')
After that, you should be able to do post using:
data = {
"equipment_type": 5, # The ID of the equipment type record
"part_identifier":"something_new",
"manufacturer_name": 10 # The ID of the manufacturer record
"serial_number":"WBA1",
"date_of_manufacture": "1900-01-01",
"is_active":true
}
In my case, I do like making it the GETs more convenient so I add read-only fields to return a name (or even the whole serialized record):
class EquipmentInfoSerializer(serializers.ModelSerializer):
type_name = serializers.SerializerMethodField(read_only=True)
class Meta:
model = EquipmentInfo
fields = ('id', 'equipment_type', 'part_identifier', 'manufacturer_name','serial_number', 'date_of_manufacture', 'is_active')
def get_type_name(self, obj):
return obj.equipment_type.equipment_type
Hope this helps.

Categories

Resources