Django REST Framework many to many create and update - python

models.py:
class Book(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(max_length=500)
image = models.ImageField(height_field="height_field", width_field="width_field")
height_field = models.IntegerField(default=255)
width_field = models.IntegerField(default=255)
price = models.FloatField()
edition = models.CharField(max_length=100)
no_of_page = models.IntegerField()
country = models.CharField(max_length=50)
publication = models.ForeignKey(Publication, on_delete=models.CASCADE)
authors = models.ManyToManyField(Author, through='AuthorBook')
ratings = GenericRelation(Rating, related_query_name='books')
class AuthorBook(models.Model):
author = models.ForeignKey(Author, on_delete=models.CASCADE)
book = models.ForeignKey(Book, on_delete=models.CASCADE)
class Author(models.Model):
name = models.CharField(max_length=100)
biography = models.TextField(max_length=500)
image = models.ImageField(height_field="height_field", width_field="width_field")
height_field = models.IntegerField(default=255)
width_field = models.IntegerField(default=255)
serializers.py
class AuthorListSerializer(ModelSerializer):
url = author_detail_url
class Meta:
model = Author
fields = [
'url',
'id',
'name',
]
class BookCreateUpdateSerializer(ModelSerializer):
authors = AuthorListSerializer(many=True, read_only=True)
def create(self, validated_data):
#code
def update(self, instance, validated_data):
#code
class Meta:
model = Book
fields = [
'name',
'description',
'price',
'edition',
'no_of_page',
'country',
'publication',
'authors',
]
views.py
class BookCreateAPIView(CreateAPIView):
queryset = Book.objects.all()
serializer_class = BookCreateUpdateSerializer
I am working for implementing Django REST Framework. I have two models Book and Author. But django create api view don't show authors field drop down. I checked many solution DRF for many to many field. Please help me to show authors field and write create() and update function. If you see DRF api page, it will be clear.

This is because of read_only=True. Try to remove this argument for bookserializer's authors field:
class BookCreateUpdateSerializer(ModelSerializer):
authors = AuthorListSerializer(many=True)

Related

Django Rest Framework - Filter with logical and

I'm using Django Rest Framework with DjangoFilterBackend to filter through Publications. Every publication can have multiple authors. My api call to filter the api for authors looks like:
/api/v1/publication/?author=1&author=2
This gives me every publication that either author 1 or author 2 has been assigned to. Instead I want to only see the publications that both have published. In other words it should be a logic and, not or.
My code is the following:
models.py
class Publication(models.Model):
id = models.BigAutoField(primary_key=True)
title = models.CharField(max_length=400)
author = models.ManyToManyField(Author, blank=False)
type = models.ForeignKey(
Type, on_delete=models.PROTECT, null=False, default=1)
label = models.ManyToManyField(Label, blank=True)
date = models.DateField(blank=False, null=False)
url = models.URLField(null=True, blank=True)
journal = models.CharField(max_length=400, blank=True)
bibtex = models.TextField(blank=True)
public = models.BooleanField(default=False)
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
file = models.FileField(upload_to=upload_path, blank=True, null=True)
class Meta:
ordering = ['-date']
def __str__(self):
return self.title
views.py
class PublicationFilter(django_filters.FilterSet):
author = django_filters.ModelMultipleChoiceFilter(
queryset=Author.objects.all())
class Meta:
model = Publication
fields = {
'title': ["exact"],
'author': ["exact"]
}
class PublicationView(viewsets.ModelViewSet):
queryset = Publication.objects.prefetch_related(
'author', 'label').select_related('type')
serializer_class = PublicationSerializer
filter_backends = [DjangoFilterBackend,
SearchFilter, OrderingFilter]
filterset_fields = ['title', 'date', 'journal', 'label', 'author']
search_fields = ['title']
ordering_fields = ['date', 'title']
serializers.py
class PublicationSerializer(ModelSerializer):
type = TypeSerializer(read_only=False, many=False)
label = LabelSerializer(read_only=False, many=True)
author = AuthorSerializer(read_only=False, many=True)
class Meta:
model = Publication
fields = ['id', 'title', 'date',
'url', 'journal', 'label', 'author', 'type', 'date', 'bibtex', 'file']
I think if you will pass only one author field in the url query, but separate the values with a comma you will end up with a list value in the django-filter.
/api/v1/publication/?author=1,2
Than you can override filter_author method in your PublicationFilter class and build the query filter manually there.
See here under method: https://django-filter.readthedocs.io/en/stable/ref/filters.html#method
You can build the query with a help of the Q object:
How to dynamically compose an OR query filter in Django?

DRF post to model with a many-to-many field

I have the following models:
class Tag(TimeStampModel):
name = models.CharField(unique=True, max_length=100)
slug = models.SlugField(max_length=100, unique=True, blank=True)
featured = models.BooleanField(default=False, blank=True)
class Deal(VoteModel, models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='deals',
on_delete=models.CASCADE)
title = models.CharField(max_length=1024, blank=False, null=False)
slug = models.SlugField(max_length=1024, unique=True, blank=True)
description = models.TextField(blank=False, null=False)
poster = models.ImageField(blank=True)
tags = models.ManyToManyField(
Tag, blank=True)
And the following serializers:
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ['id', 'name', 'slug', 'featured', 'created_at']
class DealSerializer(serializers.ModelSerializer):
user = UserSerializer(many=False, read_only=True)
tags = TagSerializer(many=True, read_only=True)
tags_ids = serializers.PrimaryKeyRelatedField(many=True, write_only=True, queryset=Tag.objects.all())
class Meta:
model = Deal
fields = '__all__'
Views
class DealList(viewsets.ModelViewSet, VoteMixin):
serializer_class = DealSerializer
permission_classes = [IsOwnerOrAdminOrReadOnly]
def get_queryset(self):
return Deal.objects.all()
def perform_create(self, serializer):
serializer.save(user=self.request.user)
I am able to get the data and also post it, but because of the many-to-many field (tags), I seem to have some issues as a Deal may have tags that only exist (created beforehand, and cannot be created through a post request to the Deal).
I send data as the following:
{
title: 'some title',
all_other_fields: 'some data',
tags_ids: [2, 4]
}
The tags are sent as an array of tag ids, but I get an error as the following:
"Incorrect type. Expected pk value, received str."
I only added the tags_ids so I could perform write operations on it as I couldn't figure out a way to use the field tags as both a read and write field that would return an object on read, and accept an id on write.
I have read through many posts here on Stackoverflow, but nothing that would work for me yet. Any help would be very appreciated. Thank you.
Try changing your serializer like this
class DealSerializer(serializers.ModelSerializer):
user = UserSerializer(many=False, read_only=True)
tags = serializers.PrimaryKeyRelatedField(many=True, queryset=Tag.objects.all())
class Meta:
model = Deal
fields = '__all__'
Try doing it this way.
class DealSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True)
tags = TagSerializer(read_only=True)
class Meta:
model = Deal
fields = ('user', 'title', 'slug', 'description', 'poster', 'tags')

