Unit Testing a Django Form with a ImageField without external file - python

django version: 1.11, python version: 3.6.3
I found this stackoverflow question:
Unit Testing a Django Form with a FileField
and I like how there isn't an actual image/external file used for the unittest; however I tried these approaches:
from django.test import TestCase
from io import BytesIO
from PIL import Image
from my_app.forms import MyForm
from django.core.files.uploadedfile import InMemoryUploadedFile
class MyModelTest(TestCase):
def test_valid_form_data(self):
im_io = BytesIO() # BytesIO has to be used, StrinIO isn't working
im = Image.new(mode='RGB', size=(200, 200))
im.save(im_io, 'JPEG')
form_data = {
'some_field': 'some_data'
}
image_data = {
InMemoryUploadedFile(im_io, None, 'random.jpg', 'image/jpeg', len(im_io.getvalue()), None)
}
form = MyForm(data=form_data, files=image_data)
self.assertTrue(form.is_valid())
however, this always results in the following error message:
Traceback (most recent call last):
File "/home/my_user/projects/my_app/products/tests/test_forms.py", line 44, in test_valid_form_data
self.assertTrue(form.is_valid())
File "/home/my_user/.virtualenvs/forum/lib/python3.6/site-packages/django/forms/forms.py", line 183, in is_valid
return self.is_bound and not self.errors
File "/home/my_user/.virtualenvs/forum/lib/python3.6/site-packages/django/forms/forms.py", line 175, in errors
self.full_clean()
File "/home/my_user/.virtualenvs/forum/lib/python3.6/site-packages/django/forms/forms.py", line 384, in full_clean
self._clean_fields()
File "/home/my_user/.virtualenvs/forum/lib/python3.6/site-packages/django/forms/forms.py", line 396, in _clean_fields
value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
File "/home/my_user/.virtualenvs/forum/lib/python3.6/site-packages/django/forms/widgets.py", line 423, in value_from_datadict
upload = super(ClearableFileInput, self).value_from_datadict(data, files, name)
File "/home/my_user/.virtualenvs/forum/lib/python3.6/site-packages/django/forms/widgets.py", line 367, in value_from_datadict
return files.get(name)
AttributeError: 'set' object has no attribute 'get'
Why? I understand that .get() is a dictionary method, but I fail to see where it created a set.

image_data should be dict, without providing key {value} will create set object. You need to define it like this {key: value}. Fix to this:
image_data = {
'image_field': InMemoryUploadedFile(im_io, None, 'random.jpg', 'image/jpeg', len(im_io.getvalue()), None)
}

Without External File Code
from django.core.files.uploadedfile import SimpleUploadedFile
testfile = (
b'\x47\x49\x46\x38\x39\x61\x01\x00\x01\x00\x00\x00\x00\x21\xf9\x04'
b'\x01\x0a\x00\x01\x00\x2c\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02'
b'\x02\x4c\x01\x00\x3b')
avatar = SimpleUploadedFile('small.gif', testfile, content_type='image/gif')

Related

binascii.Error: Incorrect padding in python django

