when i try to post a house instance it tells me the data is valid but won't save it knowing that it was working before i added the user attribute , the problem i think is in the "id" attribute
class houseSerializers(serializers.ModelSerializer):
class Meta:
model = House
fields =('id','user','title','city','type','address','rooms','beds','price')
the representation of the serializer
>>> print(repr(serializer))
houseSerializers():
id = IntegerField(label='ID', read_only=True)
user = PrimaryKeyRelatedField(queryset=User.objects.all())
title = CharField(max_length=50)
city = CharField(max_length=50)
type = CharField(max_length=50)
address = CharField(max_length=50)
rooms = IntegerField()
beds = IntegerField()
price = IntegerField()
>>>
#api_view(['POST'])
def houseCreate(request):
serializer = houseSerializers(data=request.data)
if serializer.is_valid():
serializer.save
return Response(serializer.data)
Related
Let us imagine that I have two models.
First model contains curse details and user that created this course
class Course(models.Model):
course_name = models.CharField(max_length=100, null=False)
description = models.CharField(max_length=255)
user_profile = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
and my second model is:
class Lesson(models.Model):
course = models.OneToOneField(Course, on_delete=models.CASCADE) #
# inside the course I want my APIVIEW to list only the courses that current user created.
# OnetoOne relationship does not solve the problem.
status = models.CharField(choices=STATUS, null=False, default=GOZLEMEDE,max_length=20)
tariffs = models.FloatField(max_length=5,null=False,default=0.00)
continues_off = models.CharField(max_length=2)
user_profile = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
My serializers for both Models:
class LessonSerializer(serializers.ModelSerializer):
class Meta:
model = models.Lesson
fields = ('course', 'status', 'tariffs', 'continues_off', 'user_profile')
def create(self, validated_data):
lesson = models.Lesson.objects.create(
course = validated_data['course'],
status = validated_data['status'],
tariffs=validated_data['tariffs'],
continues_off=validated_data['continues_off'],
user_profile=validated_data['user_profile']
)
return lesson
class CourseSerializer(serializers.ModelSerializer):
"""Serializers Course content"""
class Meta:
model = models.Course
fields = '__all__'
def create(self,validated_data):
course = models.Course.objects.create(
course_name = validated_data['course_name'],
description=validated_data['description'],
user_profile=validated_data['user_profile']
)
return course
My Viewset:
class LessonViewset(viewsets.ModelViewSet):
model = models.Lesson
serializer_class = serializers.LessonSerializer
authentication_classes = (SessionAuthentication,)
permission_classes = (IsAuthenticated,BasePermission,)
def get_queryset(self):
user_current = self.request.user.id
return models.Lesson.objects.filter(user_profile=user_current)
How can I get the desired result. I want to get the courses for the current user and show them as a dropdown list in my API view. Just only the courses that user created should be in the dropdown list not all.
OnetoOne relationship gives all results of course table.
i think change your view code to :
def get_queryset(self,id):
return model.objects.filter(user_profile=id)
#You do not need to call it again when you put the Lesson on the model
\
trying to POST request and create new location. now the filed country_id cues many troubles.
those are the classes
class Country(models.Model):
id = UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
country_name = CharField(max_length=255)
federal_rate = FloatField()
social_security = FloatField()
comment = CharField(max_length=255)
class Location(models.Model):
participant_id = ForeignKey('Participant', on_delete=CASCADE, related_name="locations")
country_id = ForeignKey('Country', on_delete=CASCADE, related_name="country")
start_date = DateField()
end_date = DateField()
region = CharField(max_length=255)
tax_policy = CharField(max_length=255, null=True)
Serializer
class CountrySerializer(serializers.ModelSerializer):
class Meta:
model = Country
fields = "__all__"
class LoactionSerializer(serializers.ModelSerializer):
country_id = CountrySerializer(many=False)
class Meta:
model = Location
fields = "__all__"
now the only why that I mange to join the tables are adding the country_id line in the seirializer, and whem im trying to create new location, an error occur
this is the way I'm trying to add new location
{
"start_date": "2021-10-10",
"end_date": "2021-11-18",
"region": "markian-land",
"tax_policy": "N/A",
"participant_id": "c5c1a00c-4263-418a-9f3a-3ce40a1ea334",
"country_id": "9067e71f-c6b9-4ecc-ad6b-461843063aee"
}
the Error - {"country_id": {"non_field_errors": ["Invalid data. Expected a dictionary, but got str."]}}
when i mange to send as dict, but i have another error that asking for all Country fields, so i get the specific country from data base, and when im sending it i got json error
You can have more details in this answer
Django Rest Framework doesn't provide this functionality, you need to create a serializer field first.
from rest_framework import serializers
class RelatedFieldAlternative(serializers.PrimaryKeyRelatedField):
def __init__(self, **kwargs):
self.serializer = kwargs.pop('serializer', None)
if self.serializer is not None and not issubclass(self.serializer, serializers.Serializer):
raise TypeError('"serializer" is not a valid serializer class')
super().__init__(**kwargs)
def use_pk_only_optimization(self):
return False if self.serializer else True
def to_representation(self, instance):
if self.serializer:
return self.serializer(instance, context=self.context).data
return super().to_representation(instance)
Then use this new serializer field in Location serializer as,
class LoactionSerializer(serializers.ModelSerializer):
country_id = RelatedFieldAlternative(queryset = Country.objects.all(),
serializer=CountrySerializer)
class Meta:
model = Location
fields = "__all__"
Please note: You have a typo in LoactionSerializer.
this
country_id = ForeignKey(Country, on_delete=CASCADE, related_name="country")
You defined country_id as a CountrySerializer in the LocationSerializer and in the CountrySerializer, fields = "__all__" means you need to pass all the country object fields to make it work.
Try to delete country_id = CountrySerializer(many=False) in the LocationSerializer and try again.
So I have 3 models of interest:
models.py
class Author(models.Model): #Author of books
name = models.CharField(max_length = 100)
#property # This is code to get the books linked to an author
def books(self):
book = Book.objects.filter(authors = self.id)
return ', '.join(map(str,book)) #This makes the book list in this format "A, B, C"
def __str__(self):
return self.name.title()
class Genre(models.Model): #Genre of books
name = models.CharField(max_length = 50, unique = True)
#property
def books(self):
book = Book.objects.filter(genre = self.id)
return ', '.join(map(str,book))
def __str__(self):
return self.name.title()
class Book(models.Model):
name = models.CharField(max_length = 150,) #Name of books
authors = models.ManyToManyField(Author,) #Many to many because multiple books can have multiple authors
genre = models.ManyToManyField(Genre)
def __str__(self):
return self.name.title()
Those are the models, and both genre and author have a many to many relation with books
serializers.py
class AuthorListSerializer(serializers.ModelSerializer):
class Meta:
model = models.Author
fields = ('name',)
class GenreListSerializer(serializers.ModelSerializer):
class Meta:
model = models.Genre
fields = ('name',)
class BookListSerializer(serializers.ModelSerializer):
authors = AuthorListSerializer(many = True,) #To represent the relationship as a string instead of id
genre = serializers.SlugRelatedField(many = True, queryset = models.Genre.objects.all(),slug_field = 'name')
class Meta:
model = models.Book
fields = ('name','authors','rating', 'genre')
class BookDetailSerializer(serializers.ModelSerializer):
publisher = serializers.SlugRelatedField(queryset = models.Publisher.objects.all(), slug_field= 'name') #To display publisher name instead of id
authors = serializers.StringRelatedField(many = True,) #To represent the relationship as a string instead of id
genre = serializers.StringRelatedField(many = True,)
class Meta:
model = models.Book
fields = ('name', 'authors', 'rating','genre',
'publisher', 'total_qty', 'avail_qty',
'pub_date','isbn','price',)
So in my Django-Rest-Framework Browsable api, Normal ForeignKey fields have their options displayed to create a new object instance.
For example The publishers in the "BookDetailSerializer" have all the publishers displayed as options for a POST or PUT or PATCH request.
but for the many to many fields that include Genre and Author it is not only blank, but it appears that I cannot put any input whatsoever.
I have tried using the DRF-writable-nested third party package but nothing changed:
class BookListSerializer(WritableNestedModelSerializer):
authors = AuthorListSerializer(many = True,) #To represent the relationship as a string instead of id
genre = serializers.SlugRelatedField(many = True,
queryset = models.Genre.objects.all(),slug_field = 'name')
My question is how can I be able to make POST request with my list of Authors and Genres available through the DRF browsable api?
Thanks in advance!!
This image shows the options available for making a POST request involving publishers
This image shows that you can't add any input for POST request involving both Genre and Authors as many-to many relations.
Update: So I have added a create method and it still doesn't work, as shown below:
class BookListSerializer(serializers.ModelSerializer):
authors = AuthorListSerializer(many = True,) #To represent the relationship as a string instead of id
genre = serializers.SlugRelatedField(many = True,
queryset = models.Genre.objects.all(),slug_field = 'name')
class Meta:
model = models.Book
fields = ('name','authors','rating', 'genre')
def create(self,validated_data):
authors_data = models.Author.objects.all()
book = Book.objects.create(authors = authors_data,**validated_data,)
return book
What can I do?
After struggling for 4 days I figured out the answer
When handling many to many relationships and you want to want the code to be as follows.
class BookListSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = ('name','authors','rating', 'genre')
depth = 1
Adding the depth allows the nested relationships to fully serialize.
Because the relationship is nested, and is a many to many relationship you would have to create your own create function in the views.py as follows:
class BookViewSet(GetSerializerClassMixin, viewsets.ModelViewSet):
queryset = models.Book.objects.all()
serializer_class = serializers.BookDetailSerializer
serializer_action_classes = {'list': serializers.BookListSerializer}
permission_classes = [IsAdminOrReadOnly, permissions.IsAuthenticated,]
def create(self,request, *args, **kwargs):
data = request.data
new_book = models.Book.objects.create(name = data["name"], publisher = models.Publisher.objects.get(id = data["publisher"]),
pub_date = data["pub_date"],
price = data["price"],
isbn = data['isbn'],)
new_book.save()
for author in data['authors']:
author_obj = models.Author.objects.get(name = author['name'])
new_book.authors.add(author_obj)
for gen in data['genre']:
gen_obj = models.Genre.objects.get(name = gen['name'])
new_book.genre.add(gen_obj)
serializer = serializers.BookListSerializer(new_book)
return Response(serializer.data)
For the many to many relationships you have to create them after saving the object and add them to the object manually.
There is a foriegn Key relationship that's present there "Publishers"
For that relationship you have to manually point to its location in the database hence the code below.
name = data["name"], publisher = models.Publisher.objects.get(id = data["publisher"]),
For the Detail book serializer in the question its the same thing with the BookListSerializer
That's how I was able to handle POST requests in the Many-to-Many relations
My form sends data to django-rest-framework, but the form contains two fields, and I want to save 5 fields in the database, other fields I calculate on my own (they are not sent by the form). How can I add additional values before saving?
so, form send 'user' and 'comment' values, I want add 'article', 'ip_address' before save to DB
models.py
class Comments(models.Model):
article = models.ForeignKey(Articles, on_delete=models.CASCADE)
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
comment = models.TextField(verbose_name=_('Comment'))
submit_date = models.DateTimeField(_('Created'), auto_now_add=True)
ip_address = models.CharField(_('IP address'), max_length=50)
is_public = models.BooleanField(verbose_name=_('Publish'), default=False)
serializers.py
class CommentsSerializer(serializers.ModelSerializer):
user = serializers.ReadOnlyField(source='user.first_name')
class Meta:
model = Comments
fields = ('user', 'comment')
views.py
class AddCommentViewSet(viewsets.ModelViewSet):
queryset = Comments.objects.all()
serializer_class = CommentsSerializer
You have to override create() method:
class CommentsSerializer(serializers.ModelSerializer):
user = serializers.ReadOnlyField(source='user.first_name')
class Meta:
model = Comments
fields = ('user', 'comment')
def create(self, validated_data):
new_comment = models.Comment()
new_comment.user = validated_data['user']
new_comment.comment = validated_data['comment']
new_comment.article = get_your_article_somehow()
new_comment.ip_address = get_your_ip_address_somehow()
new_comment.save()
return new_comment
I have a customer model in Bcustomer app that extends the django User model, So I will save the basic details such as name in User table and the remaining data (city, etc) in customer table.
Saving is working perfectly. But now it is showing the following error when I call the GET method.
AttributeError at /api/v1/customer 'str' object has no attribute 'values'
Request Method: GET
bcustomer/models.py
class BCustomer(models.Model):
customer = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, primary_key=True, blank=True )
address = models.CharField(max_length=50)
city = models.CharField(max_length=256)
state = models.CharField(max_length=50)
user = models.ForeignKey(settings.AUTH_USER_MODEL, db_index=True, on_delete=models.CASCADE, related_name='customer_creator')
# more fields to go
def __str__(self):
# return str(self.name) (This should print first and last name in User model)
class Meta:
app_label = 'bcustomer'
bcusomer/serializers.py
class CustomerDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = BCustomer
fields = ('city', 'phone')
class CustomerSerializer(serializers.ModelSerializer):
customer_details = CustomerDetailsSerializer()
class Meta:
model = get_user_model()
fields = ('id','first_name', 'email', 'customer_details')
def create(self, validated_data):
request = self.context.get('request')
customer_details_data = validated_data.pop('customer_details')
customer_user = get_user_model().objects.create(**validated_data)
BCustomer.objects.create(customer=customer_user, user=request.user, **customer_details_data)
customer_user.customer_details = customer_details_data
return customer_user
class CustomerListSerializer(serializers.ModelSerializer):
model = get_user_model()
fields = '__all__'
class Meta:
model = get_user_model()
fields = '__all__'
bcustomer/views.py
class CustomerViewSet(viewsets.ModelViewSet):
customer_photo_thumb = BCustomer.get_thumbnail_url
permission_classes = [permissions.IsAuthenticated, TokenHasReadWriteScope]
queryset = BCustomer.objects.all()
serializer_class = CustomerSerializer
def get_queryset(self):
queryset = BCustomer.objects.all()
return queryset
def get_serializer_class(self):
if self.action == 'list' or self.action == 'retrieve':
return CustomerListSerializer
return CustomerSerializer
bcustomer/urls.py
router.register(r'customer', views.CustomerViewSet, 'customers')
Data post parameter format
{
"first_name":"Myname",
"email":"testemail#gmail.com",
"customer_details": {
"city":"citys",
"phone":"04722874567",
}
}
You should remove model and fields from CustomListSerializer
class CustomerListSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = '__all__'
customer_details = CustomerDetailsSerializer()
You need to set the source argument to point to the user model's customer. Most probably:
customer_details = CustomerDetailsSerializer(source='customer')
(or maybe source='bcustomer', not sure if it reversed the field name or class name).
On a side not, you should not need the ListSerializer at all. The list method will call the serializer with the many=True argument on CustomerSerializer which will create the ListSerializer appropriately.