Django DRF nested serializers are not linked

I'm trying to build an API for a picture gallery using Django and Django Rest Framework for the API.
I have set up the following two models:
class Album(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(null=True, blank=True)
start_date = models.DateField()
end_date = models.DateField(null=True, blank=True)
parent_album = models.ForeignKey('self',
null=True,
blank=True,
on_delete=models.CASCADE
)
class Picture(models.Model):
path = models.ImageField()
album = models.ForeignKey('Album', on_delete=models.CASCADE)
For the serializers, I have defined the following, using the official Django DRF doc:
class PictureSerializer(serializers.ModelSerializer):
class Meta:
model = Picture
fields = '__all__'
class AlbumSerializer(serializers.ModelSerializer):
pictures = PictureSerializer(many=True, read_only=True)
class Meta:
model = Album
fields = '__all__'
Now, I have some objects already defined, so I wanted to try in a shell:
>>> album = Album.objects.get(pk=1)
>>> len(Picture.objects.filter(album__exact=Album.objects.get(pk=1)))
3
>>> AlbumSerializer(instance=album).data
{'id': 1, 'name': 'First album', 'description': '', 'start_date': '2019-08-15', 'end_date': None, 'parent_album': None}
>>> AlbumSerializer()
AlbumSerializer():
id = IntegerField(label='ID', read_only=True)
pictures = PictureSerializer(many=True, read_only=True):
id = IntegerField(label='ID', read_only=True)
path = ImageField(max_length=100)
album = PrimaryKeyRelatedField(queryset=Album.objects.all())
name = CharField(max_length=100)
description = CharField(allow_blank=True, allow_null=True, required=False, style={'base_template': 'textarea.html'})
start_date = DateField()
end_date = DateField(allow_null=True, required=False)
parent_album = PrimaryKeyRelatedField(allow_null=True, queryset=Album.objects.all(), required=False)
You will notice that there are no pictures in the serialized data, but the field is there when I print the serializer, and that's my problem... Have I missed something somewhere?
Thanks for the help!
Your problem is due to the related name given to ForeignKeys.
Right now with the code you provided the name backwards relation name should be picture_set. So for the serializer to work you should change it do the following:
class AlbumSerializer(serializers.ModelSerializer):
picture_set = PictureSerializer(many=True, read_only=True)
class Meta:
model = Album
fields = '__all__'
But, if you want to display as pictures you can also do the following:
class AlbumSerializer(serializers.ModelSerializer):
pictures = PictureSerializer(many=True, read_only=True, source='picture_set')
class Meta:
model = Album
fields = '__all__'
Finally, if you want to get rid of the picture_set you should change your code to the following:
models.py
class Picture(models.Model):
path = models.ImageField()
album = models.ForeignKey('Album', on_delete=models.CASCADE, related_name='pictures')
serializers.py
class AlbumSerializer(serializers.ModelSerializer):
pictures = PictureSerializer(many=True, read_only=True)
class Meta:
model = Album
fields = '__all__'

How to serialize a one to many relation in django-rest using Model serializer?

These are my models and serializers. I want a representation of Question Model along with a list of people the question was asked to.
I am trying this:
#api_view(['GET', 'PATCH'])
def questions_by_id(request,user,pk):
question = Question.objects.get(pk=pk)
if request.method == 'GET':
serializer = QuestionSerializer(question)
return Response(serializer.data)
But I get an empty dictionary ({}). However when I remove the asked field from QuestionSerializer I get a complete representation of Question along with Places serialized nicely. What am I missing ?
class AskedToSerializer(serializers.ModelSerializer):
class Meta:
model = AskedTo
fields = ('to_user', 'answered')
class QuestionSerializer(serializers.ModelSerializer):
class Meta:
model = Question
places = PlaceSerializer(many=True, required=False)
asked = AskedToSerializer(source='askedto_set', many=True)
fields = ('id', 'created_on', 'title', 'places', 'answered','asked')
extra_kwargs = {'created_by': {'read_only': True}}
class Question(BaseModel):
title = models.CharField(max_length=200, null=False)
places = models.ManyToManyField(Place, blank=True)
answered = models.BooleanField(default=False)
class AskedTo(BaseModel):
ques = models.ForeignKey(Question, on_delete=models.CASCADE)
to_user = models.ForeignKey(settings.AUTH_USER_MODEL)
replied = models.BooleanField(default=False)
class Place(models.Model):
g_place_id = models.CharField(max_length=20,primary_key=True)
json = models.TextField(null=True)
name = models.CharField(max_length=40)
I figured it out. There were two errors.
Changed this:
class AskedToSerializer(serializers.ModelSerializer):
class Meta:
model = AskedTo
fields = ('to_user', 'answered')
to this (notice the change in fields, fields on model and serializer didn't match)
class AskedToSerializer(serializers.ModelSerializer):
class Meta:
model = AskedTo
fields = ('to_user', 'replied')
Secondly, I needed to define any extra fields outside class Meta
class QuestionSerializer(serializers.ModelSerializer):
places = PlaceSerializer(many=True, required=False)
asked = AskedToSerializer(source='askedto_set', many=True)
class Meta:
model = Question
fields = ('id', 'created_on', 'title', 'places', 'answered','asked')
extra_kwargs = {'created_by': {'read_only': True}}
Notice the change in definition of places and asked.
In my case, I have this models.py:
class Section(models.Model):
title = models.CharField(max_length=500)
description = models.TextField(blank=True)
user = models.ForeignKey(Profile, related_name="sections", on_delete=models.CASCADE)
class Feed(models.Model):
title = models.CharField(max_length=500)
link_rss = models.URLField(max_length=500)
link_web = models.URLField(max_length=500)
description = models.TextField(blank=True)
language = models.CharField(max_length=50, blank=True)
logo = models.URLField(blank=True)
sections = models.ManyToManyField(Section, related_name="feeds")
And I completed the serializers.py this:
class FeedSerializer(serializers.ModelSerializer):
sections = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Feed
fields = '__all__'
class SectionSerializer(serializers.ModelSerializer):
feeds = FeedSerializer(many=True, read_only=True)
class Meta:
model = Section
exclude = ('user',)

Django Python 'Customer' object is not iterable

from django.db import models
class Customer(models.Model):
cust_firstname=models.TextField(max_length=50)
cust_lastname=models.TextField(max_length=50)
cust_company=models.TextField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
cust_contact_number = models.IntegerField()
cust_email = models.TextField(max_length=100)
cust_address=models.TextField(max_length=200,default=None)
class Employee(models.Model):
employee_firstname = models.TextField(max_length=50)
employee_lastname = models.TextField(max_length=50)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
employee_contact_number = models.IntegerField()
employee_email = models.TextField(max_length=100)
employee_designation = models.TextField(max_length=50)
employee_address=models.TextField(max_length=200, default=None)
class ProjectDetails(models.Model):
customer = models.ForeignKey(Customer)
employee=models.ForeignKey(Employee)
project_name = models.TextField(max_length=50)
project_startdate = models.DateTimeField(auto_now=False, default=None)
project_status = models.TextField(default="Initial")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
The above code is my model declaration
and my Serializer class is
from ProjectTemplate.models import Customer, Employee, ProjectDetails
from rest_framework import serializers
class CustomerSerializers(serializers.ModelSerializer):
class Meta:
model=Customer
fields = ('id','cust_firstname','cust_lastname','cust_company','created_at','updated_at','cust_contact','cust_email','cust_address')
read_only_fields = ('created_at', 'updated_at')
class EmployeeSerializer(serializers.ModelSerializer):
class Meta:
model=Employee
fields = ('id','employee_firstname','employee_lastname','created_at','updated_at','employee_contact','employee_email','employee_designation','employee_address')
read_only_fields = ('created_at', 'updated_at')
class ProjectDetailsSerializer(serializers.ModelSerializer):
customer = CustomerSerializers(many=True, read_only=True)
employee = EmployeeSerializer(many=True, read_only=True)
class Meta:
model = ProjectDetails
fields = ('id','project_name','project_startdate','created_at','updated_at','project_status','customer','employee')
read_only_fields = ('created_at', 'updated_at')
In my view i have the below code
def get(self, request, format=None):
queryset = ProjectDetails.objects.all()
projectid = self.request.query_params.get('pk', None)
if projectid is not None:
queryset = queryset.get(id=projectid)
serializer = ProjectDetailsSerializer(queryset, many=False)
return Response(serializer.data)
else:
serializer = ProjectDetailsSerializer(queryset, many=True)
return Response(serializer.data)
And my URL for the above view is
url(r'^api/v1/projects/$', ProjectListView.as_view()),
And when i try to access the URL on my Browser i get TypeError. Im trying to get all the Customers and Employees who belong to a project. But it fails can any one help me to fix this.
I'm trying to get all the Customers and Employees who belong to a project.
I am not sure what do you want to achieve here because looking at your model, an instance of ProjectDetail will only have one customer and one employee:
class ProjectDetails(models.Model):
customer = models.ForeignKey(Customer)
employee=models.ForeignKey(Employee)
Considering this, using many=True doesn't make any sense in the serializer. So this is what causing the error:
class ProjectDetailsSerializer(serializers.ModelSerializer):
customer = CustomerSerializers(many=True, read_only=True)
employee = EmployeeSerializer(many=True, read_only=True)
UPDATE: To show a specific field from the related object:
Based on the comments in the answer the OP want to show the name of customer or employee instead of id.
It can be achieved using a SlugRelatedField:
class ProjectDetailsSerializer(serializers.ModelSerializer):
customer = serializers.SlugRelatedField(slug_field='cust_firstname', read_only=True)
employee = serializers.SlugRelatedField(slug_field='employee_firstname ', read_only=True)
# other fields
Do note that using SlugRelatedField you can get only one field as in the example above you would get firstname for both.

Categories