I am trying to save the base64 encoded image in the django rest framework. First of all, we make a code to insert the base64 encoded image into the imagefield and test it, and the following error appears.
binascii.Error: Incorrect padding
What I don't understand is that I've used the same code before and there was no such error. Can you help me? Here is my code.
serializers.py
from rest_framework import serializers
from .models import post, comment
class Base64ImageField (serializers.ImageField) :
def to_internal_value (self, data) :
from django.core.files.base import ContentFile
import base64
import six
import uuid
if isinstance(data, six.string_types):
if 'data:' in data and ';base64,' in data :
header, data = data.split(';base64,')
try :
decoded_file = base64.b64decode(data)
except TypeError :
self.fail('invalid_image')
file_name = str(uuid.uuid4())[:12]
file_extension = self.get_file_extension(file_name, decoded_file)
complete_file_name = "%s.%s" % (file_name, file_extension, )
data = ContentFile(decoded_file, name=complete_file_name)
return super(Base64ImageField, self).to_internal_value(data)
def get_file_extension (self, file_name, decoded_file) :
import imghdr
extension = imghdr.what(file_name, decoded_file)
extension = "jpg" if extension == "jpeg" else extension
return extension
class commentSerializer (serializers.ModelSerializer) :
class Meta :
model = comment
fields = '__all__'
class postSerializer (serializers.ModelSerializer) :
author = serializers.CharField(source='author.username', read_only=True)
image1 = Base64ImageField(use_url=True)
image2 = Base64ImageField(use_url=True)
image3 = Base64ImageField(use_url=True)
image4 = Base64ImageField(use_url=True)
image5 = Base64ImageField(use_url=True)
comment = commentSerializer(many=True, read_only=True)
class Meta:
model = post
fields = ['pk', 'author', 'title', 'text', 'image1', 'image2', 'image3', 'image4', 'image5', 'tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'comment']
I'm not sure this applies to your situation, depending on where you're storing your encoded data.
I had the same error, but it related to some encoded session data. I cleared out the session data (cookies, cache etc) in the browser Devtools, and it fixed my issue.
Just posting this in case it applies or helps others who come along for the same reason.
Run following command in shell
from django.contrib.sessions.models import Session
Session.objects.all().delete()
More information at https://code.djangoproject.com/ticket/31592
I had the same error. I do all things clear cache but it doesn't work. Now change the browser to Mozilla. Now it's working.
I had the same issue. I guess it was caused by me using django 4.0.1 in the beginning and later switching back to django 2.2... (Maybe your issue was not caused by this but I just want to provide some idea on where the problem might be to all the reader who has this issue and visit this page.)
Django 4.0.1 should directly save the session data in string into the database but Django 2.2 saves and reads base64 encoded data into / from the same column session_data in table django_session in DB.
The string failed in base64.b64decode() in my case is .eJxVjEEOwiAQRe_C2pABCgWX7j0DmRlAqoYmpV0Z765NutDtf-_9l4i4rTVuPS9xSuIslDj9boT8yG0H6Y7tNkue27pMJHdFHrTL65zy83K4fwcVe_3WGtkEHfLg2IMroL1VZA0BFGMJPJhRkdEucypY2CfA7C2HgRHGor14fwDNWjfC:1nERxl:5jJRHXpQH7aZrf2-C99MnTIWARd_cUag76Xa2YjW1yw, which is obviously not valid base64 string since symbols : - . . do not exist in base64 character list at all.
My full traceback info is:
Internal Server Error: /admin
Traceback (most recent call last):
File "C:\Users\Public\django2.2\lib\site-packages\django\contrib\sessions\backends\base.py", line 189, in _get_session
return self._session_cache
AttributeError: 'SessionStore' object has no attribute '_session_cache'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\Public\django2.2\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
response = get_response(request)
File "C:\Users\Public\django2.2\lib\site-packages\django\core\handlers\base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File "C:\Users\Public\django2.2\lib\site-packages\django\core\handlers\base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "C:\Users\Public\django2.2\lib\site-packages\django\contrib\admin\sites.py", line 241, in wrapper
return self.admin_view(view, cacheable)(*args, **kwargs)
File "C:\Users\Public\django2.2\lib\site-packages\django\utils\decorators.py", line 142, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "C:\Users\Public\django2.2\lib\site-packages\django\views\decorators\cache.py", line 44, in _wrapped_view_func
response = view_func(request, *args, **kwargs)
File "C:\Users\Public\django2.2\lib\site-packages\django\contrib\admin\sites.py", line 212, in inner
if not self.has_permission(request):
File "C:\Users\Public\django2.2\lib\site-packages\django\contrib\admin\sites.py", line 186, in has_permission
return request.user.is_active and request.user.is_staff
File "C:\Users\Public\django2.2\lib\site-packages\django\utils\functional.py", line 256, in inner
self._setup()
File "C:\Users\Public\django2.2\lib\site-packages\django\utils\functional.py", line 392, in _setup
self._wrapped = self._setupfunc()
File "C:\Users\Public\django2.2\lib\site-packages\django\contrib\auth\middleware.py", line 24, in <lambda>
request.user = SimpleLazyObject(lambda: get_user(request))
File "C:\Users\Public\django2.2\lib\site-packages\django\contrib\auth\middleware.py", line 12, in get_user
request._cached_user = auth.get_user(request)
File "C:\Users\Public\django2.2\lib\site-packages\django\contrib\auth\__init__.py", line 182, in get_user
user_id = _get_user_session_key(request)
File "C:\Users\Public\django2.2\lib\site-packages\django\contrib\auth\__init__.py", line 59, in _get_user_session_key
return get_user_model()._meta.pk.to_python(request.session[SESSION_KEY])
File "C:\Users\Public\django2.2\lib\site-packages\django\contrib\sessions\backends\base.py", line 54, in __getitem__
return self._session[key]
File "C:\Users\Public\django2.2\lib\site-packages\django\contrib\sessions\backends\base.py", line 194, in _get_session
self._session_cache = self.load()
File "C:\Users\Public\django2.2\lib\site-packages\django\contrib\sessions\backends\db.py", line 44, in load
return self.decode(s.session_data) if s else {}
File "C:\Users\Public\django2.2\lib\site-packages\django\contrib\sessions\backends\base.py", line 100, in decode
encoded_data = base64.b64decode(session_data.encode('ascii'))
It's possible you run the server in a different Environment, Activate that environment and try to run the server again.
incase the env folder is in your current Path here's what to do
kill the server and then run
source env/bin/activate
python manage.py runserver

How to debug patched method with unittest.mock

I have the following (simplified) FBV:
def check_existing_contacts(request):
if request.is_ajax and request.method == "GET":
print('Function called')
return mailgun_validate_email(request)
return JsonResponse({"error": "Incorrect AJAX / GET request."}, status=400)
I want to test that the mailgun_validate_email function is called:
class TestCheckExistingContacts(TestCase):
#patch('myapp.mailgun_validate_email')
def test_new_contact(self, mock):
client = Client()
client.get('/check/', HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertTrue(mock.called)
I am certain the test calls mailgun_validate_email as the print('Function called') displays in the console. However I get an assertion error that the mock.called is False.
Where am I going wrong / how can I debug this?
************ UPDATE *******************
When patching the function in the same module as the view, I get the following error:
class TestCheckExistingContacts(TestCase):
#patch('[path to views.py with check_existing_contacts].mailgun_validate_email')
def test_new_contact(self, mock):
client = Client()
client.get('/check/', HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertTrue(mock.called)
Results in:
Failure
Traceback (most recent call last):
File "\tests\test_utils.py", line 123, in test_new_contact
response = self.client.get('/validate/',
File "\.venv\lib\site-packages\django\test\client.py", line 518, in get
response = super().get(path, data=data, secure=secure, **extra)
File "\.venv\lib\site-packages\django\test\client.py", line 344, in get
return self.generic('GET', path, secure=secure, **{
File "\.venv\lib\site-packages\django\test\client.py", line 421, in generic
return self.request(**r)
File "\.venv\lib\site-packages\django\test\client.py", line 496, in request
raise exc_value
File "\.venv\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
response = get_response(request)
File "\.venv\lib\site-packages\django\utils\deprecation.py", line 96, in __call__
response = self.process_response(request, response)
File "\.venv\lib\site-packages\django\contrib\sessions\middleware.py", line 45, in process_response
patch_vary_headers(response, ('Cookie',))
File "\.venv\lib\site-packages\django\utils\cache.py", line 267, in patch_vary_headers
vary_headers = cc_delim_re.split(response['Vary'])
TypeError: expected string or bytes-like object
If you did from myapp import mailgun_validate_email for check_existing_contacts, then you need to patch the reference in that module instead of myapp.
E.g. if the import is in myapp/views.py, then patch myapp.views.mailgun_validate_email.
The view needs to return an instance of HttpResponse or one of its subclasses, same for mailgun_validate_email since you directly return mailgun_validate_email(...).
# #patch('myapp.mailgun_validate_email') # Change this
#patch('myapp.views.mailgun_validate_email', return_value=JsonResponse({})) # to this

AttirbuteError: read when trying to post an image using Python Facebook SDK (in a Django proj)

In a Django web app of mine, users post photos on the website. Once photos accumulate high enough upvotes, I post them to a Facebook fan page using this function from the Python Facebook SDK. Initial tests of the SDK work correctly. One simply passes a file object representing the image, and a caption to graph.put_image() method, and it works.
But I can only get it to work when the image is being uploaded from the client. I'm struggling if an image object is already saved on the server; then the SDK throws an AttributeError: read error. I might be making a rookie mistake, so please advise. I get the error when I do:
from PIL import Image
api.put_photo(image=Image.open(photo.image_file),message='my caption')
Where the photo object is defined as follows in my models.py:
class Photo(models.Model):
image_file = models.ImageField(upload_to=upload_photo, storage=OverwriteStorage())
upload_time = models.DateTimeField(auto_now_add=True)
OverwriteStorate() is an overwritten Django Storage class (imported from django.core.files.storage). It contains functionality to upload to Azure blob storage, and has been working correctly. upload_photo_to_location is simply:
def upload_photo(instance, filename):
try:
blocks = filename.split('.')
ext = blocks[-1]
filename = "%s.%s" % (uuid.uuid4(), ext)
instance.title = blocks[0]
return os.path.join('photos/', filename)
except Exception as e:
print '%s (%s)' % (e.message, type(e))
return 0
The full error traceback:
File "/home/myuser/Desktop/myproj/myapp/tasks.py", line 179, in rank_all_photos
photo_poster(photo.image_file, photo.caption)
File "/home/myuser/Desktop/myproj/myapp/facebook_api.py", line 14, in photo_poster
status = api.put_photo(image=Image.open(image_obj),message=caption)
File "/home/myuser/.virtualenvs/myenv/local/lib/python2.7/site-packages/facebook/__init__.py", line 193, in put_photo
method="POST")
File "/home/myuser/.virtualenvs/myenv/local/lib/python2.7/site-packages/facebook/__init__.py", line 247, in request
files=files)
File "/home/myuser/.virtualenvs/myenv/local/lib/python2.7/site-packages/requests/api.py", line 50, in request
response = session.request(method=method, url=url, **kwargs)
File "/home/myuser/.virtualenvs/myenv/local/lib/python2.7/site-packages/requests/sessions.py", line 451, in request
prep = self.prepare_request(req)
File "/home/myuser/.virtualenvs/myenv/local/lib/python2.7/site-packages/requests/sessions.py", line 382, in prepare_request
hooks=merge_hooks(request.hooks, self.hooks),
File "/home/myuser/.virtualenvs/myenv/local/lib/python2.7/site-packages/requests/models.py", line 307, in prepare
self.prepare_body(data, files, json)
File "/home/myuser/.virtualenvs/myenv/local/lib/python2.7/site-packages/requests/models.py", line 453, in prepare_body
(body, content_type) = self._encode_files(files, data)
File "/home/myuser/.virtualenvs/myenv/local/lib/python2.7/site-packages/requests/models.py", line 150, in _encode_files
fdata = fp.read()
File "/home/myuser/.virtualenvs/myenv/local/lib/python2.7/site-packages/PIL/Image.py", line 626, in __getattr__
raise AttributeError(name)
AttributeError: read

Python unexpected import behaviour

I am trying to build a view where forms are loaded dynamically based on the form slug, the available forms are defined in a list of tuples like this, this is meant to speed up development of new forms in a "framework-like" way:
#installed_io.py
forms = [("json",JsonForm),("csv",CsvForm),....]
froms are defined in the forms.py module as usual.
from django import forms
class FileForm(BaseDatasetForm):
file = forms.FileField(label="Opcion 1: Seleccione un archivo", required=False)
text = forms.CharField(widget=forms.Textarea, label="Opction 2: Introduzca el contenido en este campo", required=False)
utils.py defines the function to dynamically select the Form class:
from installed_io import installed_inputs
def get_input_form(slug):
for entry in installed_inputs:
if entry[0] == slug:
return entry[1]
raise NotImplementedError("The required form is not implemented or missing from the installed inputs")
The view is defined in the views.py module of my django app:
#views.py
from utils import get_input_form
#login_required
def add(request, slug):
InputForm = get_input_form(slug)
if request.method == "POST":
form = InputForm(request.POST, request.FILES)
if form.is_valid():
object_id = form.save()
messages.success(request, "Dataset created")
return redirect(reverse("input:dataset", args=[str(object_id.inserted_id)]))
else:
form = InputForm()
return render(request, "datasets/add-form.html", {"form":form})
but I'm getting this import error:
python manage.py runserver
Unhandled exception in thread started by <function wrapper at 0x7fe9c465c398>
Traceback (most recent call last):
File "/home/jesus/workspace/tensorflow-board-django/venv/local/lib/python2.7/site-packages/django/utils/autoreload.py", line 226, in wrapper
fn(*args, **kwargs)
File "/home/jesus/workspace/tensorflow-board-django/venv/local/lib/python2.7/site-packages/django/core/management/commands/runserver.py", line 109, in inner_run
autoreload.raise_last_exception()
File "/home/jesus/workspace/tensorflow-board-django/venv/local/lib/python2.7/site-packages/django/utils/autoreload.py", line 249, in raise_last_exception
six.reraise(*_exception)
File "/home/jesus/workspace/tensorflow-board-django/venv/local/lib/python2.7/site-packages/django/utils/autoreload.py", line 226, in wrapper
fn(*args, **kwargs)
File "/home/jesus/workspace/tensorflow-board-django/venv/local/lib/python2.7/site-packages/django/__init__.py", line 18, in setup
apps.populate(settings.INSTALLED_APPS)
File "/home/jesus/workspace/tensorflow-board-django/venv/local/lib/python2.7/site-packages/django/apps/registry.py", line 108, in populate
app_config.import_models(all_models)
File "/home/jesus/workspace/tensorflow-board-django/venv/local/lib/python2.7/site-packages/django/apps/config.py", line 202, in import_models
self.models_module = import_module(models_module_name)
File "/usr/lib/python2.7/importlib/__init__.py", line 37, in import_module
__import__(name)
File "/home/jesus/workspace/tensorflow-board-django/tensorflow_board_django/analysis/models.py", line 2, in <module>
from ..input.forms import SOURCES
File "/home/jesus/workspace/tensorflow-board-django/tensorflow_board_django/input/forms.py", line 2, in <module>
from utils import save_dataset
File "/home/jesus/workspace/tensorflow-board-django/tensorflow_board_django/input/utils.py", line 2, in <module>
from installed_io import installed_inputs
File "/home/jesus/workspace/tensorflow-board-django/tensorflow_board_django/input/installed_io.py", line 1, in <module>
from forms import FileForm
ImportError: cannot import name FileForm
What I've tried:
Changing the import statements using absolute paths
Deleting pyc files
Using dot notation to import modules
Trying to save the installed_input list on init.py
Only moving all the code to the views.py file worked, but I find this solution to be very monolithic and non-pythonic.
Based on the comments on the original post I made the following changes to my code:
instead of importing classes on installed_io module I used strings:
#installed_io.py
forms = [("json","JsonForm"),("csv","CsvForm"),....]
then I changed utils.py to import the class from string:
from installed_io import installed_inputs
def get_input_form(slug):
for entry in installed_inputs:
if entry[0] == slug:
module = importlib.import_module("tensorflow_board_django.io.forms")
class_name = entry[1]
return getattr(module, class_name)
raise NotImplementedError("The required form is not implemented or missing from the installed inputs")

How do I use custom filters when using django as a standalone template engine

I'm trying to use django as a stand alone template engine.
This works fine based on this
I tried to add a simple filter, but the template isn't able to use it
The basic example code is:
from django.template import Template, Context, Library
from django.conf import settings
settings.configure()
register = Library()
#register.filter
def nothing(value):
return value
template = '''
{{var|nothing}}
'''
t = Template(template)
c = Context({'var':1})
print (t.render(c))
and the error is:
Traceback (most recent call last):
File "C:/dev/git/ophir/dj.py", line 20, in <module>
t = Template(template)
File "C:\Python27\lib\site-packages\django\template\base.py", line 190, in __init__
self.nodelist = engine.compile_string(template_string, origin)
File "C:\Python27\lib\site-packages\django\template\engine.py", line 261, in compile_string
return parser.parse()
File "C:\Python27\lib\site-packages\django\template\base.py", line 317, in parse
filter_expression = self.compile_filter(token.contents)
File "C:\Python27\lib\site-packages\django\template\base.py", line 423, in compile_filter
return FilterExpression(token, self)
File "C:\Python27\lib\site-packages\django\template\base.py", line 633, in __init__
filter_func = parser.find_filter(filter_name)
File "C:\Python27\lib\site-packages\django\template\base.py", line 429, in find_filter
raise TemplateSyntaxError("Invalid filter: '%s'" % filter_name)
django.template.base.TemplateSyntaxError: Invalid filter: 'nothing'
Process finished with exit code 1
Any ideas?
You need to follow the instructions in the Code Layout section of the docs, there isn't any way around it.
Therefore, you need to include {% load my_template_tags %} inside the template string, and include the app that includes the templatetags/my_template_tags.py module in your INSTALLED_APPS setting.
You may find it easier to use Jinja2, which can be used as a stand alone template engine.

Categories