I'm having an strange issue with DRF ans some serializers.
Here is my model:
class Accommodation(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
product = models.OneToOneField(
Product,
on_delete=models.CASCADE,
primary_key=True,
)
description = models.TextField(null=True, blank=True, verbose_name=_(u'Description'))
shared_accommodation = models.BooleanField(default=False)
accommodation_unit_quantity = models.PositiveSmallIntegerField(default=1,
verbose_name=_(u'Number of acommodation units '
u'for this acommodation'))
accommodation_unit_name = models.TextField(null=False, blank=False, verbose_name=_(u'Name for accommodation units '
u'for this accommodation'))
class Meta:
verbose_name_plural = _(u'Accommodations')
def __unicode__(self):
return u'{0} <{1}>'.format(self.product.name, self.product.school)
class Product(AbstractProduct):
name = models.CharField(max_length=50, verbose_name=_(u'Name'))
school = models.ForeignKey('school.School')
levels = models.ManyToManyField('school.Level',verbose_name=_(u'Level'))
age = IntegerRangeField(null=True)
gender = models.CharField(choices=GENDER_CHOICES, max_length=1, null=True, blank=True, verbose_name=_(u'Gender'))
num_sessions = models.PositiveSmallIntegerField(
verbose_name=_(u'Number of sessions'),
default=1,
help_text=_(u"Number of sessions that the product has."),
)
school_category = models.ForeignKey(
'school.Category',
blank=True, null=True,
verbose_name=_(u'Category')
)
addons = models.ManyToManyField('self',
verbose_name=_(u'Administrators'),
through='AddonInService',
symmetrical=False,
related_name='addon_can_be_used_in'
)
pay_option = models.CharField(choices=PAYMENT_OPTIONS, max_length=1, null=True, blank=True, verbose_name=_(u'Pay_option'), default='U')
payment_type = models.CharField(choices=PAYMENT_TYPE, max_length=1, null=True, blank=True, verbose_name=_(u'pay_type'))
payment_amount = models.FloatField(verbose_name=_(u'Amount'), default=0.0)
objects = ProductManager()
class Meta(AbstractProduct.Meta):
verbose_name_plural = _(u'Products')
def __unicode__(self):
return self.name
As you can see, basically a Product can be an Accommodation. Here are the Serializers
class AccommodationSerializer(serializers.ModelSerializer):
class Meta:
model = Accommodation
fields = [
'description',
'shared_accommodation',
'accommodation_unit_quantity',
'accommodation_unit_name',
]
class ProductAccommodationSerializer(ProductSerializer):
accommodation = AccommodationSerializer()
class Meta(ProductSerializer.Meta):
fields = [
'id',
'structure',
'upc',
'title',
'slug',
'description',
'rating',
'date_created',
'date_updated',
'is_discountable',
'name',
'age',
'gender',
'num_sessions',
'parent',
'product_class',
'school',
'levels',
'school_category',
'addons',
'color',
'price',
'all_prices',
'variants',
'pay_option',
'payment_type',
'payment_amount',
'accommodation',
]
def create(self, validated_data):
accommodation_data = validated_data.pop('accommodation')
levels = []
if 'levels' in validated_data:
levels = validated_data.pop('levels')
product = Product.objects.create(**validated_data)
school_accommodation, created = ProductClass.objects.get_or_create(name='School Accommodation')
if created:
product.product_class = school_accommodation
for lev in levels:
product.levels.add(lev)
product.save()
acc = AccommodationSerializer(product=product, **accommodation_data)
acc.save()
return product
class ProductSerializer(serializers.ModelSerializer):
age = IntegerRangeField()
addons = AddonSerializer(many=True, read_only=True)
# Get the price for the Product, using the property in the Model
price = serializers.DecimalField(required=False, max_digits=7,
decimal_places=2, source='get_price',
read_only=True)
color = serializers.SerializerMethodField()
all_prices = PriceSerializer(source='stockrecords', many=True,
required=False)
variants = VariantSerializer(many=True, source='children', required=False)
class Meta:
model = Product
fields = [
'id',
'structure',
'upc',
'title',
'slug',
'description',
'rating',
'date_created',
'date_updated',
'is_discountable',
'name',
'age',
'gender',
'num_sessions',
'parent',
'product_class',
'school',
'levels',
'school_category',
'addons',
'color',
'price',
'all_prices',
'variants',
'pay_option',
'payment_type',
'payment_amount'
]
Performing a simple test where I try to create an Accommodation, I get the following error:
Traceback (most recent call last):
File "/home/internetmosquito/git/mvp_opencoast/opencoast_django/opencoast/applications/accommodation/tests/test_accommodations.py", line 165, in test_create_accommodation
response = self.client.post(url, data, format='json')
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/rest_framework/test.py", line 170, in post
path, data=data, format=format, content_type=content_type, **extra)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/rest_framework/test.py", line 92, in post
return self.generic('POST', path, data, content_type, **extra)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/django/test/client.py", line 380, in generic
return self.request(**r)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/rest_framework/test.py", line 159, in request
return super(APIClient, self).request(**kwargs)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/rest_framework/test.py", line 111, in request
request = super(APIRequestFactory, self).request(**kwargs)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/django/test/client.py", line 467, in request
six.reraise(*exc_info)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 149, in get_response
response = self.process_exception_by_middleware(e, request)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 147, in get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
return view_func(*args, **kwargs)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/rest_framework/viewsets.py", line 87, in view
return self.dispatch(request, *args, **kwargs)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/rest_framework/views.py", line 466, in dispatch
response = self.handle_exception(exc)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/rest_framework/views.py", line 463, in dispatch
response = handler(request, *args, **kwargs)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/rest_framework/mixins.py", line 21, in create
self.perform_create(serializer)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/rest_framework/mixins.py", line 26, in perform_create
serializer.save()
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/rest_framework/serializers.py", line 191, in save
self.instance = self.create(validated_data)
File "/home/internetmosquito/git/mvp_opencoast/opencoast_django/opencoast/applications/accommodation/serializers.py", line 77, in create
acc = AccommodationSerializer(product=product, **accommodation_data)
File "/home/internetmosquito/python_envs/opencoast_django/local/lib/python2.7/site-packages/rest_framework/serializers.py", line 95, in __init__
super(BaseSerializer, self).__init__(**kwargs)
TypeError: __init__() got an unexpected keyword argument 'product'
Tried to remove the
product=product
From
acc = AccommodationSerializer(product=product, **accommodation_data)
But then I get the same error but with 'shared_accommodation' field instead of product
WHat I'm doing wrong here? Any ideas?
EDIT: Added ProductSerializer, I missed that one sorry
SECOND EDIT: As suggested by some, I've added the product field to the AccommodationSerializer:
class AccommodationSerializer(serializers.ModelSerializer):
class Meta:
model = Accommodation
fields = [
'product',
'description',
'shared_accommodation',
'accommodation_unit_quantity',
'accommodation_unit_name',
]
But then when trying to create an instance, I get the following error:
{'accommodation': OrderedDict([('product', [u'This field is required.'])])}
Funny enough, if I add the product to the test data payload (even though I haven't created the product at the time I call the endpoint to make an Accommodation, the error above dissappears):
data = {
"name": "Hotel Hudson",
"slug": "hotel-hudson",
"age": {'upper': 99, 'lower': 18},
"school": school1.id,
"levels": [school1.level_set.all()[0].id],
"accommodation": {
"product": 1,
"description": "A very nice hotel",
"shared_accommodation": False,
"accommodation_unit_quantity": 1,
"accommodation_unit_name": "Room",
"accommodation_units": [
{
'name': "Room-1",
'max_pax': 1,
},
{
'name': "Room-2",
'max_pax': 3,
},
]
},
}
While this is interesting, this is obviously not what I want...I don't want having to pass a fake product ID when calling the endpoint to create an Accommodation...any pointers?
Using the data field would be the right way since the keywords in the DRF Serializer hierarchy are not generic. If the dictionary you specifiy for data is valid you could create a model instance with .save() (after calling .is_valid()). The dictionary could of course be augmented with further attributes before creating the model. But beware, that the serializer only uses the attributes, which are specied in the Meta.fields field of the serializer.
And here is the critical point, why your approach would not work after all: the AccomodationSerializer.Meta.fields does not include the product field, which is mandatory if you want to create a model.
It is fine to use AccommodationSerializer to read from the Accommodation model or if you want to post a partial structure of the model for some reason. But if you want to use it to create a model instance, you have to specify all fields which are not nullable or have a default value.
Instead of using the AccommodationSerializer here, you could just call:
Accommodation.objects.create(product=product, **accommodation_data)
I tried to set up a minimal example. Hope this helps.
models.py:
class Owner(models.Model):
owner_name = models.CharField(max_length=255)
class Product(models.Model):
name = models.CharField(max_length=255)
owner = models.OneToOneField(Owner)
serializer.py
class OwnerSerializer(serializers.ModelSerializer):
class Meta:
model = Owner
fields = [
'owner_name',
]
class ProductSerializer(serializers.ModelSerializer):
owner = OwnerSerializer(read_only=True)
class Meta:
model = Product
fields = [
'owner',
'name',
]
class ProductOwnerSerializer(serializers.ModelSerializer):
product = ProductSerializer()
class Meta:
model = Owner
fields = [
'product',
'owner_name',
]
def create(self, validated_data):
product_data = validated_data.pop('product')
owner = Owner.objects.create(**validated_data)
Product.objects.create(owner=owner, **product_data)
return owner
I also agree with Jerzyk's comment, that I really don't like the Meta(Superclass), seems like an anti-pattern to me.
Related
I have a CreateView with which I create new blog posts, I want to test it in order to check if everything is ok but something is wrong with my test and I can't understand what exactly. it gives me 2 errors, for the first method I get this error:
Traceback (most recent call last):
File "C:\Users\Bularu Lilian\Desktop\EcoMon\blog\tests\test_views.py", line 73, in test_post_create_view_GET
self.assertEquals(response.status_code, 200)
AssertionError: 302 != 200
and for the second one is this error:
File "C:\Users\Bularu Lilian\Desktop\EcoMon\blog\tests\test_views.py", line 78, in test_post_create_view_POST_success
post = Post.objects.get(title=self.post['title'])
File "C:\Users\Bularu Lilian\Desktop\Environments\ecomon\lib\site-packages\django\db\models\manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "C:\Users\Bularu Lilian\Desktop\Environments\ecomon\lib\site-packages\django\db\models\query.py", line 429, in get
raise self.model.DoesNotExist(
blog.models.Post.DoesNotExist: Post matching query does not exist.
This is my Test class:
class TestPostCreateViews(BaseTest):
def test_post_create_view_GET(self):
response = self.client.get(self.add_post_url)
self.assertEquals(response.status_code, 200)
self.assertTemplateUsed(response, 'blog/add_post.html')
def test_post_create_view_POST_success(self):
response = self.client.post(self.add_post_url, self.post, author=self.user, format='text/html')
post = Post.objects.get(title=self.post['title'])
self.assertEquals(response.status_code, 302)
self.assertEquals(post.title, 'test post')
my CreateView:
class PostCreateView(LoginRequiredMixin, IsSuperuserOrStaffMixin, CreateView):
template_name = 'blog/add_post.html'
form_class = PostCreateForm
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
my url path:
path('new/post/', PostCreateView.as_view(), name='add-post'),
my form:
class PostCreateForm(forms.ModelForm):
title = forms.CharField(widget=forms.TextInput(), max_length=200)
content = forms.CharField(widget=forms.Textarea(attrs={'rows': 25, 'cols': 50}))
class Meta:
model = Post
exclude = ['author', 'slug', 'published_date', 'updated_date']
and my model:
class Post(models.Model):
class PostCategory(models.TextChoices):
FAMILY = 'FAMILY', _('Family')
BUSINESS = 'BUSINESS', _('Business')
MWRKETING = 'MARKETING', _('Marketing')
SPENDINGS = 'SPENDINGS', _('Spendings')
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(_('Title'), max_length=200, unique=True)
content = models.TextField(_('Content'))
category = models.CharField(_('Category'), max_length=9, choices=PostCategory.choices, default=PostCategory.BUSINESS)
slug = models.SlugField(_('Slug'), max_length=200, blank=True, null=False, unique=True)
tags = TaggableManager(_('Tags'))
published_date = models.DateTimeField(_('Published Date/Time'), auto_now_add=True)
updated_date = models.DateTimeField(_('Updated Date/Time'), auto_now=True)
def __str__(self):
return self.title
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title)
super().save(*args, **kwargs)
#property
def comments_count(self):
return self.comments.count()
def get_absolute_url(self):
return reverse('blog')
for any help I would be greatefull
Try to write it like this:
def test_post_create_view_POST_success(self):
data = {
'author': self.user,
'title': 'test_title',
'content': 'test_content',
'category': 'test_category',
'slug': 'test_slug',
'tags': None, # or create some tags and populate
}
response = self.client.post(self.add_post_url, data=data, follow=True) # make sure your url is correct too btw that could also be the issue
self.assertEquals(response.status_code, 200)
self.assertEquals(Post.objects.filter(title='test_title').count(), 1)
I have been trying this for some days now with no solution.I am getting this weird error after which I have made several trials all of which hasn't solved my issue, I would be glad to receive a solution.
ERROR LOGS
Traceback (most recent call last):
File "C:\Users\Habib\Documents\django\django-new\student-management-system\venv\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
response = get_response(request)
File "C:\Users\Habib\Documents\django\django-new\student-management-system\venv\lib\site-packages\django\core\handlers\base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File "C:\Users\Habib\Documents\django\django-new\student-management-system\venv\lib\site-packages\django\core\handlers\base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\Habib\Documents\django\django-new\student-management-system\venv\lib\site-packages\django\views\generic\base.py", line 71, in view
return self.dispatch(request, *args, **kwargs)
File "C:\Users\Habib\Documents\django\django-new\student-management-system\venv\lib\site-packages\django\views\generic\base.py", line 97, in dispatch
return handler(request, *args, **kwargs)
File "C:\Users\Habib\Documents\django\django-new\student-management-system\venv\lib\site-packages\django\views\generic\list.py", line 142, in get
self.object_list = self.get_queryset()
File "C:\Users\Habib\Documents\django\django-new\student-management-system\student_management_app\StaffViews.py", line 364, in get_queryset
queryset = self.request.user.quizzes \
File "C:\Users\Habib\Documents\django\django-new\student-management-system\venv\lib\site-packages\django\utils\functional.py", line 225, in inner
return func(self._wrapped, *args)
Exception Type: AttributeError at /staff_quiz_home/
Exception Value: 'CustomUser' object has no attribute 'quizzes'
MODELS.PY
class CustomUser(AbstractUser):
user_type_data = ((1, "HOD"), (2, "Staff"), (3, "Student"))
user_type = models.CharField(default=1, choices=user_type_data, max_length=10)
class Quiz(models.Model):
owner = models.ForeignKey(Staffs, on_delete=models.CASCADE, related_name='quizzes')
name = models.CharField(max_length=255)
subject = models.ForeignKey(Subjects, on_delete=models.CASCADE, related_name='quizzes')
class student(models.Model):
name = models.CharField(max_length=255)
admin = models.OneToOneField(CustomUser, on_delete = models.CASCADE)
gender = models.CharField(max_length=50)
quizzes = models.ManyToManyField(Quiz, through='TakenQuiz')
class Staffs(models.Model):
name = models.CharField(max_length=255)
admin = models.OneToOneField(CustomUser, on_delete = models.CASCADE)
address = models.TextField()
class Subjects(models.Model):
name = models.CharField(max_length=255)
staff_id = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
VIEWS.PY
class QuizListView(ListView):
model = Quiz
ordering = ('name', )
context_object_name = 'quizzes'
template_name = "staff_template/quiz_home_page.html"
def get_queryset(self):
queryset = self.request.user.quizzes \
.select_related('subject') \
.annotate(questions_count=Count('questions', distinct=True)) \
.annotate(taken_count=Count('taken_quizzes', distinct=True))
return queryset
Thanks in advance, if you need anymore info please do let me know.
Your Quiz object is related to Staffs and not CustomUser so you should access it through
self.request.user.staffs.quizess
Other than that it is clear that you cannot do Query on a set so you should do something in a line of
Quiz.object.filter(owner__admin=request.user).
...
I am customizing the API that I give when I send the get request. The following error occurred when the get request was sent after customizing the response value using GenericAPIView.
traceback
Traceback (most recent call last):
File "C:\Users\kurak\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
response = get_response(request)
File "C:\Users\kurak\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\core\handlers\base.py", line 145, in _get_response
response = self.process_exception_by_middleware(e, request)
File "C:\Users\kurak\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\core\handlers\base.py", line 143, in _get_response
response = response.render()
File "C:\Users\kurak\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\template\response.py", line 105, in render
self.content = self.rendered_content
File "C:\Users\kurak\AppData\Local\Programs\Python\Python38-32\lib\site-packages\rest_framework\response.py", line 70, in rendered_content
ret = renderer.render(self.data, accepted_media_type, context)
File "C:\Users\kurak\AppData\Local\Programs\Python\Python38-32\lib\site-packages\rest_framework\renderers.py", line 100, in render
ret = json.dumps(
File "C:\Users\kurak\AppData\Local\Programs\Python\Python38-32\lib\site-packages\rest_framework\utils\json.py", line 25, in dumps
return json.dumps(*args, **kwargs)
File "C:\Users\kurak\AppData\Local\Programs\Python\Python38-32\lib\json\__init__.py", line 234, in dumps
return cls(
File "C:\Users\kurak\AppData\Local\Programs\Python\Python38-32\lib\json\encoder.py", line 199, in encode
chunks = self.iterencode(o, _one_shot=True)
File "C:\Users\kurak\AppData\Local\Programs\Python\Python38-32\lib\json\encoder.py", line 257, in iterencode
return _iterencode(o, 0)
File "C:\Users\kurak\AppData\Local\Programs\Python\Python38-32\lib\site-packages\rest_framework\utils\encoders.py", line 67, in default
return super().default(obj)
File "C:\Users\kurak\AppData\Local\Programs\Python\Python38-32\lib\json\encoder.py", line 179, in default
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type User is not JSON serializable
What's problem in my code? I can't solve this error. Please help me. Here is my code. Thanks in advance
views.py
class ReadPostView (GenericAPIView) :
serializer_class = PostSerializer
permission_classes = [IsAuthenticated]
def get (self, serializer) :
serializer = self.serializer_class()
posts = Post.objects.all()
data = []
for post in posts :
comments = Comment.objects.filter(post=post)
json = {
'pk': post.pk,
'author': {
'email': post.author_email,
'username': post.author_name,
'profile': post.author_profile
},
'like': post.liker.count,
'liker': post.liker,
'text': post.text,
'images': Image.objects.filter(post=post),
'comments_count': comments.count(),
'view': post.view,
'viewer_liked': None,
'tag': post.tag
}
data.append(json)
return Response(data)
models.py
class Post (models.Model):
author_name = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='authorName', null=True)
author_email = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='authorEmail', null=True)
author_profile = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='authorProfile', null=True)
title = models.CharField(max_length=40)
text = models.TextField(max_length=300)
tag = models.CharField(max_length=511, null=True)
view = models.IntegerField(default=0)
viewer = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='viewer', blank=True)
like = models.IntegerField(default=0)
liker = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='liker', blank=True)
def __str__ (self) :
return self.title
class Image (models.Model) :
post = models.ForeignKey(Post, on_delete=models.CASCADE)
image = models.ImageField(null=True, blank=True)
class Comment (models.Model) :
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
text = models.TextField(max_length=200)
There are a few problems with your code:
First, you can't pass an instance or a list of instances to your JSON fields. 'email': post.author_email,, 'username': post.author_name,, 'profile': post.author_profile, 'liker': post.liker, and 'images': Image.objects.filter(post=post),
To fix this you either have to create a serializer for their model and pass the serialized data instead or you have to just pass a serializable field of those models like post.liker.email
You can use DRF ModelSerializer's to make a model serializer: ModelSerializer
Second, you don't need all three fields author_name, author_email, and author_profile in your model. all of them are pointing to your default user model and you can access everywhere from one of them:
post.author_profile.email # will give you the user email
post.author_profile.first_name # will give you the user's first name
# and so on ...
Third, you can just use ListAPIView to generate a serialized list of your data: ListAPIView
You are doing the whole thing wrong here. Please consider looking at some more django and rest framework examples.
I have a big problem regarding the serialization of a Many to Many relationship with intermediate model in DRF: If the request method is get everything works perfectly. But as soon as i try to POST or PUT Data to the API I get the following Error:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/core/handlers/base.py", line 149, in get_response
response = self.process_exception_by_middleware(e, request)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/core/handlers/base.py", line 147, in get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
return view_func(*args, **kwargs)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/django/views/generic/base.py", line 68, in view
return self.dispatch(request, *args, **kwargs)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/djangorestframework-3.5.3-py2.7.egg/rest_framework/views.py", line 477, in dispatch
response = self.handle_exception(exc)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/djangorestframework-3.5.3-py2.7.egg/rest_framework/views.py", line 437, in handle_exception
self.raise_uncaught_exception(exc)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/djangorestframework-3.5.3-py2.7.egg/rest_framework/views.py", line 474, in dispatch
response = handler(request, *args, **kwargs)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/djangorestframework-3.5.3-py2.7.egg/rest_framework/generics.py", line 243, in post
return self.create(request, *args, **kwargs)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/djangorestframework-3.5.3-py2.7.egg/rest_framework/mixins.py", line 21, in create
self.perform_create(serializer)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/djangorestframework-3.5.3-py2.7.egg/rest_framework/mixins.py", line 26, in perform_create
serializer.save()
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/djangorestframework-3.5.3-py2.7.egg/rest_framework/serializers.py", line 214, in save
self.instance = self.create(validated_data)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/djangorestframework-3.5.3-py2.7.egg/rest_framework/serializers.py", line 888, in create
raise_errors_on_nested_writes('create', self, validated_data)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/djangorestframework-3.5.3-py2.7.egg/rest_framework/serializers.py", line 780, in raise_errors_on_nested_writes
class_name=serializer.__class__.__name__
AssertionError: The `.create()` method does not support writable nested fields by default.
Write an explicit `.create()` method for serializer `manager.serializers.EquipmentSerializer`, or set `read_only=True` on nested serializer fields.
I am not really sure how to write proper create and update functions and i donĀ“t really understand it, how it is explained in the documentation.
Code:
views.py:
from django.shortcuts import render
from django.contrib.auth.models import User, Group
from manager.serializers import *
from rest_framework import generics
from rest_framework import viewsets
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.views import APIView
from django.forms.models import model_to_dict
class OrderSetDetails(generics.RetrieveUpdateDestroyAPIView):
queryset = Order.objects.all()
serializer_class = OrderSerializer
class OrderSetList(generics.ListCreateAPIView):
queryset = Order.objects.all()
serializer_class = OrderSerializer
class EquipmentSetDetails(generics.RetrieveUpdateDestroyAPIView):
queryset = Equipment.objects.all()
serializer_class = EquipmentSerializer
class EquipmentSetList(generics.ListCreateAPIView):
queryset = Equipment.objects.all()
serializer_class = EquipmentSerializer
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all().order_by('-date_joined')
serializer_class = UserSerializer
class GroupViewSet(viewsets.ModelViewSet):
queryset = Group.objects.all()
serializer_class = GroupSerializer
class ClientList(generics.ListCreateAPIView):
queryset = client.objects.all()
serializer_class = ClientSerializer
serializers.py
from rest_framework import serializers
from django.contrib.auth.models import User, Group
from storage.models import *
class AssignmentSerializer(serializers.HyperlinkedModelSerializer):
id = serializers.ReadOnlyField(source = 'Order.id')
name = serializers.ReadOnlyField(source = 'Order.name')
class Meta:
model = Assignment
fields = ('id', 'name', 'quantity')
class EquipmentSerializer(serializers.ModelSerializer):
event = AssignmentSerializer(source= 'assignment_set', many = True)
class Meta:
model = Equipment
fields = '__all__'
class ClientSerializer(serializers.ModelSerializer):
class Meta:
model = client
fields = '__all__'
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('url', 'username', 'email', 'groups')
class GroupSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Group
fields = ('url', 'name')
class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = '__all__'
models.py:
from __future__ import unicode_literals
from django.db import models
from storage.choices import *
# Create your models here.
class Equipment(models.Model):
name = models.CharField(max_length=30)
fabricator = models.CharField(max_length=30, default='-')
storeplace = models.IntegerField()
labor = models.CharField(max_length=1, choices=labor_choices)
event = models.ManyToManyField('Order', blank = True, through= 'Assignment', through_fields=('Equipment', 'Order'))
max_quantity = models.IntegerField(default=1, null = True)
status = models.CharField(max_length=8, choices = STATUS_CHOICES, default = 'im Lager')
def __str__(self):
return self.name
class client(models.Model):
firstname = models.CharField(max_length=30)
secondname = models.CharField(max_length=30)
email = models.EmailField()
post_code = models.IntegerField()
city = models.CharField(max_length=30)
street= models.CharField(max_length=30)
def __str__(self):
return "%s %s" % (self.firstname, self.secondname)
class Order(models.Model):
name = models.CharField(max_length=30)
Type = models.CharField(
max_length=2,
choices=TYPE_CHOICES,
default='Rental',
)
city = models.CharField(max_length=30)
street= models.CharField(max_length=30)
date = models.DateField()
GuestNumber = models.IntegerField()
description = models.TextField()
client = models.ForeignKey("client", on_delete=models.CASCADE, blank = True, null = True)
status = models.CharField(max_length=30, choices=order_choices, default='glyphicon glyphicon-remove')
def __str__(self):
return self.name
class Assignment(models.Model):
Equipment = models.ForeignKey('Equipment', on_delete=models.CASCADE)
Order = models.ForeignKey('Order', on_delete=models.CASCADE)
quantity = models.PositiveIntegerField(default=1)
Thanks in Advance.
DRF does not support create method for nested serializers. If you want to show related fields in an extended layout and not only with pks then you can override the to_representation method instead of rewriting default mtm field. You should also override a create method, because of the third model in mtm link:
class EquipmentSerializer(serializers.ModelSerializer):
class Meta:
model = Equipment
fields = '__all__'
def create(self, validated_data):
order = Order.objects.get(pk=validated_data.pop('event'))
instance = Equipment.objects.create(**validated_data)
Assignment.objects.create(Order=order, Equipment=instance)
return instance
def to_representation(self, instance):
representation = super(EquipmentSerializer, self).to_representation(instance)
representation['assigment'] = AssignmentSerializer(instance.assigment_set.all(), many=True).data
return representation
Now it'll save mtm fields properly passing list of pks, like [1, 2, 3] and for representation of that mtm related model, EquipmentSerializer will use AssignmentSerializer.
Maybe for most people who is having the same issue, this question is a quite long.
The short answer is that DRF does not support natively create method for nested serializers. so what to do?
Simply overriding the default behaviour. Check out a full example in the Official DRF docs
I had a similar problem but with the update() method ...
The solution was simple thanks to this thread: https://github.com/beda-software/drf-writable-nested/issues/104...
All I had to do was installing the library pip install drf-writable-nested
and import it:
from drf_writable_nested import WritableNestedModelSerializer
the code should look like this:
(credit to: https://github.com/Leonardoperrella)
--serializers.py--
from drf_writable_nested import WritableNestedModelSerializer
class ProductsSerializer(serializers.ModelSerializer):
class Meta:
model = Products
fields = ('name', 'code', 'price')
class VendorsSerializer(WritableNestedModelSerializer,
serializers.ModelSerializer):
products = ProductsSerializer(source='vendor', many=True)
class Meta:
model = Vendors
fields = ('name', 'cnpj', 'city', 'products')
--models.py--
class Vendors(models.Model):
name = models.CharField('Name', max_length=50)
cnpj = models.CharField('CNPJ', max_length=14, unique=True, validators=[validate_cnpj])
city = models.CharField('City', max_length=100, blank=True)
class Meta:
verbose_name = "Vendor"
verbose_name_plural = "Vendors"
def __str__(self):
return self.name
class Products(models.Model):
name = models.CharField('Name', max_length=60)
code = models.CharField('Code', max_length=13)
price = models.DecimalField('Price',
max_digits=15,
decimal_places=2,
default=0.00,
validators=[MinValueValidator(Decimal("0.01"))])
vendor = models.ForeignKey('Vendors', on_delete=models.CASCADE, related_name='vendor')
class Meta:
verbose_name = "Product"
verbose_name_plural = "Products"
def __str__(self):
return self.name
I think that the cause for error: JSON parse error - No JSON object could be decoded is because you forgot to put .data at the 2nd line from #Ivan Semochkin solution: representation['assigment'] = AssignmentSerializer(instance.assigment_set.all(), many=True).data.
Thus I find out that I will stumble upon Keyword Error: 'event' from line: representation = super(EquipmentSerializer, self).to_representation(instance) since the EquipmentSeralizer object contain the intermediary assignment_set instead of event.
Here are the end result adapting from #Ivan Semochkin solution I do. Correct me if I'm wrong/inappropriate in practice.
class EquipmentSerializer(serializers.ModelSerializer):
class Meta:
model = Equipment
fields = '__all__'
def create(self, validated_data):
order = Order.objects.get(pk=validated_data.pop('assignment_set').get('id'))
instance = Equipment.objects.create(**validated_data)
Assignment.objects.create(Order=order, Equipment=Equipment)
return instance
def to_representation(self, instance):
representation = super(EquipmentSerializer, self).to_representation(instance)
representation['assigment'] = AssignmentSerializer(instance.assigment_set.all(), many=True).data
return representation
Please correct me if I'm wrong. I'm new to Django.
I have a Django REST project in which I used the DjangoFilterBackend from django-rest-framework-filtersgithub link.
This backend works well for some of my models which has ForeignKey & ManyToMany relations, however, it just doesn't work for some other models and always gives me an AttributeError: 'NoneType' object has no attribute 'verbose_name'.
my model:
from django.db import models
from authuser.models import AbstractUser, BaseUserManager
from corporation.models import Corporation
class Organization(AbstractUser):
company = models.ForeignKey(Corporation, verbose_name='company',
related_name='organizations',
null=True, on_delete=models.SET_NULL)
contact_name = models.CharField(max_length=50, verbose_name='contact_name', default='')
contact_phone = models.CharField(max_length=50, null=True, verbose_name='contact_phone')
contact_email = models.EmailField(max_length=50, verbose_name='contact_email', default='')
about_us = models.TextField(null=True, verbose_name='about_us')
info_completed = models.BooleanField(default=False, verbose_name='info_completed')
objects = BaseUserManager()
class Meta:
ordering = ('date_joined',)
my views:
class OrganizationDetailView(generics.RetrieveUpdateDestroyAPIView):
serializer_class = OrganizationSerializer
queryset = Organization.objects.all()
class OrganizationListCreateView(generics.ListCreateAPIView):
queryset = Organization.objects.all()
permission_classes = ()
def get_serializer_class(self):
if self.request.method == "POST":
return CreateOrganizationSerializer
return OrganizationSerializer
filter_backends = (
rest_backends.DjangoFilterBackend,
filters.SearchFilter,
filters.OrderingFilter,
)
filter_fields = (
'contact_name',
)
filter_class = OrganizationFilter
search_fields = (
# 'company',
'contact_name',
'contact_email', 'contact_phone',
'description',
)
ordering = ('date_joined')
my filterset:
class OrganizationFilter(filters.FilterSet):
company = filters.RelatedFilter(CorporationFilter, name='company')
description = filters.AllLookupsFilter(name='description')
contact_name = filters.AllLookupsFilter(name='contact_name')
contact_phone = filters.AllLookupsFilter(name='contact_phone')
contact_email = filters.AllLookupsFilter(name='contact_email')
class Meta:
model = Organization
fields = (
'company',
'description', 'contact_name', 'contact_phone',
'contact_email',
)
my serializer:
class OrganizationSerializer(serializers.ModelSerializer):
company = serializers.PrimaryKeyRelatedField(queryset=Corporation.objects.all())
class Meta:
model = Organization
fields = ('id', 'company',
'contact_name', 'contact_email',
'contact_phone', 'info_completed')
The CRUD operations are fine. It just gives me the attribute error when listing organizations:
Creating test database for alias 'default'...
E.........
======================================================================
ERROR: test_list_organizations (organization.tests.SimpleTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "F:\landi-django\organization\tests.py", line 73, in test_list_organizati
ons
response = client.get('/organizations?contact_name=Tingtao')
File "F:\landi-django\env\lib\site-packages\rest_framework\test.py", line 160,
in get
response = super(APIClient, self).get(path, data=data, **extra)
File "F:\landi-django\env\lib\site-packages\rest_framework\test.py", line 86,
in get
return self.generic('GET', path, **r)
File "F:\landi-django\env\lib\site-packages\rest_framework\compat.py", line 18
9, in generic
return self.request(**r)
File "F:\landi-django\env\lib\site-packages\rest_framework\test.py", line 157,
in request
return super(APIClient, self).request(**kwargs)
File "F:\landi-django\env\lib\site-packages\rest_framework\test.py", line 109,
in request
request = super(APIRequestFactory, self).request(**kwargs)
File "F:\landi-django\env\lib\site-packages\django\test\client.py", line 440,
in request
six.reraise(*exc_info)
File "F:\landi-django\env\lib\site-packages\django\core\handlers\base.py", lin
e 111, in get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "F:\landi-django\env\lib\site-packages\django\views\decorators\csrf.py",
line 57, in wrapped_view
return view_func(*args, **kwargs)
File "F:\landi-django\env\lib\site-packages\django\views\generic\base.py", lin
e 69, in view
return self.dispatch(request, *args, **kwargs)
File "F:\landi-django\env\lib\site-packages\rest_framework\views.py", line 407
, in dispatch
response = self.handle_exception(exc)
File "F:\landi-django\env\lib\site-packages\rest_framework\views.py", line 404
, in dispatch
response = handler(request, *args, **kwargs)
File "F:\landi-django\env\lib\site-packages\rest_framework\generics.py", line
311, in get
return self.list(request, *args, **kwargs)
File "F:\landi-django\env\lib\site-packages\rest_framework\mixins.py", line 40
, in list
instance = self.filter_queryset(self.get_queryset())
File "F:\landi-django\env\lib\site-packages\rest_framework\generics.py", line
144, in filter_queryset
queryset = backend().filter_queryset(self.request, queryset, self)
File "F:\landi-django\env\lib\site-packages\rest_framework_filters\backends.py
", line 45, in filter_queryset
_filter = filter_class(request.QUERY_PARAMS, queryset=queryset)
File "F:\landi-django\env\lib\site-packages\rest_framework_filters\filterset.p
y", line 61, in __init__
f = self.filter_for_field(field, filter_.name)
File "F:\landi-django\env\lib\site-packages\django_filters\filterset.py", line
400, in filter_for_field
'label': capfirst(f.verbose_name),
AttributeError: 'NoneType' object has no attribute 'verbose_name'
----------------------------------------------------------------------
I also tried commenting line 400 in django_filters\filterset.py, but it gives me more errors.
Many thanks for your help!
On your OrganizationFilter, you are defining a description field that can be used to filter down to Organization objects. The description field does not exist on the Organization model though, so django-filter is not able to filter the queryset based on that field. You do appear to have an about_us field, which I'm guessing is what you meant to use.
You can fix this issue by either renaming the field on the OrganizationFilter to about_us, or setting the name on the description field to be about_us, so django-filter uses that field instead.
class OrganizationFilter(filters.FilterSet):
company = filters.RelatedFilter(CorporationFilter, name='company')
about_us = filters.AllLookupsFilter(name='about_us')
contact_name = filters.AllLookupsFilter(name='contact_name')
contact_phone = filters.AllLookupsFilter(name='contact_phone')
contact_email = filters.AllLookupsFilter(name='contact_email')
class Meta:
model = Organization
fields = (
'company',
'description', 'contact_name', 'contact_phone',
'contact_email',
)
Either of these changes should make it work without throwing an error.