Wednesday, December 26, 2018

Django form test only fails when breakpoint set on form.is_valid() in PyCharm

I have a model called Table:

class Table(models.Model):
"""
Models tables which are initially scanned by customers. Orders are linked to tables
"""
MINIMUM_QR_WIDTH, MINIMUM_QR_HEIGHT = 115, 115
MAX_QR_SIZE = 10485760 # 10MB

number = models.CharField(max_length=256, blank=False, null=False)

identifier = models.UUIDField(default=uuid.uuid4, unique=True, editable=False)
venue = models.ForeignKey(Venue, null=False, blank=False, on_delete=models.CASCADE)
qr_code = models.ImageField(upload_to='table_qr_codes', blank=True, null=True)

def __str__(self):
return "Table " + self.number

class Meta:
unique_together = ('venue', 'number')
ordering = ['venue', 'number']


I have a ModelForm to create Table objects called TableAddForm.

class TableAddForm(forms.ModelForm):

class Meta:
model = Table
fields = ['number', 'qr_code', 'venue']

def clean_qr_code(self):
qr = self.cleaned_data.get('qr_code')

if qr:
w, h = get_image_dimensions(qr)
if w < Table.MINIMUM_QR_WIDTH or h < Table.MINIMUM_QR_HEIGHT:
msg = forms.ValidationError("The image you selected is too small. QR codes must be at least " + str(Table.MINIMUM_QR_WIDTH)
+ "px by " + str(Table.MINIMUM_QR_HEIGHT) + "px.")
self.add_error('qr_code', msg)

if qr.size > Table.MAX_QR_SIZE:
msg = forms.ValidationError("The file you uploaded is too large. File size can be up to " + str(size(Table.MAX_QR_SIZE))
+ ". File size is currently " + str(size(qr.size)))
self.add_error('logo', msg)

return qr

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['venue'].empty_label = 'Select your venue'

I have a test for the TableAddForm.

class TableAddFormTests(TestCase):
def setUp(self):
user = User.objects.create(username='john1@email.com', password='password', email='john1@email.com')
business1 = create_business(name='business1', owner=user)
venue = create_venue(name='venue1', address='address1', zip='zip1', city='city1', country='country1', business=business1)

def test_valid_data(self):
qr = SimpleUploadedFile(name='valid_qr.png', content=open('media/tests/225_by_225_image.png', 'rb').read())
venue = get_object_or_404(Venue, name='venue1')
data = {'number': 1, 'venue': venue.pk}
form = TableAddForm(data=data, files={'qr_code': qr})
self.assertTrue(form.is_valid())
table = form.save()
self.assertEqual(table.number, '1')
self.assertEqual(table.venue, venue)
self.assertEqual(open('media/tests/225_by_225_image.png', 'rb').read(), table.qr_code.read())

  

When I run test_valid_data() without breakpoints in PyCharm, it passes. When I run the test with a break point on: "self.assertTrue(form.is_valid()) two things happen.

1. The breakpoints on Django's_clean_fields() method are ignored (i.e. code execution is not stopped at the breakpoints).

2. The venue key is not present in the form's cleaned_data dictionary (as can be seen in the screenshot below). This leads to an integrity error when I try to save the form as Table objects must

have a Venue foreign key.


Screenshot 2018-12-26 at 15.13.11.jpg


















Fig 1. Screenshot of debugger variables just before I call form.save() if I have set a breakpoint on form.is_valid()

Traceback when I attempt to save the form without a Venue in cleaned_data.

Error Traceback (most recent call last): File "/Users/williamdavies/.virtualenvs/swiftkiosknew/lib/python3.6/site-packages/django/db/backends/utils.py", line 85, in _execute return self.cursor.execute(sql, params) File "/Users/williamdavies/.virtualenvs/swiftkiosknew/lib/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 296, in execute return Database.Cursor.execute(self, query, params) sqlite3.IntegrityError: NOT NULL constraint failed: sk_business_table.venue_id The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/Users/williamdavies/Documents/PyCharmProjects/SwiftKiosk_new/swiftkiosk/sk_business/test_forms.py", line 322, in test_valid_data table = form.save() File "/Users/williamdavies/.virtualenvs/swiftkiosknew/lib/python3.6/site-packages/django/forms/models.py", line 458, in save self.instance.save() File "/Users/williamdavies/.virtualenvs/swiftkiosknew/lib/python3.6/site-packages/django/db/models/base.py", line 717, in save force_update=force_update, update_fields=update_fields) File "/Users/williamdavies/.virtualenvs/swiftkiosknew/lib/python3.6/site-packages/django/db/models/base.py", line 747, in save_base updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields) File "/Users/williamdavies/.virtualenvs/swiftkiosknew/lib/python3.6/site-packages/django/db/models/base.py", line 830, in _save_table result = self._do_insert(cls._base_manager, using, fields, update_pk, raw) File "/Users/williamdavies/.virtualenvs/swiftkiosknew/lib/python3.6/site-packages/django/db/models/base.py", line 868, in _do_insert using=using, raw=raw) File "/Users/williamdavies/.virtualenvs/swiftkiosknew/lib/python3.6/site-packages/django/db/models/manager.py", line 82, in manager_method return getattr(self.get_queryset(), name)(*args, **kwargs) File "/Users/williamdavies/.virtualenvs/swiftkiosknew/lib/python3.6/site-packages/django/db/models/query.py", line 1133, in _insert return query.get_compiler(using=using).execute_sql(return_id) File "/Users/williamdavies/.virtualenvs/swiftkiosknew/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 1285, in execute_sql cursor.execute(sql, params) File "/Users/williamdavies/.virtualenvs/swiftkiosknew/lib/python3.6/site-packages/django/db/backends/utils.py", line 68, in execute return self._execute_with_wrappers(sql, params, many=False, executor=self._execute) File "/Users/williamdavies/.virtualenvs/swiftkiosknew/lib/python3.6/site-packages/django/db/backends/utils.py", line 77, in _execute_with_wrappers return executor(sql, params, many, context) File "/Users/williamdavies/.virtualenvs/swiftkiosknew/lib/python3.6/site-packages/django/db/backends/utils.py", line 85, in _execute return self.cursor.execute(sql, params) File "/Users/williamdavies/.virtualenvs/swiftkiosknew/lib/python3.6/site-packages/django/db/utils.py", line 89, in __exit__ raise dj_exc_value.with_traceback(traceback) from exc_value File "/Users/williamdavies/.virtualenvs/swiftkiosknew/lib/python3.6/site-packages/django/db/backends/utils.py", line 85, in _execute return self.cursor.execute(sql, params) File "/Users/williamdavies/.virtualenvs/swiftkiosknew/lib/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 296, in execute return Database.Cursor.execute(self, query, params) django.db.utils.IntegrityError: NOT NULL constraint failed: sk_business_table.venue_id Destroying test database for alias 'default'... Process finished with exit code 1

When I run the test with a breakpoint on any other line, the problem is not reproduced and the breakpoints in Django's _clean_fields() method are called. It seems that setting a breakpoint on the line where

I call form.is_valid() changes the way the form data is cleaned and the venue foreign key is not processed correctly, leading to its absence in cleaned_data.


Thanks for reading through this and any help would really be appreciated! If you need any other code/tracebacks/screenshots etc that I missed please let me know and I'd be happy to provide!


Will




--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/054f2f3b-9c3b-4eae-89af-66e17e953087%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

No comments:

Post a Comment