How solve the problem with clean function in django - python

I'm building a booking website, where customer can book an appointment on a day of his/her choice and the time of appointment is selected from the available slots with the help of drop-down menu. The problem is if a time slot has already been booked by someone it should not be available and an error message should be shown to the customer.
I have written a clean function to perform the check. It gives and error as follows :-
No booking on 2019-06-08
Internal Server Error: /
Traceback (most recent call last):
File "/home/gireesh/PycharmProjects/astrobookinenv/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/home/gireesh/PycharmProjects/astrobookinenv/lib/python3.6/site-packages/django/core/handlers/base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/home/gireesh/PycharmProjects/astrobookinenv/lib/python3.6/site-packages/django/core/handlers/base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/gireesh/PycharmProjects/astrobookinenv/lib/python3.6/site-packages/django/views/generic/base.py", line 71, in view
return self.dispatch(request, *args, **kwargs)
File "/home/gireesh/PycharmProjects/astrobookinenv/lib/python3.6/site-packages/django/views/generic/base.py", line 97, in dispatch
return handler(request, *args, **kwargs)
File "/home/gireesh/PycharmProjects/astrobookinenv/lib/python3.6/site-packages/django/views/generic/edit.py", line 172, in post
return super().post(request, *args, **kwargs)
File "/home/gireesh/PycharmProjects/astrobookinenv/lib/python3.6/site-packages/django/views/generic/edit.py", line 141, in post
if form.is_valid():
File "/home/gireesh/PycharmProjects/astrobookinenv/lib/python3.6/site-packages/django/forms/forms.py", line 185, in is_valid
return self.is_bound and not self.errors
File "/home/gireesh/PycharmProjects/astrobookinenv/lib/python3.6/site-packages/django/forms/forms.py", line 180, in errors
self.full_clean()
File "/home/gireesh/PycharmProjects/astrobookinenv/lib/python3.6/site-packages/django/forms/forms.py", line 383, in full_clean
self._post_clean()
File "/home/gireesh/PycharmProjects/astrobookinenv/lib/python3.6/site-packages/django/forms/models.py", line 403, in _post_clean
self.instance.full_clean(exclude=exclude, validate_unique=False)
File "/home/gireesh/PycharmProjects/astrobookinenv/lib/python3.6/site-packages/django/db/models/base.py", line 1181, in full_clean
self.clean_fields(exclude=exclude)
File "/home/gireesh/PycharmProjects/astrobookinenv/lib/python3.6/site-packages/django/db/models/base.py", line 1223, in clean_fields
setattr(self, f.attname, f.clean(raw_value, self))
File "/home/gireesh/PycharmProjects/astrobookinenv/lib/python3.6/site-packages/django/db/models/fields/__init__.py", line 629, in clean
value = self.to_python(value)
File "/home/gireesh/PycharmProjects/astrobookinenv/lib/python3.6/site-packages/django/db/models/fields/__init__.py", line 2187, in to_python
parsed = parse_time(value)
File "/home/gireesh/PycharmProjects/astrobookinenv/lib/python3.6/site-packages/django/utils/dateparse.py", line 89, in parse_time
match = time_re.match(value)
TypeError: expected string or bytes-like object
The Code for the clean function :-
def clean_booking_time(self):
booked_time = self.cleaned_data['booking_time']
booked_date = self.cleaned_data['booking_date']
# count = Booking.objects.filter(booking_date=booked_date).filter(booking_time=booked_time).count()
count_date = Booking.objects.filter(booking_date=booked_date).count()
if count_date == 0:
print("No booking on ",booked_date)
return self.cleaned_data
else:
count_time = Booking.objects.filter(booking_date=booked_date).filter(booking_time=booked_time).count()
if count_time != 0:
error_message = "%s is not available !" % booked_time
raise ValidationError(error_message)
else:
return self.cleaned_data
If I book an appointment by removing the clean it'll save successfully and then if I add the code back and try to book on the same time the code works fine and throws the error. It only seem not to work when the date has no booking. Problem might be with the ORM I have written.

You're returning the cleaned_data dict in a field-specific clean method. You are supposed to return just the value for that field.
However it seems that this should actually be the overall clean() method, since it references multiple fields. That method is meant to return the whole dict.
Just rename the method to clean.

You should return the booking_time data for the clean_booking_time function:
def clean_booking_time(self):
booked_time = self.cleaned_data['booking_time']
booked_date = self.cleaned_data['booking_date']
booking_exists = Booking.objects.filter(
booking_date=booked_date,
booking_time=booked_time
).exists()
if booking_exists:
error_message = "%s is not available !" % booked_time
raise ValidationError(error_message)
return booked_time
It furthermore is a bit odd that you clean a specific field here, since your cleaning constrains the relation between two fields.
That being said, you might want to consider a DateTimeField [Django-doc] that contains both the time and the date.

Related

Test of Django ProfileListView fails with ValueError: Cannot assign "<SimpleLazyObject:....>": "Profile.user" must be a "User" instance

I am a Django beginner and a SOF newbie, sorry if this question sounds a silly to some.
I am struggling with my integration tests.
In my app I have a one-to-one User/Profile relationship. I have a list view to show registered users' profile data:
class ProfileListView(views.ListView, LoginRequiredMixin):
model = Profile
template_name = 'registration/profile_list.html'
paginate_by = 8
def get_context_data(self, *, object_list=None, **kwargs):
context = super(ProfileListView, self).get_context_data()
# superuser raises DoesNotExist at /accounts/profiles/ as they are created with createsuperuser in manage.py
# hence are not assigned a profile automatically => create profile for them here
try:
context['current_profile'] = Profile.objects.get(pk=self.request.user.pk)
except ObjectDoesNotExist:
Profile.objects.create(user=self.request.user)
context['current_profile'] = Profile.objects.get(pk=self.request.user.pk)
# get all other users' profiles apart from staff and current user
regular_users = User.objects \
.filter(is_superuser=False, is_staff=False, is_active=True) \
.exclude(pk=self.request.user.pk)
context['non_staff_active_profiles'] = Profile.objects.filter(user__in=regular_users)
return context
I want to test the get_context_data() method to ensure it returns:
correct logged in user
correct queryset of non-staff profiles
My test breaks as soon as I try something like:
response = self.client.get('/accounts/profiles/')
I understand I need to pass user/profile data to the client but I could not figure out how to do that. It looks like it fails because of context['current_profile'] = Profile.objects.get(pk=self.request.user.pk) and I have no idea why.
The whole 'test' is below:
def test_view_get_context_data__should_return_correct_context(self):
new_user = User.objects.create_user(**self.VALID_USER_DATA_1)
# create profile
new_profile = Profile.objects.create(user=new_user)
# test profile
self.assertEqual(new_profile.user_id, new_user.pk)
response = self.client.get('/accounts/profiles/')
It fails with:
/home/kk/Documents/Github/Phonotheque/venv/bin/python /snap/pycharm-professional/280/plugins/python/helpers/pycharm/django_test_manage.py test Phonotheque.accounts_app.tests.views.test_ProfileListView.ProfilesListViewTests.test_view_get_context_data__should_return_correct_context /home/kk/Documents/Github/Phonotheque
Testing started at 15:54 ...
Found 1 test(s).
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/views/generic/list.py:91: UnorderedObjectListWarning: Pagination may yield inconsistent results with an unordered object_list: <class 'Phonotheque.accounts_app.models.Profile'> QuerySet.
return self.paginator_class(
Destroying test database for alias 'default'...
Error
Traceback (most recent call last):
File "/home/kk/Documents/Github/Phonotheque/Phonotheque/accounts_app/views.py", line 138, in get_context_data
context['current_profile'] = Profile.objects.get(pk=self.request.user.pk)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/db/models/query.py", line 496, in get
raise self.model.DoesNotExist(
Phonotheque.accounts_app.models.Profile.DoesNotExist: Profile matching query does not exist.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/kk/Documents/Github/Phonotheque/Phonotheque/accounts_app/tests/views/test_ProfileListView.py", line 65, in test_view_get_context_data__should_return_correct_context
response = self.client.get('/accounts/profiles/')
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/test/client.py", line 836, in get
response = super().get(path, data=data, secure=secure, **extra)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/test/client.py", line 424, in get
return self.generic(
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/test/client.py", line 541, in generic
return self.request(**r)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/test/client.py", line 810, in request
self.check_exception(response)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/test/client.py", line 663, in check_exception
raise exc_value
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/core/handlers/base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/views/generic/base.py", line 84, in view
return self.dispatch(request, *args, **kwargs)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/views/generic/base.py", line 119, in dispatch
return handler(request, *args, **kwargs)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/views/generic/list.py", line 174, in get
context = self.get_context_data()
File "/home/kk/Documents/Github/Phonotheque/Phonotheque/accounts_app/views.py", line 140, in get_context_data
Profile.objects.create(user=self.request.user)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/db/models/query.py", line 512, in create
obj = self.model(**kwargs)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/db/models/base.py", line 541, in __init__
_setattr(self, field.name, rel_obj)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/db/models/fields/related_descriptors.py", line 338, in __set__
super().__set__(instance, value)
File "/home/kk/Documents/Github/Phonotheque/venv/lib/python3.8/site-packages/django/db/models/fields/related_descriptors.py", line 235, in __set__
raise ValueError(
ValueError: Cannot assign "<SimpleLazyObject: <django.contrib.auth.models.AnonymousUser object at 0x7f682f11e220>>": "Profile.user" must be a "User" instance.
Process finished with exit code 1
How do I simulate creation of multiple users/profiles, logging in of one of them and obtaining the relevant data?
Thanks a lot in advance.
It was quite a specific question and I very much doubt anyone ever will be looking at this answer but just in case:
def test_view_get_context_data__with__logged_in_user_should_return_correct_context(self):
user_data = {'username': 'BayHuy', 'password': '11111111', }
new_user = User.objects.create_user(**user_data)
new_profile = Profile.objects.create(user=new_user)
self.assertEqual(len(User.objects.all()), 1)
self.assertEqual(new_profile.user_id, new_user.pk)
self.assertEqual(len(Profile.objects.all()), 1)
self.client.login(**user_data)
response = self.client.get(reverse('profiles-list'))
self.assertEqual(
new_profile,
response.context_data['current_profile'])
self.assertEqual(
new_profile, response.context_data['current_profile'])
self.assertEqual(len(response.context_data['profile_list']), 1)

Django TypeError: Object of type User is not JSON serializable

So I was trying to make an ajax call in django but this happend
Internal Server Error: /ajax/new_notification/
Traceback (most recent call last):
File "C:\Users\berna\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\berna\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\core\handlers\base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File "C:\Users\berna\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\core\handlers\base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\berna\Desktop\Python & Javascript\Web development\MiFamiliaEsUnDesastre\mifamiliaesundesastre\chat\views.py", line 71, in new_notification
return JsonResponse(user, safe=False)
File "C:\Users\berna\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\http\response.py", line 561, in __init__
data = json.dumps(data, cls=encoder, **json_dumps_params)
File "C:\Users\berna\AppData\Local\Programs\Python\Python38-32\lib\json\__init__.py", line 234, in dumps
return cls(
File "C:\Users\berna\AppData\Local\Programs\Python\Python38-32\lib\json\encoder.py", line 199, in encode
chunks = self.iterencode(o, _one_shot=True)
File "C:\Users\berna\AppData\Local\Programs\Python\Python38-32\lib\json\encoder.py", line 257, in iterencode
return _iterencode(o, 0)
File "C:\Users\berna\AppData\Local\Programs\Python\Python38-32\lib\site-packages\django\core\serializers\json.py", line 104, in default
return super().default(o)
File "C:\Users\berna\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
HTTP GET /ajax/new_notification/ 500 [0.15, 127.0.0.1:64660]
the code that is raising this error is here, also I am new in django so maybe the solution can be ver obvious
def new_notification(request):
user = request.user
user.profile.notifications = user.profile.notifications + 1
user.save()
return JsonResponse(user, safe=False)
thank you for the help
Well, the error is rather self-explanatory:
TypeError: Object of type User is not JSON serializable
Unfortunately, user from within the request object (not necessarily 'user' model, correct me if I'm wrong here) is not JSON-serializable by default. Simplest solution would be to map only necessary fields:
return JsonResponse({ "notifications": profile.notifications }, safe=True)
But that's rather naive. Instead, maybe django serializers could do the job:
from django.core import serializers
def new_notification(request):
user = request.user
user.profile.notifications = user.profile.notifications + 1
user.save()
return JsonResponse(serializers.serialize('json', user), safe=False)

Using django_xhtml2pdf with django 1.11" error: context must be a dict rather than Context."

Per one of the comments: I did change my code to:
providers = Provider.objects.all()
context = { 'providers':providers}
I know it didn't make a difference but figured I would try it anyway cause stranger things have happened. I am worried the error is within the module itself running on my version of django here.
I did see the other answers on this, and it confused me because I am just using what was documented here:
https://spapas.github.io/2015/11/27/pdf-in-django/#django-integration
for getting the django_xhtml2pdf stuff to work. My view is as such:
def providers_plain_old_view(request):
resp = HttpResponse(content_type='application/pdf')
context = {
'providers': Provider.objects.all()
}
result = generate_pdf('ipaswdb/provider/providers_plain_old_view.html', file_object=resp, context=context)
return result
Which I know now is bad in django 1.11.14 I am using, but no idea how to fix the error:
Traceback (most recent call last):
File "D:\Python27\lib\site-packages\django\core\handlers\exception.py", line 41, in inner
response = get_response(request)
File "D:\Python27\lib\site-packages\django\core\handlers\base.py", line 249, in _legacy_get_response
response = self._get_response(request)
File "D:\Python27\lib\site-packages\django\core\handlers\base.py", line 187, in _get_response
response = self.process_exception_by_middleware(e, request)
File "D:\Python27\lib\site-packages\django\core\handlers\base.py", line 185, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "D:\Programming\web\ipa_django\mysite\ipaswdb\views.py", line 312, in providers_plain_old_view
result = generate_pdf('ipaswdb/provider/providers_plain_old_view.html', file_object=resp, context=context)
File "D:\Python27\lib\site-packages\django_xhtml2pdf\utils.py", line 62, in generate_pdf
generate_pdf_template_object(tmpl, file_object, context)
File "D:\Python27\lib\site-packages\django_xhtml2pdf\utils.py", line 39, in generate_pdf_template_object
html = template_object.render(Context(context))
File "D:\Python27\lib\site-packages\django\template\backends\django.py", line 64, in render
context = make_context(context, request, autoescape=self.backend.engine.autoescape)
File "D:\Python27\lib\site-packages\django\template\context.py", line 287, in make_context
raise TypeError('context must be a dict rather than %s.' % context.__class__.__name__)
TypeError: context must be a dict rather than Context.
"GET /ipaswdb/provider_roster/ HTTP/1.1" 500 86485
I mean it wants me to call the generate_pdf function a different way in the latest django version?
The main issue lies in the line
File "D:\Python27\lib\site-packages\django_xhtml2pdf\utils.py", line 39, in generate_pdf_template_object
html = template_object.render(Context(context))
in the error output. This is an issue the django-xhtml2pdf package not being up to date for 1.11. The call to render has changed from
html = template_object.render(Context(context))
to
html = template_object.render(context)
according to the upgrading to 1.11 notes https://docs.djangoproject.com/en/1.11/ref/templates/upgrading/
django.template.loader section.
You can either wait for them to fix it, by submitting a bug report or implement the functionality the package provides in your views.py

Modify DRF Request objects for dispatch to other views

Context
AWS Elastic Beanstalk Worker tiers can be configured to accept a single AWS SQS queue automatically. Incoming messages on this queue can be routed to a single endpoint which some instance in the worker tier will respond to; they show up to the workers as POST events.
The software my workers are running is based on Django/DRF.
Goal
I want my worker tier instances to be able to handle more than one type of incoming event. Ideally, SQS could be configured to deliver the requests to more than one endpoint, but that appears to be impossible.
My implementation
class DispatchSerializer(serializers.Serializer):
"`to` is the name of the destination endpoint; `payload` is the data I want delivered"
to = serializers.CharField(
max_length=80,
)
payload = serializers.JSONField()
#api_view(http_method_names=['POST'])
#permission_classes([AllowAny])
def dispatch(request):
"""
Dispatch a request to a sibling endpoint.
"""
routing_table = {
... # this is specific to my application;
# it's just a dict of names to views
}
serializer = DispatchSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
to = serializer.validated_data['to']
try:
view = routing_table[to]
except KeyError:
raise Http404()
# https://github.com/encode/django-rest-framework/blob/master/rest_framework/request.py#L183-L187
request._full_data = serializer.validated_data['payload']
return view(request)
# If this returns other than 200, SQS simply re-attempts the request later.
# No returned data is preserved, so we might as well not return any.
return Response()
As you can see, I attempt to simply replace _full_data attribute of the request with the payload, so that the inner view sees only the data intended for it (which to the dispatch view is the payload).
The problem
This implementation doesn't actually work. I get errors of this form:
Traceback (most recent call last):
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/request.py", line 378, in __getattribute__
return super(Request, self).__getattribute__(attr)
AttributeError: 'Request' object has no attribute 'body'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/request.py", line 378, in __getattribute__
return super(Request, self).__getattribute__(attr)
AttributeError: 'Request' object has no attribute 'body'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/django/core/handlers/exception.py", line 39, in inner
response = get_response(request)
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/django/core/handlers/base.py", line 187, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/django/core/handlers/base.py", line 185, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
return view_func(*args, **kwargs)
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/django/views/generic/base.py", line 68, in view
return self.dispatch(request, *args, **kwargs)
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/views.py", line 483, in dispatch
response = self.handle_exception(exc)
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/views.py", line 443, in handle_exception
self.raise_uncaught_exception(exc)
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/views.py", line 480, in dispatch
response = handler(request, *args, **kwargs)
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/decorators.py", line 52, in handler
return func(*args, **kwargs)
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/project/django-backend/cardamom/worker/views.py", line 196, in dispatch
return view(request)
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
return view_func(*args, **kwargs)
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/django/views/generic/base.py", line 68, in view
return self.dispatch(request, *args, **kwargs)
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/views.py", line 483, in dispatch
response = self.handle_exception(exc)
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/views.py", line 443, in handle_exception
self.raise_uncaught_exception(exc)
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/views.py", line 480, in dispatch
response = handler(request, *args, **kwargs)
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/decorators.py", line 52, in handler
return func(*args, **kwargs)
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/project/django-backend/cardamom/core/views/mailchimp.py", line 229, in wrapper
return func(*args, **kwargs)
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/project/django-backend/cardamom/worker/views.py", line 52, in wrapper
result = f(log_entry, *args, **kwargs)
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/project/django-backend/cardamom/worker/views.py", line 106, in match_one
user_id = int(request.data['user_id'])
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/request.py", line 378, in __getattribute__
return super(Request, self).__getattribute__(attr)
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/request.py", line 186, in data
self._load_data_and_files()
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/request.py", line 246, in _load_data_and_files
self._data, self._files = self._parse()
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/request.py", line 290, in _parse
stream = self.stream
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/request.py", line 378, in __getattribute__
return super(Request, self).__getattribute__(attr)
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/request.py", line 173, in stream
self._load_stream()
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/request.py", line 270, in _load_stream
self._stream = six.BytesIO(self.body)
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/request.py", line 382, in __getattribute__
return getattr(self._request, attr)
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/rest_framework/request.py", line 382, in __getattribute__
return getattr(self._request, attr)
File "/mnt/d/Users/coriolinus/Documents/Toptal/cardamom/.venv/lib/python3.4/site-packages/django/http/request.py", line 264, in body
raise RawPostDataException("You cannot access body after reading from request's data stream")
django.http.request.RawPostDataException: You cannot access body after reading from request's data stream
Problem restatement
The request object is complicated; generating a new one is implausible. At the same time, we need to modify the data of this one in order to properly pass it along to the next view, so that view only sees the data in its expected format.
How can I accomplish this?
[edit] Passing an un-modified request doesn't work either
I tried editing the above example to remove any modification of the request object, and instead created the following serializer mixin:
class UndispatcherMixin:
"""
Adjust a serializer such that it can accept its normal serialization,
or alternately the payload of a DispatchSerializer.
"""
def is_valid(self, raise_exception=False):
if not hasattr(self, '_validated_data'):
assert hasattr(self, 'initial_data'), (
'Cannot call `.is_valid()` as no `data=` keyword argument was '
'passed when instantiating the serializer instance.'
)
ds = DispatchSerializer(data=self.initial_data)
if ds.is_valid(raise_exception=False):
self.initial_data = ds.validated_data['payload']
return super().is_valid(raise_exception=raise_exception)
This mixin was then added to the relevant serializers used by the downstream views. Interestingly, it failed with the same django.http.RawPostDataException as before.
It's received wisdom all over SO that you can just call your views as normal functions, so long as you pass in a proper request object. This may not actually be true, at least for the case where the view was created by the DRF #api_view decorator.
Still working on a way to actually solve this.
For whatever reason--this may be an error in DRF, but I'm not sure--you can't just call an #api_view function from within another one and pass along the request object. It just doesn't work.
What does work is to factor out the code which does the actual work, and call it separately from the #api_view function and also the dispatch view. That is, something like this:
def perform_action(request, data):
"""
Do something
`request` is included for completeness in case it's necessary,
but the data normally read from `request.data` should be read instead
from the `data` argument.
"""
serializer = ActionSerializer(data=data)
if serializer.is_valid():
... # Whatever your action should be, goes here
#api_view(http_method_names=['POST'])
#permission_classes([AllowAny])
def action(request)
perform_action(request, request.data)
return Response() # 200, no content
#api_view(http_method_names=['POST'])
#permission_classes([AllowAny])
def dispatch(request):
"""
Dispatch a request to a sibling endpoint.
"""
routing_table = {
'action': perform_action, # etc...
}
serializer = DispatchSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
to = serializer.validated_data['to']
try:
view = routing_table[to]
except KeyError:
raise Http404()
view(request, serializer.validated_data['payload'])
return Response()

Where has cleaned_data vanished in Django 1.11?

I have created an inlineformset_factory as below :
formset = inlineformset_factory(Author, Book, form=BookForm,
formset=BaseBookFormSet,
can_order=False, can_delete=True,
extra=1, fields=('id', name)
)
BookForm is as below:
class BookForm(forms.ModelForm):
name = forms.Charfield(required=True)
def __init__(self, *args, **kwargs):
super(BookForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.layout = Layout(
Div(
Field("id", type="hidden"),
Field("name"),
Field("DELETE")
)
)
class Meta:
model = Book
fields = ('id', 'name')
def clean_name(self):
book_name = self.cleaned_data['name']
try:
book = Book.objects.get(name=book_name)
return book
except:
return book_name
def clean(self):
cleaned_data = super(BookForm, self).clean()
... other operations on cleaned_data ...
def has_changed(self):
changed = super(BookForm, self).has_changed()
cleaned_data = self.clean()
... other code here ...
This is throwing an error on submitting the form :
Exception Type: AttributeError
Exception Value: 'BookForm' object has no attribute 'cleaned_data'
when formset.is_valid() is called in views.py. Traceback first shows the line in has_changed where the self.clean is being called, and then the line in clean() where the super clean is being called.
This used to work fine in django 1.10.
When I tried printing dir(self) in Django 1.10 it does show 'cleaned_data' as one of the attributes where as in Django 1.11 it does not.
Where has the 'cleaned_data' vanished in Django 1.11?
EDIT: Adding traceback:
Traceback (most recent call last):
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/core/handlers/exception.py", line 41, in inner
response = get_response(request)
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 249, in _legacy_get_response
response = self._get_response(request)
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 187, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 185, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/views/generic/base.py", line 68, in view
return self.dispatch(request, *args, **kwargs)
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/views/generic/base.py", line 88, in dispatch
return handler(request, *args, **kwargs)
File "/vagrant/test_os/inventory/views.py", line 297, in post
if formset.is_valid():
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/forms/formsets.py", line 321, in is_valid
self.errors
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/forms/formsets.py", line 295, in errors
self.full_clean()
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/forms/formsets.py", line 345, in full_clean
if not form.has_changed():
File "/vagrant/test_os/inventory/forms.py", line 220, in has_changed
cleaned_data = self.clean()
File "/vagrant/test_os/inventory/forms.py", line 177, in clean
cleaned_data = super(BookForm, self).clean()
File "/home/vagrant/venv/local/lib/python2.7/site-packages/django/forms/models.py", line 344, in clean
return self.cleaned_data
AttributeError: 'BookForm' object has no attribute 'cleaned_data'
Formsets were fixed in 1.11 (in #26844) to ignore empty forms when validating the minimum number of forms. As a side-effect, formsets now call form.has_changed() on each form before validating the form. Django expects form.has_changed() to be safe to call before the form is validated, and the default implementation is indeed safe to call.
You have overridden form.has_changed() to call self.clean(), which now happens before the form is validated. Since form.clean() requires that the form is validated, this fails.
Since form.full_clean() actually calls self.has_changed(), you can't simply validate the form from within form.has_changed(). You don't show what you do in has_changed(), but it would most likely be a good idea to put this code elsewhere.

Categories